blob: fd44e1ad5c5556c1b2a77f3d801211c482ded362 [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 *
Daniel Veillardc5d64342001-06-24 12:13:24 +000013 * Author: daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +000014 *
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
Owen Taylor3473f882001-02-23 17:55:21 +000033#ifdef HAVE_CTYPE_H
34#include <ctype.h>
35#endif
Daniel Veillard5792e162001-04-30 17:44:45 +000036#ifdef HAVE_SIGNAL_H
Daniel Veillardb45c43b2001-04-28 17:02:11 +000037#include <signal.h>
Daniel Veillardb45c43b2001-04-28 17:02:11 +000038#endif
Owen Taylor3473f882001-02-23 17:55:21 +000039
40#include <libxml/xmlmemory.h>
41#include <libxml/tree.h>
42#include <libxml/valid.h>
43#include <libxml/xpath.h>
44#include <libxml/xpathInternals.h>
45#include <libxml/parserInternals.h>
46#include <libxml/hash.h>
47#ifdef LIBXML_XPTR_ENABLED
48#include <libxml/xpointer.h>
49#endif
50#ifdef LIBXML_DEBUG_ENABLED
51#include <libxml/debugXML.h>
52#endif
53#include <libxml/xmlerror.h>
54
55/* #define DEBUG */
56/* #define DEBUG_STEP */
Daniel Veillardf06307e2001-07-03 10:35:50 +000057/* #define DEBUG_STEP_NTH */
Owen Taylor3473f882001-02-23 17:55:21 +000058/* #define DEBUG_EXPR */
Daniel Veillardf06307e2001-07-03 10:35:50 +000059/* #define DEBUG_EVAL_COUNTS */
Owen Taylor3473f882001-02-23 17:55:21 +000060
61void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
62double xmlXPathStringEvalNumber(const xmlChar *str);
Daniel Veillard5792e162001-04-30 17:44:45 +000063double xmlXPathDivideBy(double f, double fzero);
Owen Taylor3473f882001-02-23 17:55:21 +000064
Daniel Veillard9e7160d2001-03-18 23:17:47 +000065/************************************************************************
66 * *
67 * Floating point stuff *
68 * *
69 ************************************************************************/
70
Daniel Veillardc0631a62001-09-20 13:56:06 +000071#ifndef TRIO_REPLACE_STDIO
Daniel Veillardcda96922001-08-21 10:56:31 +000072#define TRIO_PUBLIC static
Daniel Veillardc0631a62001-09-20 13:56:06 +000073#endif
Daniel Veillardcda96922001-08-21 10:56:31 +000074#include "trionan.c"
75
Owen Taylor3473f882001-02-23 17:55:21 +000076/*
Owen Taylor3473f882001-02-23 17:55:21 +000077 * The lack of portability of this section of the libc is annoying !
78 */
79double xmlXPathNAN = 0;
80double xmlXPathPINF = 1;
81double xmlXPathNINF = -1;
82
Owen Taylor3473f882001-02-23 17:55:21 +000083/**
84 * xmlXPathInit:
85 *
86 * Initialize the XPath environment
87 */
88void
89xmlXPathInit(void) {
90 static int initialized = 0;
91
92 if (initialized) return;
93
Bjorn Reese45029602001-08-21 09:23:53 +000094 xmlXPathPINF = trio_pinf();
95 xmlXPathNINF = trio_ninf();
96 xmlXPathNAN = trio_nan();
Owen Taylor3473f882001-02-23 17:55:21 +000097
98 initialized = 1;
99}
100
Daniel Veillardcda96922001-08-21 10:56:31 +0000101/**
102 * xmlXPathIsNaN:
103 * @val: a double value
104 *
105 * Provides a portable isnan() function to detect whether a double
106 * is a NotaNumber. Based on trio code
107 * http://sourceforge.net/projects/ctrio/
108 *
109 * Returns 1 if the value is a NaN, 0 otherwise
110 */
111int
112xmlXPathIsNaN(double val) {
113 return(trio_isnan(val));
114}
115
116/**
117 * xmlXPathIsInf:
118 * @val: a double value
119 *
120 * Provides a portable isinf() function to detect whether a double
121 * is a +Infinite or -Infinite. Based on trio code
122 * http://sourceforge.net/projects/ctrio/
123 *
124 * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
125 */
126int
127xmlXPathIsInf(double val) {
128 return(trio_isinf(val));
129}
130
Owen Taylor3473f882001-02-23 17:55:21 +0000131/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000132 * *
133 * Parser Types *
134 * *
135 ************************************************************************/
136
137/*
138 * Types are private:
139 */
140
141typedef enum {
142 XPATH_OP_END=0,
143 XPATH_OP_AND,
144 XPATH_OP_OR,
145 XPATH_OP_EQUAL,
146 XPATH_OP_CMP,
147 XPATH_OP_PLUS,
148 XPATH_OP_MULT,
149 XPATH_OP_UNION,
150 XPATH_OP_ROOT,
151 XPATH_OP_NODE,
152 XPATH_OP_RESET,
153 XPATH_OP_COLLECT,
154 XPATH_OP_VALUE,
155 XPATH_OP_VARIABLE,
156 XPATH_OP_FUNCTION,
157 XPATH_OP_ARG,
158 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000159 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000160 XPATH_OP_SORT
161#ifdef LIBXML_XPTR_ENABLED
162 ,XPATH_OP_RANGETO
163#endif
164} xmlXPathOp;
165
166typedef enum {
167 AXIS_ANCESTOR = 1,
168 AXIS_ANCESTOR_OR_SELF,
169 AXIS_ATTRIBUTE,
170 AXIS_CHILD,
171 AXIS_DESCENDANT,
172 AXIS_DESCENDANT_OR_SELF,
173 AXIS_FOLLOWING,
174 AXIS_FOLLOWING_SIBLING,
175 AXIS_NAMESPACE,
176 AXIS_PARENT,
177 AXIS_PRECEDING,
178 AXIS_PRECEDING_SIBLING,
179 AXIS_SELF
180} xmlXPathAxisVal;
181
182typedef enum {
183 NODE_TEST_NONE = 0,
184 NODE_TEST_TYPE = 1,
185 NODE_TEST_PI = 2,
186 NODE_TEST_ALL = 3,
187 NODE_TEST_NS = 4,
188 NODE_TEST_NAME = 5
189} xmlXPathTestVal;
190
191typedef enum {
192 NODE_TYPE_NODE = 0,
193 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
194 NODE_TYPE_TEXT = XML_TEXT_NODE,
195 NODE_TYPE_PI = XML_PI_NODE
196} xmlXPathTypeVal;
197
198
199typedef struct _xmlXPathStepOp xmlXPathStepOp;
200typedef xmlXPathStepOp *xmlXPathStepOpPtr;
201struct _xmlXPathStepOp {
202 xmlXPathOp op;
203 int ch1;
204 int ch2;
205 int value;
206 int value2;
207 int value3;
208 void *value4;
209 void *value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000210 void *cache;
Daniel Veillard42596ad2001-05-22 16:57:14 +0000211 void *cacheURI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000212};
213
214struct _xmlXPathCompExpr {
215 int nbStep;
216 int maxStep;
217 xmlXPathStepOp *steps; /* ops for computation */
218 int last;
Daniel Veillardf06307e2001-07-03 10:35:50 +0000219#ifdef DEBUG_EVAL_COUNTS
220 int nb;
221 xmlChar *string;
222#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000223};
224
225/************************************************************************
226 * *
227 * Parser Type functions *
228 * *
229 ************************************************************************/
230
231/**
232 * xmlXPathNewCompExpr:
233 *
234 * Create a new Xpath component
235 *
236 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
237 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000238static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000239xmlXPathNewCompExpr(void) {
240 xmlXPathCompExprPtr cur;
241
242 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
243 if (cur == NULL) {
244 xmlGenericError(xmlGenericErrorContext,
245 "xmlXPathNewCompExpr : malloc failed\n");
246 return(NULL);
247 }
248 memset(cur, 0, sizeof(xmlXPathCompExpr));
249 cur->maxStep = 10;
250 cur->nbStep = 0;
251 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
252 sizeof(xmlXPathStepOp));
253 if (cur->steps == NULL) {
254 xmlGenericError(xmlGenericErrorContext,
255 "xmlXPathNewCompExpr : malloc failed\n");
256 xmlFree(cur);
257 return(NULL);
258 }
259 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
260 cur->last = -1;
Daniel Veillardf06307e2001-07-03 10:35:50 +0000261#ifdef DEBUG_EVAL_COUNTS
262 cur->nb = 0;
263#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000264 return(cur);
265}
266
267/**
268 * xmlXPathFreeCompExpr:
269 * @comp: an XPATH comp
270 *
271 * Free up the memory allocated by @comp
272 */
273void
Daniel Veillardf06307e2001-07-03 10:35:50 +0000274xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
275{
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000276 xmlXPathStepOpPtr op;
277 int i;
278
279 if (comp == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +0000280 return;
281 for (i = 0; i < comp->nbStep; i++) {
282 op = &comp->steps[i];
283 if (op->value4 != NULL) {
284 if (op->op == XPATH_OP_VALUE)
285 xmlXPathFreeObject(op->value4);
286 else
287 xmlFree(op->value4);
288 }
289 if (op->value5 != NULL)
290 xmlFree(op->value5);
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000291 }
292 if (comp->steps != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +0000293 xmlFree(comp->steps);
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000294 }
Daniel Veillardf06307e2001-07-03 10:35:50 +0000295#ifdef DEBUG_EVAL_COUNTS
296 if (comp->string != NULL) {
297 xmlFree(comp->string);
298 }
299#endif
300
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000301 xmlFree(comp);
302}
303
304/**
305 * xmlXPathCompExprAdd:
306 * @comp: the compiled expression
307 * @ch1: first child index
308 * @ch2: second child index
309 * @op: an op
310 * @value: the first int value
311 * @value2: the second int value
312 * @value3: the third int value
313 * @value4: the first string value
314 * @value5: the second string value
315 *
316 * Add an step to an XPath Compiled Expression
317 *
318 * Returns -1 in case of failure, the index otherwise
319 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000320static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000321xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
322 xmlXPathOp op, int value,
323 int value2, int value3, void *value4, void *value5) {
324 if (comp->nbStep >= comp->maxStep) {
325 xmlXPathStepOp *real;
326
327 comp->maxStep *= 2;
328 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
329 comp->maxStep * sizeof(xmlXPathStepOp));
330 if (real == NULL) {
331 comp->maxStep /= 2;
332 xmlGenericError(xmlGenericErrorContext,
333 "xmlXPathCompExprAdd : realloc failed\n");
334 return(-1);
335 }
336 comp->steps = real;
337 }
338 comp->last = comp->nbStep;
339 comp->steps[comp->nbStep].ch1 = ch1;
340 comp->steps[comp->nbStep].ch2 = ch2;
341 comp->steps[comp->nbStep].op = op;
342 comp->steps[comp->nbStep].value = value;
343 comp->steps[comp->nbStep].value2 = value2;
344 comp->steps[comp->nbStep].value3 = value3;
345 comp->steps[comp->nbStep].value4 = value4;
346 comp->steps[comp->nbStep].value5 = value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000347 comp->steps[comp->nbStep].cache = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000348 return(comp->nbStep++);
349}
350
Daniel Veillardf06307e2001-07-03 10:35:50 +0000351/**
352 * xmlXPathCompSwap:
353 * @comp: the compiled expression
354 * @op: operation index
355 *
356 * Swaps 2 operations in the compiled expression
357 * TODO: not thread safe, disable for multi-thread operations
358 *
359 * Returns -1 in case of failure, the index otherwise
360 */
361static void
362xmlXPathCompSwap(xmlXPathStepOpPtr op) {
363 int tmp;
364
365 tmp = op->ch1;
366 op->ch1 = op->ch2;
367 op->ch2 = tmp;
368}
369
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000370#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
371 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
372 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000373#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
374 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
375 (op), (val), (val2), (val3), (val4), (val5))
376
377#define PUSH_LEAVE_EXPR(op, val, val2) \
378xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
379
380#define PUSH_UNARY_EXPR(op, ch, val, val2) \
381xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
382
383#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
384xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
385
386/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000387 * *
388 * Debugging related functions *
389 * *
390 ************************************************************************/
391
392#define TODO \
393 xmlGenericError(xmlGenericErrorContext, \
394 "Unimplemented block at %s:%d\n", \
395 __FILE__, __LINE__);
396
397#define STRANGE \
398 xmlGenericError(xmlGenericErrorContext, \
399 "Internal error at %s:%d\n", \
400 __FILE__, __LINE__);
401
402#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000403static void
404xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000405 int i;
406 char shift[100];
407
408 for (i = 0;((i < depth) && (i < 25));i++)
409 shift[2 * i] = shift[2 * i + 1] = ' ';
410 shift[2 * i] = shift[2 * i + 1] = 0;
411 if (cur == NULL) {
412 fprintf(output, shift);
413 fprintf(output, "Node is NULL !\n");
414 return;
415
416 }
417
418 if ((cur->type == XML_DOCUMENT_NODE) ||
419 (cur->type == XML_HTML_DOCUMENT_NODE)) {
420 fprintf(output, shift);
421 fprintf(output, " /\n");
422 } else if (cur->type == XML_ATTRIBUTE_NODE)
423 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
424 else
425 xmlDebugDumpOneNode(output, cur, depth);
426}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000427static void
428xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000429 xmlNodePtr tmp;
430 int i;
431 char shift[100];
432
433 for (i = 0;((i < depth) && (i < 25));i++)
434 shift[2 * i] = shift[2 * i + 1] = ' ';
435 shift[2 * i] = shift[2 * i + 1] = 0;
436 if (cur == NULL) {
437 fprintf(output, shift);
438 fprintf(output, "Node is NULL !\n");
439 return;
440
441 }
442
443 while (cur != NULL) {
444 tmp = cur;
445 cur = cur->next;
446 xmlDebugDumpOneNode(output, tmp, depth);
447 }
448}
Owen Taylor3473f882001-02-23 17:55:21 +0000449
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000450static void
451xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000452 int i;
453 char shift[100];
454
455 for (i = 0;((i < depth) && (i < 25));i++)
456 shift[2 * i] = shift[2 * i + 1] = ' ';
457 shift[2 * i] = shift[2 * i + 1] = 0;
458
459 if (cur == NULL) {
460 fprintf(output, shift);
461 fprintf(output, "NodeSet is NULL !\n");
462 return;
463
464 }
465
Daniel Veillard911f49a2001-04-07 15:39:35 +0000466 if (cur != NULL) {
467 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
468 for (i = 0;i < cur->nodeNr;i++) {
469 fprintf(output, shift);
470 fprintf(output, "%d", i + 1);
471 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
472 }
Owen Taylor3473f882001-02-23 17:55:21 +0000473 }
474}
475
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000476static void
477xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000478 int i;
479 char shift[100];
480
481 for (i = 0;((i < depth) && (i < 25));i++)
482 shift[2 * i] = shift[2 * i + 1] = ' ';
483 shift[2 * i] = shift[2 * i + 1] = 0;
484
485 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
486 fprintf(output, shift);
487 fprintf(output, "Value Tree is NULL !\n");
488 return;
489
490 }
491
492 fprintf(output, shift);
493 fprintf(output, "%d", i + 1);
494 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
495}
Owen Taylor3473f882001-02-23 17:55:21 +0000496#if defined(LIBXML_XPTR_ENABLED)
497void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000498static void
499xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000500 int i;
501 char shift[100];
502
503 for (i = 0;((i < depth) && (i < 25));i++)
504 shift[2 * i] = shift[2 * i + 1] = ' ';
505 shift[2 * i] = shift[2 * i + 1] = 0;
506
507 if (cur == NULL) {
508 fprintf(output, shift);
509 fprintf(output, "LocationSet is NULL !\n");
510 return;
511
512 }
513
514 for (i = 0;i < cur->locNr;i++) {
515 fprintf(output, shift);
516 fprintf(output, "%d : ", i + 1);
517 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
518 }
519}
Daniel Veillard017b1082001-06-21 11:20:21 +0000520#endif /* LIBXML_XPTR_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000521
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000522/**
523 * xmlXPathDebugDumpObject:
524 * @output: the FILE * to dump the output
525 * @cur: the object to inspect
526 * @depth: indentation level
527 *
528 * Dump the content of the object for debugging purposes
529 */
530void
531xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000532 int i;
533 char shift[100];
534
535 for (i = 0;((i < depth) && (i < 25));i++)
536 shift[2 * i] = shift[2 * i + 1] = ' ';
537 shift[2 * i] = shift[2 * i + 1] = 0;
538
539 fprintf(output, shift);
540
541 if (cur == NULL) {
542 fprintf(output, "Object is empty (NULL)\n");
543 return;
544 }
545 switch(cur->type) {
546 case XPATH_UNDEFINED:
547 fprintf(output, "Object is uninitialized\n");
548 break;
549 case XPATH_NODESET:
550 fprintf(output, "Object is a Node Set :\n");
551 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
552 break;
553 case XPATH_XSLT_TREE:
554 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000555 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000556 break;
557 case XPATH_BOOLEAN:
558 fprintf(output, "Object is a Boolean : ");
559 if (cur->boolval) fprintf(output, "true\n");
560 else fprintf(output, "false\n");
561 break;
562 case XPATH_NUMBER:
Daniel Veillardcda96922001-08-21 10:56:31 +0000563 switch (xmlXPathIsInf(cur->floatval)) {
Daniel Veillard357c9602001-05-03 10:49:20 +0000564 case 1:
565 fprintf(output, "Object is a number : +Infinity\n");
566 break;
567 case -1:
568 fprintf(output, "Object is a number : -Infinity\n");
569 break;
570 default:
Daniel Veillardcda96922001-08-21 10:56:31 +0000571 if (xmlXPathIsNaN(cur->floatval)) {
Daniel Veillard357c9602001-05-03 10:49:20 +0000572 fprintf(output, "Object is a number : NaN\n");
573 } else {
574 fprintf(output, "Object is a number : %0g\n", cur->floatval);
575 }
576 }
Owen Taylor3473f882001-02-23 17:55:21 +0000577 break;
578 case XPATH_STRING:
579 fprintf(output, "Object is a string : ");
580 xmlDebugDumpString(output, cur->stringval);
581 fprintf(output, "\n");
582 break;
583 case XPATH_POINT:
584 fprintf(output, "Object is a point : index %d in node", cur->index);
585 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
586 fprintf(output, "\n");
587 break;
588 case XPATH_RANGE:
589 if ((cur->user2 == NULL) ||
590 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
591 fprintf(output, "Object is a collapsed range :\n");
592 fprintf(output, shift);
593 if (cur->index >= 0)
594 fprintf(output, "index %d in ", cur->index);
595 fprintf(output, "node\n");
596 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
597 depth + 1);
598 } else {
599 fprintf(output, "Object is a range :\n");
600 fprintf(output, shift);
601 fprintf(output, "From ");
602 if (cur->index >= 0)
603 fprintf(output, "index %d in ", cur->index);
604 fprintf(output, "node\n");
605 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
606 depth + 1);
607 fprintf(output, shift);
608 fprintf(output, "To ");
609 if (cur->index2 >= 0)
610 fprintf(output, "index %d in ", cur->index2);
611 fprintf(output, "node\n");
612 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
613 depth + 1);
614 fprintf(output, "\n");
615 }
616 break;
617 case XPATH_LOCATIONSET:
618#if defined(LIBXML_XPTR_ENABLED)
619 fprintf(output, "Object is a Location Set:\n");
620 xmlXPathDebugDumpLocationSet(output,
621 (xmlLocationSetPtr) cur->user, depth);
622#endif
623 break;
624 case XPATH_USERS:
625 fprintf(output, "Object is user defined\n");
626 break;
627 }
628}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000629
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000630static void
631xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000632 xmlXPathStepOpPtr op, int depth) {
633 int i;
634 char shift[100];
635
636 for (i = 0;((i < depth) && (i < 25));i++)
637 shift[2 * i] = shift[2 * i + 1] = ' ';
638 shift[2 * i] = shift[2 * i + 1] = 0;
639
640 fprintf(output, shift);
641 if (op == NULL) {
642 fprintf(output, "Step is NULL\n");
643 return;
644 }
645 switch (op->op) {
646 case XPATH_OP_END:
647 fprintf(output, "END"); break;
648 case XPATH_OP_AND:
649 fprintf(output, "AND"); break;
650 case XPATH_OP_OR:
651 fprintf(output, "OR"); break;
652 case XPATH_OP_EQUAL:
653 if (op->value)
654 fprintf(output, "EQUAL =");
655 else
656 fprintf(output, "EQUAL !=");
657 break;
658 case XPATH_OP_CMP:
659 if (op->value)
660 fprintf(output, "CMP <");
661 else
662 fprintf(output, "CMP >");
663 if (!op->value2)
664 fprintf(output, "=");
665 break;
666 case XPATH_OP_PLUS:
667 if (op->value == 0)
668 fprintf(output, "PLUS -");
669 else if (op->value == 1)
670 fprintf(output, "PLUS +");
671 else if (op->value == 2)
672 fprintf(output, "PLUS unary -");
673 else if (op->value == 3)
674 fprintf(output, "PLUS unary - -");
675 break;
676 case XPATH_OP_MULT:
677 if (op->value == 0)
678 fprintf(output, "MULT *");
679 else if (op->value == 1)
680 fprintf(output, "MULT div");
681 else
682 fprintf(output, "MULT mod");
683 break;
684 case XPATH_OP_UNION:
685 fprintf(output, "UNION"); break;
686 case XPATH_OP_ROOT:
687 fprintf(output, "ROOT"); break;
688 case XPATH_OP_NODE:
689 fprintf(output, "NODE"); break;
690 case XPATH_OP_RESET:
691 fprintf(output, "RESET"); break;
692 case XPATH_OP_SORT:
693 fprintf(output, "SORT"); break;
694 case XPATH_OP_COLLECT: {
695 xmlXPathAxisVal axis = op->value;
696 xmlXPathTestVal test = op->value2;
697 xmlXPathTypeVal type = op->value3;
698 const xmlChar *prefix = op->value4;
699 const xmlChar *name = op->value5;
700
701 fprintf(output, "COLLECT ");
702 switch (axis) {
703 case AXIS_ANCESTOR:
704 fprintf(output, " 'ancestors' "); break;
705 case AXIS_ANCESTOR_OR_SELF:
706 fprintf(output, " 'ancestors-or-self' "); break;
707 case AXIS_ATTRIBUTE:
708 fprintf(output, " 'attributes' "); break;
709 case AXIS_CHILD:
710 fprintf(output, " 'child' "); break;
711 case AXIS_DESCENDANT:
712 fprintf(output, " 'descendant' "); break;
713 case AXIS_DESCENDANT_OR_SELF:
714 fprintf(output, " 'descendant-or-self' "); break;
715 case AXIS_FOLLOWING:
716 fprintf(output, " 'following' "); break;
717 case AXIS_FOLLOWING_SIBLING:
718 fprintf(output, " 'following-siblings' "); break;
719 case AXIS_NAMESPACE:
720 fprintf(output, " 'namespace' "); break;
721 case AXIS_PARENT:
722 fprintf(output, " 'parent' "); break;
723 case AXIS_PRECEDING:
724 fprintf(output, " 'preceding' "); break;
725 case AXIS_PRECEDING_SIBLING:
726 fprintf(output, " 'preceding-sibling' "); break;
727 case AXIS_SELF:
728 fprintf(output, " 'self' "); break;
729 }
730 switch (test) {
731 case NODE_TEST_NONE:
732 fprintf(output, "'none' "); break;
733 case NODE_TEST_TYPE:
734 fprintf(output, "'type' "); break;
735 case NODE_TEST_PI:
736 fprintf(output, "'PI' "); break;
737 case NODE_TEST_ALL:
738 fprintf(output, "'all' "); break;
739 case NODE_TEST_NS:
740 fprintf(output, "'namespace' "); break;
741 case NODE_TEST_NAME:
742 fprintf(output, "'name' "); break;
743 }
744 switch (type) {
745 case NODE_TYPE_NODE:
746 fprintf(output, "'node' "); break;
747 case NODE_TYPE_COMMENT:
748 fprintf(output, "'comment' "); break;
749 case NODE_TYPE_TEXT:
750 fprintf(output, "'text' "); break;
751 case NODE_TYPE_PI:
752 fprintf(output, "'PI' "); break;
753 }
754 if (prefix != NULL)
755 fprintf(output, "%s:", prefix);
756 if (name != NULL)
757 fprintf(output, "%s", name);
758 break;
759
760 }
761 case XPATH_OP_VALUE: {
762 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
763
764 fprintf(output, "ELEM ");
765 xmlXPathDebugDumpObject(output, object, 0);
766 goto finish;
767 }
768 case XPATH_OP_VARIABLE: {
769 const xmlChar *prefix = op->value5;
770 const xmlChar *name = op->value4;
771
772 if (prefix != NULL)
773 fprintf(output, "VARIABLE %s:%s", prefix, name);
774 else
775 fprintf(output, "VARIABLE %s", name);
776 break;
777 }
778 case XPATH_OP_FUNCTION: {
779 int nbargs = op->value;
780 const xmlChar *prefix = op->value5;
781 const xmlChar *name = op->value4;
782
783 if (prefix != NULL)
784 fprintf(output, "FUNCTION %s:%s(%d args)",
785 prefix, name, nbargs);
786 else
787 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
788 break;
789 }
790 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
791 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000792 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000793#ifdef LIBXML_XPTR_ENABLED
794 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
795#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000796 default:
797 fprintf(output, "UNKNOWN %d\n", op->op); return;
798 }
799 fprintf(output, "\n");
800finish:
801 if (op->ch1 >= 0)
802 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
803 if (op->ch2 >= 0)
804 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
805}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000806
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000807/**
808 * xmlXPathDebugDumpCompExpr:
809 * @output: the FILE * for the output
810 * @comp: the precompiled XPath expression
811 * @depth: the indentation level.
812 *
813 * Dumps the tree of the compiled XPath expression.
814 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000815void
816xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
817 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000818 int i;
819 char shift[100];
820
821 for (i = 0;((i < depth) && (i < 25));i++)
822 shift[2 * i] = shift[2 * i + 1] = ' ';
823 shift[2 * i] = shift[2 * i + 1] = 0;
824
825 fprintf(output, shift);
826
827 if (comp == NULL) {
828 fprintf(output, "Compiled Expression is NULL\n");
829 return;
830 }
831 fprintf(output, "Compiled Expression : %d elements\n",
832 comp->nbStep);
833 i = comp->last;
834 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
835}
Daniel Veillard017b1082001-06-21 11:20:21 +0000836#endif /* LIBXML_DEBUG_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000837
838/************************************************************************
839 * *
840 * Parser stacks related functions and macros *
841 * *
842 ************************************************************************/
843
844/*
845 * Generic function for accessing stacks in the Parser Context
846 */
847
848#define PUSH_AND_POP(type, name) \
849extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
850 if (ctxt->name##Nr >= ctxt->name##Max) { \
851 ctxt->name##Max *= 2; \
852 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
853 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
854 if (ctxt->name##Tab == NULL) { \
855 xmlGenericError(xmlGenericErrorContext, \
856 "realloc failed !\n"); \
857 return(0); \
858 } \
859 } \
860 ctxt->name##Tab[ctxt->name##Nr] = value; \
861 ctxt->name = value; \
862 return(ctxt->name##Nr++); \
863} \
864extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
865 type ret; \
866 if (ctxt->name##Nr <= 0) return(0); \
867 ctxt->name##Nr--; \
868 if (ctxt->name##Nr > 0) \
869 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
870 else \
871 ctxt->name = NULL; \
872 ret = ctxt->name##Tab[ctxt->name##Nr]; \
873 ctxt->name##Tab[ctxt->name##Nr] = 0; \
874 return(ret); \
875} \
876
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000877/**
878 * valuePop:
879 * @ctxt: an XPath evaluation context
880 *
881 * Pops the top XPath object from the value stack
882 *
883 * Returns the XPath object just removed
884 */
885/**
886 * valuePush:
887 * @ctxt: an XPath evaluation context
888 * @value: the XPath object
889 *
890 * Pushes a new XPath object on top of the value stack
891 */
Owen Taylor3473f882001-02-23 17:55:21 +0000892PUSH_AND_POP(xmlXPathObjectPtr, value)
893
Thomas Broyerf06a3d82001-07-16 04:52:57 +0000894/**
895 * xmlXPathPopBoolean:
896 * @ctxt: an XPath parser context
897 *
898 * Pops a boolean from the stack, handling conversion if needed.
899 * Check error with #xmlXPathCheckError.
900 *
901 * Returns the boolean
902 */
903int
904xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
905 xmlXPathObjectPtr obj;
906 int ret;
907
908 obj = valuePop(ctxt);
909 if (obj == NULL) {
910 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
911 return(0);
912 }
913 ret = xmlXPathCastToBoolean(obj);
914 xmlXPathFreeObject(obj);
915 return(ret);
916}
917
918/**
919 * xmlXPathPopNumber:
920 * @ctxt: an XPath parser context
921 *
922 * Pops a number from the stack, handling conversion if needed.
923 * Check error with #xmlXPathCheckError.
924 *
925 * Returns the number
926 */
927double
928xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
929 xmlXPathObjectPtr obj;
930 double ret;
931
932 obj = valuePop(ctxt);
933 if (obj == NULL) {
934 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
935 return(0);
936 }
937 ret = xmlXPathCastToNumber(obj);
938 xmlXPathFreeObject(obj);
939 return(ret);
940}
941
942/**
943 * xmlXPathPopString:
944 * @ctxt: an XPath parser context
945 *
946 * Pops a string from the stack, handling conversion if needed.
947 * Check error with #xmlXPathCheckError.
948 *
949 * Returns the string
950 */
951xmlChar *
952xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
953 xmlXPathObjectPtr obj;
954 xmlChar * ret;
955
956 obj = valuePop(ctxt);
957 if (obj == NULL) {
958 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
959 return(NULL);
960 }
961 ret = xmlXPathCastToString(obj);
962 /* TODO: needs refactoring somewhere else */
963 if (obj->stringval == ret)
964 obj->stringval = NULL;
965 xmlXPathFreeObject(obj);
966 return(ret);
967}
968
969/**
970 * xmlXPathPopNodeSet:
971 * @ctxt: an XPath parser context
972 *
973 * Pops a node-set from the stack, handling conversion if needed.
974 * Check error with #xmlXPathCheckError.
975 *
976 * Returns the node-set
977 */
978xmlNodeSetPtr
979xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
980 xmlXPathObjectPtr obj;
981 xmlNodeSetPtr ret;
982
983 if (ctxt->value == NULL) {
984 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
985 return(NULL);
986 }
987 if (!xmlXPathStackIsNodeSet(ctxt)) {
988 xmlXPathSetTypeError(ctxt);
989 return(NULL);
990 }
991 obj = valuePop(ctxt);
992 ret = obj->nodesetval;
993 xmlXPathFreeNodeSetList(obj);
994 return(ret);
995}
996
997/**
998 * xmlXPathPopExternal:
999 * @ctxt: an XPath parser context
1000 *
1001 * Pops an external oject from the stack, handling conversion if needed.
1002 * Check error with #xmlXPathCheckError.
1003 *
1004 * Returns the object
1005 */
1006void *
1007xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
1008 xmlXPathObjectPtr obj;
1009 void * ret;
1010
1011 if (ctxt->value == NULL) {
1012 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
1013 return(NULL);
1014 }
1015 if (ctxt->value->type != XPATH_USERS) {
1016 xmlXPathSetTypeError(ctxt);
1017 return(NULL);
1018 }
1019 obj = valuePop(ctxt);
1020 ret = obj->user;
1021 xmlXPathFreeObject(obj);
1022 return(ret);
1023}
1024
Owen Taylor3473f882001-02-23 17:55:21 +00001025/*
1026 * Macros for accessing the content. Those should be used only by the parser,
1027 * and not exported.
1028 *
1029 * Dirty macros, i.e. one need to make assumption on the context to use them
1030 *
1031 * CUR_PTR return the current pointer to the xmlChar to be parsed.
1032 * CUR returns the current xmlChar value, i.e. a 8 bit value
1033 * in ISO-Latin or UTF-8.
1034 * This should be used internally by the parser
1035 * only to compare to ASCII values otherwise it would break when
1036 * running with UTF-8 encoding.
1037 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
1038 * to compare on ASCII based substring.
1039 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
1040 * strings within the parser.
1041 * CURRENT Returns the current char value, with the full decoding of
1042 * UTF-8 if we are using this mode. It returns an int.
1043 * NEXT Skip to the next character, this does the proper decoding
1044 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
1045 * It returns the pointer to the current xmlChar.
1046 */
1047
1048#define CUR (*ctxt->cur)
1049#define SKIP(val) ctxt->cur += (val)
1050#define NXT(val) ctxt->cur[(val)]
1051#define CUR_PTR ctxt->cur
Daniel Veillard61d80a22001-04-27 17:13:01 +00001052#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
1053
1054#define COPY_BUF(l,b,i,v) \
1055 if (l == 1) b[i++] = (xmlChar) v; \
1056 else i += xmlCopyChar(l,&b[i],v)
1057
1058#define NEXTL(l) ctxt->cur += l
Owen Taylor3473f882001-02-23 17:55:21 +00001059
1060#define SKIP_BLANKS \
1061 while (IS_BLANK(*(ctxt->cur))) NEXT
1062
1063#define CURRENT (*ctxt->cur)
1064#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1065
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001066
1067#ifndef DBL_DIG
1068#define DBL_DIG 16
1069#endif
1070#ifndef DBL_EPSILON
1071#define DBL_EPSILON 1E-9
1072#endif
1073
1074#define UPPER_DOUBLE 1E9
1075#define LOWER_DOUBLE 1E-5
1076
1077#define INTEGER_DIGITS DBL_DIG
1078#define FRACTION_DIGITS (DBL_DIG + 1)
1079#define EXPONENT_DIGITS (3 + 2)
1080
1081/**
1082 * xmlXPathFormatNumber:
1083 * @number: number to format
1084 * @buffer: output buffer
1085 * @buffersize: size of output buffer
1086 *
1087 * Convert the number into a string representation.
1088 */
1089static void
1090xmlXPathFormatNumber(double number, char buffer[], int buffersize)
1091{
Daniel Veillardcda96922001-08-21 10:56:31 +00001092 switch (xmlXPathIsInf(number)) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001093 case 1:
1094 if (buffersize > (int)sizeof("+Infinity"))
1095 sprintf(buffer, "+Infinity");
1096 break;
1097 case -1:
1098 if (buffersize > (int)sizeof("-Infinity"))
1099 sprintf(buffer, "-Infinity");
1100 break;
1101 default:
Daniel Veillardcda96922001-08-21 10:56:31 +00001102 if (xmlXPathIsNaN(number)) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001103 if (buffersize > (int)sizeof("NaN"))
1104 sprintf(buffer, "NaN");
1105 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +00001106 /* 3 is sign, decimal point, and terminating zero */
1107 char work[DBL_DIG + EXPONENT_DIGITS + 3];
1108 int integer_place, fraction_place;
1109 char *ptr;
1110 char *after_fraction;
1111 double absolute_value;
1112 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001113
Bjorn Reese70a9da52001-04-21 16:57:29 +00001114 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001115
Bjorn Reese70a9da52001-04-21 16:57:29 +00001116 /*
1117 * First choose format - scientific or regular floating point.
1118 * In either case, result is in work, and after_fraction points
1119 * just past the fractional part.
1120 */
1121 if ( ((absolute_value > UPPER_DOUBLE) ||
1122 (absolute_value < LOWER_DOUBLE)) &&
1123 (absolute_value != 0.0) ) {
1124 /* Use scientific notation */
1125 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
1126 fraction_place = DBL_DIG - 1;
1127 snprintf(work, sizeof(work),"%*.*e",
1128 integer_place, fraction_place, number);
1129 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001130 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00001131 else {
1132 /* Use regular notation */
Daniel Veillard56f06462001-06-24 21:34:03 +00001133 if (absolute_value > 0.0)
1134 integer_place = 1 + (int)log10(absolute_value);
1135 else
Daniel Veillarda3067d12001-06-24 21:39:39 +00001136 integer_place = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00001137 fraction_place = (integer_place > 0)
1138 ? DBL_DIG - integer_place
1139 : DBL_DIG;
1140 size = snprintf(work, sizeof(work), "%0.*f",
1141 fraction_place, number);
1142 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001143 }
1144
Bjorn Reese70a9da52001-04-21 16:57:29 +00001145 /* Remove fractional trailing zeroes */
1146 ptr = after_fraction;
1147 while (*(--ptr) == '0')
1148 ;
1149 if (*ptr != '.')
1150 ptr++;
1151 strcpy(ptr, after_fraction);
1152
1153 /* Finally copy result back to caller */
1154 size = strlen(work) + 1;
1155 if (size > buffersize) {
1156 work[buffersize - 1] = 0;
1157 size = buffersize;
1158 }
1159 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001160 }
1161 break;
1162 }
1163}
1164
Owen Taylor3473f882001-02-23 17:55:21 +00001165/************************************************************************
1166 * *
1167 * Error handling routines *
1168 * *
1169 ************************************************************************/
1170
1171
1172const char *xmlXPathErrorMessages[] = {
1173 "Ok",
1174 "Number encoding",
1175 "Unfinished litteral",
1176 "Start of litteral",
1177 "Expected $ for variable reference",
1178 "Undefined variable",
1179 "Invalid predicate",
1180 "Invalid expression",
1181 "Missing closing curly brace",
1182 "Unregistered function",
1183 "Invalid operand",
1184 "Invalid type",
1185 "Invalid number of arguments",
1186 "Invalid context size",
1187 "Invalid context position",
1188 "Memory allocation error",
1189 "Syntax error",
1190 "Resource error",
1191 "Sub resource error",
Daniel Veillard61d80a22001-04-27 17:13:01 +00001192 "Undefined namespace prefix",
1193 "Encoding error",
1194 "Char out of XML range"
Owen Taylor3473f882001-02-23 17:55:21 +00001195};
1196
1197/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001198 * xmlXPatherror:
Owen Taylor3473f882001-02-23 17:55:21 +00001199 * @ctxt: the XPath Parser context
1200 * @file: the file name
1201 * @line: the line number
1202 * @no: the error number
1203 *
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001204 * Formats an error message.
Owen Taylor3473f882001-02-23 17:55:21 +00001205 */
1206void
1207xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1208 int line, int no) {
1209 int n;
1210 const xmlChar *cur;
1211 const xmlChar *base;
1212
1213 xmlGenericError(xmlGenericErrorContext,
1214 "Error %s:%d: %s\n", file, line,
1215 xmlXPathErrorMessages[no]);
1216
1217 cur = ctxt->cur;
1218 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001219 if ((cur == NULL) || (base == NULL))
1220 return;
1221
Owen Taylor3473f882001-02-23 17:55:21 +00001222 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1223 cur--;
1224 }
1225 n = 0;
1226 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1227 cur--;
1228 if ((*cur == '\n') || (*cur == '\r')) cur++;
1229 base = cur;
1230 n = 0;
1231 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1232 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1233 n++;
1234 }
1235 xmlGenericError(xmlGenericErrorContext, "\n");
1236 cur = ctxt->cur;
1237 while ((*cur == '\n') || (*cur == '\r'))
1238 cur--;
1239 n = 0;
1240 while ((cur != base) && (n++ < 80)) {
1241 xmlGenericError(xmlGenericErrorContext, " ");
1242 base++;
1243 }
1244 xmlGenericError(xmlGenericErrorContext,"^\n");
1245}
1246
1247
1248/************************************************************************
1249 * *
1250 * Routines to handle NodeSets *
1251 * *
1252 ************************************************************************/
1253
1254/**
1255 * xmlXPathCmpNodes:
1256 * @node1: the first node
1257 * @node2: the second node
1258 *
1259 * Compare two nodes w.r.t document order
1260 *
1261 * Returns -2 in case of error 1 if first point < second point, 0 if
1262 * that's the same node, -1 otherwise
1263 */
1264int
1265xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1266 int depth1, depth2;
1267 xmlNodePtr cur, root;
1268
1269 if ((node1 == NULL) || (node2 == NULL))
1270 return(-2);
1271 /*
1272 * a couple of optimizations which will avoid computations in most cases
1273 */
1274 if (node1 == node2)
1275 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001276 if ((node1->type == XML_NAMESPACE_DECL) ||
1277 (node2->type == XML_NAMESPACE_DECL))
1278 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001279 if (node1 == node2->prev)
1280 return(1);
1281 if (node1 == node2->next)
1282 return(-1);
1283
1284 /*
1285 * compute depth to root
1286 */
1287 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1288 if (cur == node1)
1289 return(1);
1290 depth2++;
1291 }
1292 root = cur;
1293 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1294 if (cur == node2)
1295 return(-1);
1296 depth1++;
1297 }
1298 /*
1299 * Distinct document (or distinct entities :-( ) case.
1300 */
1301 if (root != cur) {
1302 return(-2);
1303 }
1304 /*
1305 * get the nearest common ancestor.
1306 */
1307 while (depth1 > depth2) {
1308 depth1--;
1309 node1 = node1->parent;
1310 }
1311 while (depth2 > depth1) {
1312 depth2--;
1313 node2 = node2->parent;
1314 }
1315 while (node1->parent != node2->parent) {
1316 node1 = node1->parent;
1317 node2 = node2->parent;
1318 /* should not happen but just in case ... */
1319 if ((node1 == NULL) || (node2 == NULL))
1320 return(-2);
1321 }
1322 /*
1323 * Find who's first.
1324 */
1325 if (node1 == node2->next)
1326 return(-1);
1327 for (cur = node1->next;cur != NULL;cur = cur->next)
1328 if (cur == node2)
1329 return(1);
1330 return(-1); /* assume there is no sibling list corruption */
1331}
1332
1333/**
1334 * xmlXPathNodeSetSort:
1335 * @set: the node set
1336 *
1337 * Sort the node set in document order
1338 */
1339void
1340xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001341 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001342 xmlNodePtr tmp;
1343
1344 if (set == NULL)
1345 return;
1346
1347 /* Use Shell's sort to sort the node-set */
1348 len = set->nodeNr;
1349 for (incr = len / 2; incr > 0; incr /= 2) {
1350 for (i = incr; i < len; i++) {
1351 j = i - incr;
1352 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001353 if (xmlXPathCmpNodes(set->nodeTab[j],
1354 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001355 tmp = set->nodeTab[j];
1356 set->nodeTab[j] = set->nodeTab[j + incr];
1357 set->nodeTab[j + incr] = tmp;
1358 j -= incr;
1359 } else
1360 break;
1361 }
1362 }
1363 }
1364}
1365
1366#define XML_NODESET_DEFAULT 10
1367/**
1368 * xmlXPathNodeSetCreate:
1369 * @val: an initial xmlNodePtr, or NULL
1370 *
1371 * Create a new xmlNodeSetPtr of type double and of value @val
1372 *
1373 * Returns the newly created object.
1374 */
1375xmlNodeSetPtr
1376xmlXPathNodeSetCreate(xmlNodePtr val) {
1377 xmlNodeSetPtr ret;
1378
1379 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1380 if (ret == NULL) {
1381 xmlGenericError(xmlGenericErrorContext,
1382 "xmlXPathNewNodeSet: out of memory\n");
1383 return(NULL);
1384 }
1385 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1386 if (val != NULL) {
1387 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1388 sizeof(xmlNodePtr));
1389 if (ret->nodeTab == NULL) {
1390 xmlGenericError(xmlGenericErrorContext,
1391 "xmlXPathNewNodeSet: out of memory\n");
1392 return(NULL);
1393 }
1394 memset(ret->nodeTab, 0 ,
1395 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1396 ret->nodeMax = XML_NODESET_DEFAULT;
1397 ret->nodeTab[ret->nodeNr++] = val;
1398 }
1399 return(ret);
1400}
1401
1402/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001403 * xmlXPathNodeSetContains:
1404 * @cur: the node-set
1405 * @val: the node
1406 *
1407 * checks whether @cur contains @val
1408 *
1409 * Returns true (1) if @cur contains @val, false (0) otherwise
1410 */
1411int
1412xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
1413 int i;
1414
1415 for (i = 0; i < cur->nodeNr; i++) {
1416 if (cur->nodeTab[i] == val)
1417 return(1);
1418 }
1419 return(0);
1420}
1421
1422/**
Owen Taylor3473f882001-02-23 17:55:21 +00001423 * xmlXPathNodeSetAdd:
1424 * @cur: the initial node set
1425 * @val: a new xmlNodePtr
1426 *
1427 * add a new xmlNodePtr ot an existing NodeSet
1428 */
1429void
1430xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1431 int i;
1432
1433 if (val == NULL) return;
1434
1435 /*
1436 * check against doublons
1437 */
1438 for (i = 0;i < cur->nodeNr;i++)
1439 if (cur->nodeTab[i] == val) return;
1440
1441 /*
1442 * grow the nodeTab if needed
1443 */
1444 if (cur->nodeMax == 0) {
1445 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1446 sizeof(xmlNodePtr));
1447 if (cur->nodeTab == NULL) {
1448 xmlGenericError(xmlGenericErrorContext,
1449 "xmlXPathNodeSetAdd: out of memory\n");
1450 return;
1451 }
1452 memset(cur->nodeTab, 0 ,
1453 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1454 cur->nodeMax = XML_NODESET_DEFAULT;
1455 } else if (cur->nodeNr == cur->nodeMax) {
1456 xmlNodePtr *temp;
1457
1458 cur->nodeMax *= 2;
1459 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1460 sizeof(xmlNodePtr));
1461 if (temp == NULL) {
1462 xmlGenericError(xmlGenericErrorContext,
1463 "xmlXPathNodeSetAdd: out of memory\n");
1464 return;
1465 }
1466 cur->nodeTab = temp;
1467 }
1468 cur->nodeTab[cur->nodeNr++] = val;
1469}
1470
1471/**
1472 * xmlXPathNodeSetAddUnique:
1473 * @cur: the initial node set
1474 * @val: a new xmlNodePtr
1475 *
1476 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1477 * when we are sure the node is not already in the set.
1478 */
1479void
1480xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1481 if (val == NULL) return;
1482
1483 /*
1484 * grow the nodeTab if needed
1485 */
1486 if (cur->nodeMax == 0) {
1487 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1488 sizeof(xmlNodePtr));
1489 if (cur->nodeTab == NULL) {
1490 xmlGenericError(xmlGenericErrorContext,
1491 "xmlXPathNodeSetAddUnique: out of memory\n");
1492 return;
1493 }
1494 memset(cur->nodeTab, 0 ,
1495 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1496 cur->nodeMax = XML_NODESET_DEFAULT;
1497 } else if (cur->nodeNr == cur->nodeMax) {
1498 xmlNodePtr *temp;
1499
1500 cur->nodeMax *= 2;
1501 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1502 sizeof(xmlNodePtr));
1503 if (temp == NULL) {
1504 xmlGenericError(xmlGenericErrorContext,
1505 "xmlXPathNodeSetAddUnique: out of memory\n");
1506 return;
1507 }
1508 cur->nodeTab = temp;
1509 }
1510 cur->nodeTab[cur->nodeNr++] = val;
1511}
1512
1513/**
1514 * xmlXPathNodeSetMerge:
1515 * @val1: the first NodeSet or NULL
1516 * @val2: the second NodeSet
1517 *
1518 * Merges two nodesets, all nodes from @val2 are added to @val1
1519 * if @val1 is NULL, a new set is created and copied from @val2
1520 *
1521 * Returns val1 once extended or NULL in case of error.
1522 */
1523xmlNodeSetPtr
1524xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001525 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001526
1527 if (val2 == NULL) return(val1);
1528 if (val1 == NULL) {
1529 val1 = xmlXPathNodeSetCreate(NULL);
1530 }
1531
1532 initNr = val1->nodeNr;
1533
1534 for (i = 0;i < val2->nodeNr;i++) {
1535 /*
1536 * check against doublons
1537 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001538 skip = 0;
1539 for (j = 0; j < initNr; j++) {
1540 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1541 skip = 1;
1542 break;
1543 }
1544 }
1545 if (skip)
1546 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001547
1548 /*
1549 * grow the nodeTab if needed
1550 */
1551 if (val1->nodeMax == 0) {
1552 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1553 sizeof(xmlNodePtr));
1554 if (val1->nodeTab == NULL) {
1555 xmlGenericError(xmlGenericErrorContext,
1556 "xmlXPathNodeSetMerge: out of memory\n");
1557 return(NULL);
1558 }
1559 memset(val1->nodeTab, 0 ,
1560 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1561 val1->nodeMax = XML_NODESET_DEFAULT;
1562 } else if (val1->nodeNr == val1->nodeMax) {
1563 xmlNodePtr *temp;
1564
1565 val1->nodeMax *= 2;
1566 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1567 sizeof(xmlNodePtr));
1568 if (temp == NULL) {
1569 xmlGenericError(xmlGenericErrorContext,
1570 "xmlXPathNodeSetMerge: out of memory\n");
1571 return(NULL);
1572 }
1573 val1->nodeTab = temp;
1574 }
1575 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1576 }
1577
1578 return(val1);
1579}
1580
1581/**
1582 * xmlXPathNodeSetDel:
1583 * @cur: the initial node set
1584 * @val: an xmlNodePtr
1585 *
1586 * Removes an xmlNodePtr from an existing NodeSet
1587 */
1588void
1589xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1590 int i;
1591
1592 if (cur == NULL) return;
1593 if (val == NULL) return;
1594
1595 /*
1596 * check against doublons
1597 */
1598 for (i = 0;i < cur->nodeNr;i++)
1599 if (cur->nodeTab[i] == val) break;
1600
1601 if (i >= cur->nodeNr) {
1602#ifdef DEBUG
1603 xmlGenericError(xmlGenericErrorContext,
1604 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1605 val->name);
1606#endif
1607 return;
1608 }
1609 cur->nodeNr--;
1610 for (;i < cur->nodeNr;i++)
1611 cur->nodeTab[i] = cur->nodeTab[i + 1];
1612 cur->nodeTab[cur->nodeNr] = NULL;
1613}
1614
1615/**
1616 * xmlXPathNodeSetRemove:
1617 * @cur: the initial node set
1618 * @val: the index to remove
1619 *
1620 * Removes an entry from an existing NodeSet list.
1621 */
1622void
1623xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1624 if (cur == NULL) return;
1625 if (val >= cur->nodeNr) return;
1626 cur->nodeNr--;
1627 for (;val < cur->nodeNr;val++)
1628 cur->nodeTab[val] = cur->nodeTab[val + 1];
1629 cur->nodeTab[cur->nodeNr] = NULL;
1630}
1631
1632/**
1633 * xmlXPathFreeNodeSet:
1634 * @obj: the xmlNodeSetPtr to free
1635 *
1636 * Free the NodeSet compound (not the actual nodes !).
1637 */
1638void
1639xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1640 if (obj == NULL) return;
1641 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001642 xmlFree(obj->nodeTab);
1643 }
Owen Taylor3473f882001-02-23 17:55:21 +00001644 xmlFree(obj);
1645}
1646
1647/**
1648 * xmlXPathFreeValueTree:
1649 * @obj: the xmlNodeSetPtr to free
1650 *
1651 * Free the NodeSet compound and the actual tree, this is different
1652 * from xmlXPathFreeNodeSet()
1653 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001654static void
Owen Taylor3473f882001-02-23 17:55:21 +00001655xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1656 int i;
1657
1658 if (obj == NULL) return;
1659 for (i = 0;i < obj->nodeNr;i++)
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00001660 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001661 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001662
1663 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001664 xmlFree(obj->nodeTab);
1665 }
Owen Taylor3473f882001-02-23 17:55:21 +00001666 xmlFree(obj);
1667}
1668
1669#if defined(DEBUG) || defined(DEBUG_STEP)
1670/**
1671 * xmlGenericErrorContextNodeSet:
1672 * @output: a FILE * for the output
1673 * @obj: the xmlNodeSetPtr to free
1674 *
1675 * Quick display of a NodeSet
1676 */
1677void
1678xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1679 int i;
1680
1681 if (output == NULL) output = xmlGenericErrorContext;
1682 if (obj == NULL) {
1683 fprintf(output, "NodeSet == NULL !\n");
1684 return;
1685 }
1686 if (obj->nodeNr == 0) {
1687 fprintf(output, "NodeSet is empty\n");
1688 return;
1689 }
1690 if (obj->nodeTab == NULL) {
1691 fprintf(output, " nodeTab == NULL !\n");
1692 return;
1693 }
1694 for (i = 0; i < obj->nodeNr; i++) {
1695 if (obj->nodeTab[i] == NULL) {
1696 fprintf(output, " NULL !\n");
1697 return;
1698 }
1699 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1700 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1701 fprintf(output, " /");
1702 else if (obj->nodeTab[i]->name == NULL)
1703 fprintf(output, " noname!");
1704 else fprintf(output, " %s", obj->nodeTab[i]->name);
1705 }
1706 fprintf(output, "\n");
1707}
1708#endif
1709
1710/**
1711 * xmlXPathNewNodeSet:
1712 * @val: the NodePtr value
1713 *
1714 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1715 * it with the single Node @val
1716 *
1717 * Returns the newly created object.
1718 */
1719xmlXPathObjectPtr
1720xmlXPathNewNodeSet(xmlNodePtr val) {
1721 xmlXPathObjectPtr ret;
1722
1723 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1724 if (ret == NULL) {
1725 xmlGenericError(xmlGenericErrorContext,
1726 "xmlXPathNewNodeSet: out of memory\n");
1727 return(NULL);
1728 }
1729 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1730 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001731 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001732 ret->nodesetval = xmlXPathNodeSetCreate(val);
1733 return(ret);
1734}
1735
1736/**
1737 * xmlXPathNewValueTree:
1738 * @val: the NodePtr value
1739 *
1740 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1741 * it with the tree root @val
1742 *
1743 * Returns the newly created object.
1744 */
1745xmlXPathObjectPtr
1746xmlXPathNewValueTree(xmlNodePtr val) {
1747 xmlXPathObjectPtr ret;
1748
1749 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1750 if (ret == NULL) {
1751 xmlGenericError(xmlGenericErrorContext,
1752 "xmlXPathNewNodeSet: out of memory\n");
1753 return(NULL);
1754 }
1755 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1756 ret->type = XPATH_XSLT_TREE;
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00001757 ret->boolval = 1;
1758 ret->user = (void *) val;
Owen Taylor3473f882001-02-23 17:55:21 +00001759 ret->nodesetval = xmlXPathNodeSetCreate(val);
1760 return(ret);
1761}
1762
1763/**
1764 * xmlXPathNewNodeSetList:
1765 * @val: an existing NodeSet
1766 *
1767 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1768 * it with the Nodeset @val
1769 *
1770 * Returns the newly created object.
1771 */
1772xmlXPathObjectPtr
1773xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1774 xmlXPathObjectPtr ret;
1775 int i;
1776
1777 if (val == NULL)
1778 ret = NULL;
1779 else if (val->nodeTab == NULL)
1780 ret = xmlXPathNewNodeSet(NULL);
1781 else
1782 {
1783 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1784 for (i = 1; i < val->nodeNr; ++i)
1785 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1786 }
1787
1788 return(ret);
1789}
1790
1791/**
1792 * xmlXPathWrapNodeSet:
1793 * @val: the NodePtr value
1794 *
1795 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1796 *
1797 * Returns the newly created object.
1798 */
1799xmlXPathObjectPtr
1800xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1801 xmlXPathObjectPtr ret;
1802
1803 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1804 if (ret == NULL) {
1805 xmlGenericError(xmlGenericErrorContext,
1806 "xmlXPathWrapNodeSet: out of memory\n");
1807 return(NULL);
1808 }
1809 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1810 ret->type = XPATH_NODESET;
1811 ret->nodesetval = val;
1812 return(ret);
1813}
1814
1815/**
1816 * xmlXPathFreeNodeSetList:
1817 * @obj: an existing NodeSetList object
1818 *
1819 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1820 * the list contrary to xmlXPathFreeObject().
1821 */
1822void
1823xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1824 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001825 xmlFree(obj);
1826}
1827
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001828/**
1829 * xmlXPathDifference:
1830 * @nodes1: a node-set
1831 * @nodes2: a node-set
1832 *
1833 * Implements the EXSLT - Sets difference() function:
1834 * node-set set:difference (node-set, node-set)
1835 *
1836 * Returns the difference between the two node sets, or nodes1 if
1837 * nodes2 is empty
1838 */
1839xmlNodeSetPtr
1840xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1841 xmlNodeSetPtr ret;
1842 int i, l1;
1843 xmlNodePtr cur;
1844
1845 if (xmlXPathNodeSetIsEmpty(nodes2))
1846 return(nodes1);
1847
1848 ret = xmlXPathNodeSetCreate(NULL);
1849 if (xmlXPathNodeSetIsEmpty(nodes1))
1850 return(ret);
1851
1852 l1 = xmlXPathNodeSetGetLength(nodes1);
1853
1854 for (i = 0; i < l1; i++) {
1855 cur = xmlXPathNodeSetItem(nodes1, i);
1856 if (!xmlXPathNodeSetContains(nodes2, cur))
1857 xmlXPathNodeSetAddUnique(ret, cur);
1858 }
1859 return(ret);
1860}
1861
1862/**
1863 * xmlXPathIntersection:
1864 * @nodes1: a node-set
1865 * @nodes2: a node-set
1866 *
1867 * Implements the EXSLT - Sets intersection() function:
1868 * node-set set:intersection (node-set, node-set)
1869 *
1870 * Returns a node set comprising the nodes that are within both the
1871 * node sets passed as arguments
1872 */
1873xmlNodeSetPtr
1874xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1875 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
1876 int i, l1;
1877 xmlNodePtr cur;
1878
1879 if (xmlXPathNodeSetIsEmpty(nodes1))
1880 return(ret);
1881 if (xmlXPathNodeSetIsEmpty(nodes2))
1882 return(ret);
1883
1884 l1 = xmlXPathNodeSetGetLength(nodes1);
1885
1886 for (i = 0; i < l1; i++) {
1887 cur = xmlXPathNodeSetItem(nodes1, i);
1888 if (xmlXPathNodeSetContains(nodes2, cur))
1889 xmlXPathNodeSetAddUnique(ret, cur);
1890 }
1891 return(ret);
1892}
1893
1894/**
1895 * xmlXPathDistinctSorted:
1896 * @nodes: a node-set, sorted by document order
1897 *
1898 * Implements the EXSLT - Sets distinct() function:
1899 * node-set set:distinct (node-set)
1900 *
1901 * Returns a subset of the nodes contained in @nodes, or @nodes if
1902 * it is empty
1903 */
1904xmlNodeSetPtr
1905xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
1906 xmlNodeSetPtr ret;
1907 xmlHashTablePtr hash;
1908 int i, l;
1909 xmlChar * strval;
1910 xmlNodePtr cur;
1911
1912 if (xmlXPathNodeSetIsEmpty(nodes))
1913 return(nodes);
1914
1915 ret = xmlXPathNodeSetCreate(NULL);
1916 l = xmlXPathNodeSetGetLength(nodes);
1917 hash = xmlHashCreate (l);
1918 for (i = 0; i < l; i++) {
1919 cur = xmlXPathNodeSetItem(nodes, i);
1920 strval = xmlXPathCastNodeToString(cur);
1921 if (xmlHashLookup(hash, strval) == NULL) {
1922 xmlHashAddEntry(hash, strval, strval);
1923 xmlXPathNodeSetAddUnique(ret, cur);
1924 } else {
1925 xmlFree(strval);
1926 }
1927 }
1928 xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
1929 return(ret);
1930}
1931
1932/**
1933 * xmlXPathDistinct:
1934 * @nodes: a node-set
1935 *
1936 * Implements the EXSLT - Sets distinct() function:
1937 * node-set set:distinct (node-set)
1938 * @nodes is sorted by document order, then #exslSetsDistinctSorted
1939 * is called with the sorted node-set
1940 *
1941 * Returns a subset of the nodes contained in @nodes, or @nodes if
1942 * it is empty
1943 */
1944xmlNodeSetPtr
1945xmlXPathDistinct (xmlNodeSetPtr nodes) {
1946 if (xmlXPathNodeSetIsEmpty(nodes))
1947 return(nodes);
1948
1949 xmlXPathNodeSetSort(nodes);
1950 return(xmlXPathDistinctSorted(nodes));
1951}
1952
1953/**
1954 * xmlXPathHasSameNodes:
1955 * @nodes1: a node-set
1956 * @nodes2: a node-set
1957 *
1958 * Implements the EXSLT - Sets has-same-nodes function:
1959 * boolean set:has-same-node(node-set, node-set)
1960 *
1961 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
1962 * otherwise
1963 */
1964int
1965xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1966 int i, l;
1967 xmlNodePtr cur;
1968
1969 if (xmlXPathNodeSetIsEmpty(nodes1) ||
1970 xmlXPathNodeSetIsEmpty(nodes2))
1971 return(0);
1972
1973 l = xmlXPathNodeSetGetLength(nodes1);
1974 for (i = 0; i < l; i++) {
1975 cur = xmlXPathNodeSetItem(nodes1, i);
1976 if (xmlXPathNodeSetContains(nodes2, cur))
1977 return(1);
1978 }
1979 return(0);
1980}
1981
1982/**
1983 * xmlXPathNodeLeadingSorted:
1984 * @nodes: a node-set, sorted by document order
1985 * @node: a node
1986 *
1987 * Implements the EXSLT - Sets leading() function:
1988 * node-set set:leading (node-set, node-set)
1989 *
1990 * Returns the nodes in @nodes that precede @node in document order,
1991 * @nodes if @node is NULL or an empty node-set if @nodes
1992 * doesn't contain @node
1993 */
1994xmlNodeSetPtr
1995xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
1996 int i, l;
1997 xmlNodePtr cur;
1998 xmlNodeSetPtr ret;
1999
2000 if (node == NULL)
2001 return(nodes);
2002
2003 ret = xmlXPathNodeSetCreate(NULL);
2004 if (xmlXPathNodeSetIsEmpty(nodes) ||
2005 (!xmlXPathNodeSetContains(nodes, node)))
2006 return(ret);
2007
2008 l = xmlXPathNodeSetGetLength(nodes);
2009 for (i = 0; i < l; i++) {
2010 cur = xmlXPathNodeSetItem(nodes, i);
2011 if (cur == node)
2012 break;
2013 xmlXPathNodeSetAddUnique(ret, cur);
2014 }
2015 return(ret);
2016}
2017
2018/**
2019 * xmlXPathNodeLeading:
2020 * @nodes: a node-set
2021 * @node: a node
2022 *
2023 * Implements the EXSLT - Sets leading() function:
2024 * node-set set:leading (node-set, node-set)
2025 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
2026 * is called.
2027 *
2028 * Returns the nodes in @nodes that precede @node in document order,
2029 * @nodes if @node is NULL or an empty node-set if @nodes
2030 * doesn't contain @node
2031 */
2032xmlNodeSetPtr
2033xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
2034 xmlXPathNodeSetSort(nodes);
2035 return(xmlXPathNodeLeadingSorted(nodes, node));
2036}
2037
2038/**
2039 * xmlXPathLeadingSorted:
2040 * @nodes1: a node-set, sorted by document order
2041 * @nodes2: a node-set, sorted by document order
2042 *
2043 * Implements the EXSLT - Sets leading() function:
2044 * node-set set:leading (node-set, node-set)
2045 *
2046 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2047 * in document order, @nodes1 if @nodes2 is NULL or empty or
2048 * an empty node-set if @nodes1 doesn't contain @nodes2
2049 */
2050xmlNodeSetPtr
2051xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2052 if (xmlXPathNodeSetIsEmpty(nodes2))
2053 return(nodes1);
2054 return(xmlXPathNodeLeadingSorted(nodes1,
2055 xmlXPathNodeSetItem(nodes2, 1)));
2056}
2057
2058/**
2059 * xmlXPathLeading:
2060 * @nodes1: a node-set
2061 * @nodes2: a node-set
2062 *
2063 * Implements the EXSLT - Sets leading() function:
2064 * node-set set:leading (node-set, node-set)
2065 * @nodes1 and @nodes2 are sorted by document order, then
2066 * #exslSetsLeadingSorted is called.
2067 *
2068 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2069 * in document order, @nodes1 if @nodes2 is NULL or empty or
2070 * an empty node-set if @nodes1 doesn't contain @nodes2
2071 */
2072xmlNodeSetPtr
2073xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2074 if (xmlXPathNodeSetIsEmpty(nodes2))
2075 return(nodes1);
2076 if (xmlXPathNodeSetIsEmpty(nodes1))
2077 return(xmlXPathNodeSetCreate(NULL));
2078 xmlXPathNodeSetSort(nodes1);
2079 xmlXPathNodeSetSort(nodes2);
2080 return(xmlXPathNodeLeadingSorted(nodes1,
2081 xmlXPathNodeSetItem(nodes2, 1)));
2082}
2083
2084/**
2085 * xmlXPathNodeTrailingSorted:
2086 * @nodes: a node-set, sorted by document order
2087 * @node: a node
2088 *
2089 * Implements the EXSLT - Sets trailing() function:
2090 * node-set set:trailing (node-set, node-set)
2091 *
2092 * Returns the nodes in @nodes that follow @node in document order,
2093 * @nodes if @node is NULL or an empty node-set if @nodes
2094 * doesn't contain @node
2095 */
2096xmlNodeSetPtr
2097xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
2098 int i, l;
2099 xmlNodePtr cur;
2100 xmlNodeSetPtr ret;
2101
2102 if (node == NULL)
2103 return(nodes);
2104
2105 ret = xmlXPathNodeSetCreate(NULL);
2106 if (xmlXPathNodeSetIsEmpty(nodes) ||
2107 (!xmlXPathNodeSetContains(nodes, node)))
2108 return(ret);
2109
2110 l = xmlXPathNodeSetGetLength(nodes);
Thomas Broyerf186c822001-07-31 23:30:37 +00002111 for (i = l; i > 0; i--) {
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002112 cur = xmlXPathNodeSetItem(nodes, i);
2113 if (cur == node)
2114 break;
2115 xmlXPathNodeSetAddUnique(ret, cur);
2116 }
2117 return(ret);
2118}
2119
2120/**
2121 * xmlXPathNodeTrailing:
2122 * @nodes: a node-set
2123 * @node: a node
2124 *
2125 * Implements the EXSLT - Sets trailing() function:
2126 * node-set set:trailing (node-set, node-set)
2127 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
2128 * is called.
2129 *
2130 * Returns the nodes in @nodes that follow @node in document order,
2131 * @nodes if @node is NULL or an empty node-set if @nodes
2132 * doesn't contain @node
2133 */
2134xmlNodeSetPtr
2135xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
2136 xmlXPathNodeSetSort(nodes);
2137 return(xmlXPathNodeTrailingSorted(nodes, node));
2138}
2139
2140/**
2141 * xmlXPathTrailingSorted:
2142 * @nodes1: a node-set, sorted by document order
2143 * @nodes2: a node-set, sorted by document order
2144 *
2145 * Implements the EXSLT - Sets trailing() function:
2146 * node-set set:trailing (node-set, node-set)
2147 *
2148 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2149 * in document order, @nodes1 if @nodes2 is NULL or empty or
2150 * an empty node-set if @nodes1 doesn't contain @nodes2
2151 */
2152xmlNodeSetPtr
2153xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2154 if (xmlXPathNodeSetIsEmpty(nodes2))
2155 return(nodes1);
2156 return(xmlXPathNodeTrailingSorted(nodes1,
2157 xmlXPathNodeSetItem(nodes2, 0)));
2158}
2159
2160/**
2161 * xmlXPathTrailing:
2162 * @nodes1: a node-set
2163 * @nodes2: a node-set
2164 *
2165 * Implements the EXSLT - Sets trailing() function:
2166 * node-set set:trailing (node-set, node-set)
2167 * @nodes1 and @nodes2 are sorted by document order, then
2168 * #xmlXPathTrailingSorted is called.
2169 *
2170 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2171 * in document order, @nodes1 if @nodes2 is NULL or empty or
2172 * an empty node-set if @nodes1 doesn't contain @nodes2
2173 */
2174xmlNodeSetPtr
2175xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2176 if (xmlXPathNodeSetIsEmpty(nodes2))
2177 return(nodes1);
2178 if (xmlXPathNodeSetIsEmpty(nodes1))
2179 return(xmlXPathNodeSetCreate(NULL));
2180 xmlXPathNodeSetSort(nodes1);
2181 xmlXPathNodeSetSort(nodes2);
2182 return(xmlXPathNodeTrailingSorted(nodes1,
2183 xmlXPathNodeSetItem(nodes2, 0)));
2184}
2185
Owen Taylor3473f882001-02-23 17:55:21 +00002186/************************************************************************
2187 * *
2188 * Routines to handle extra functions *
2189 * *
2190 ************************************************************************/
2191
2192/**
2193 * xmlXPathRegisterFunc:
2194 * @ctxt: the XPath context
2195 * @name: the function name
2196 * @f: the function implementation or NULL
2197 *
2198 * Register a new function. If @f is NULL it unregisters the function
2199 *
2200 * Returns 0 in case of success, -1 in case of error
2201 */
2202int
2203xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
2204 xmlXPathFunction f) {
2205 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
2206}
2207
2208/**
2209 * xmlXPathRegisterFuncNS:
2210 * @ctxt: the XPath context
2211 * @name: the function name
2212 * @ns_uri: the function namespace URI
2213 * @f: the function implementation or NULL
2214 *
2215 * Register a new function. If @f is NULL it unregisters the function
2216 *
2217 * Returns 0 in case of success, -1 in case of error
2218 */
2219int
2220xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2221 const xmlChar *ns_uri, xmlXPathFunction f) {
2222 if (ctxt == NULL)
2223 return(-1);
2224 if (name == NULL)
2225 return(-1);
2226
2227 if (ctxt->funcHash == NULL)
2228 ctxt->funcHash = xmlHashCreate(0);
2229 if (ctxt->funcHash == NULL)
2230 return(-1);
2231 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
2232}
2233
2234/**
Thomas Broyerba4ad322001-07-26 16:55:21 +00002235 * xmlXPathRegisterFuncLookup:
2236 * @ctxt: the XPath context
2237 * @f: the lookup function
2238 * @data: the lookup data
2239 *
2240 * Registers an external mecanism to do function lookup.
2241 */
2242void
2243xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
2244 xmlXPathFuncLookupFunc f,
2245 void *funcCtxt) {
2246 if (ctxt == NULL)
2247 return;
2248 ctxt->funcLookupFunc = (void *) f;
2249 ctxt->funcLookupData = funcCtxt;
2250}
2251
2252/**
Owen Taylor3473f882001-02-23 17:55:21 +00002253 * xmlXPathFunctionLookup:
2254 * @ctxt: the XPath context
2255 * @name: the function name
2256 *
2257 * Search in the Function array of the context for the given
2258 * function.
2259 *
2260 * Returns the xmlXPathFunction or NULL if not found
2261 */
2262xmlXPathFunction
2263xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
Thomas Broyerba4ad322001-07-26 16:55:21 +00002264 if (ctxt == NULL)
2265 return (NULL);
2266
2267 if (ctxt->funcLookupFunc != NULL) {
2268 xmlXPathFunction ret;
2269
2270 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2271 (ctxt->funcLookupData, name, NULL);
2272 if (ret != NULL)
2273 return(ret);
2274 }
Owen Taylor3473f882001-02-23 17:55:21 +00002275 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
2276}
2277
2278/**
2279 * xmlXPathFunctionLookupNS:
2280 * @ctxt: the XPath context
2281 * @name: the function name
2282 * @ns_uri: the function namespace URI
2283 *
2284 * Search in the Function array of the context for the given
2285 * function.
2286 *
2287 * Returns the xmlXPathFunction or NULL if not found
2288 */
2289xmlXPathFunction
2290xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2291 const xmlChar *ns_uri) {
2292 if (ctxt == NULL)
2293 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002294 if (name == NULL)
2295 return(NULL);
2296
Thomas Broyerba4ad322001-07-26 16:55:21 +00002297 if (ctxt->funcLookupFunc != NULL) {
2298 xmlXPathFunction ret;
2299
2300 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2301 (ctxt->funcLookupData, name, ns_uri);
2302 if (ret != NULL)
2303 return(ret);
2304 }
2305
2306 if (ctxt->funcHash == NULL)
2307 return(NULL);
2308
Owen Taylor3473f882001-02-23 17:55:21 +00002309 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
2310}
2311
2312/**
2313 * xmlXPathRegisteredFuncsCleanup:
2314 * @ctxt: the XPath context
2315 *
2316 * Cleanup the XPath context data associated to registered functions
2317 */
2318void
2319xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
2320 if (ctxt == NULL)
2321 return;
2322
2323 xmlHashFree(ctxt->funcHash, NULL);
2324 ctxt->funcHash = NULL;
2325}
2326
2327/************************************************************************
2328 * *
2329 * Routines to handle Variable *
2330 * *
2331 ************************************************************************/
2332
2333/**
2334 * xmlXPathRegisterVariable:
2335 * @ctxt: the XPath context
2336 * @name: the variable name
2337 * @value: the variable value or NULL
2338 *
2339 * Register a new variable value. If @value is NULL it unregisters
2340 * the variable
2341 *
2342 * Returns 0 in case of success, -1 in case of error
2343 */
2344int
2345xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
2346 xmlXPathObjectPtr value) {
2347 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
2348}
2349
2350/**
2351 * xmlXPathRegisterVariableNS:
2352 * @ctxt: the XPath context
2353 * @name: the variable name
2354 * @ns_uri: the variable namespace URI
2355 * @value: the variable value or NULL
2356 *
2357 * Register a new variable value. If @value is NULL it unregisters
2358 * the variable
2359 *
2360 * Returns 0 in case of success, -1 in case of error
2361 */
2362int
2363xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2364 const xmlChar *ns_uri,
2365 xmlXPathObjectPtr value) {
2366 if (ctxt == NULL)
2367 return(-1);
2368 if (name == NULL)
2369 return(-1);
2370
2371 if (ctxt->varHash == NULL)
2372 ctxt->varHash = xmlHashCreate(0);
2373 if (ctxt->varHash == NULL)
2374 return(-1);
2375 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
2376 (void *) value,
2377 (xmlHashDeallocator)xmlXPathFreeObject));
2378}
2379
2380/**
2381 * xmlXPathRegisterVariableLookup:
2382 * @ctxt: the XPath context
2383 * @f: the lookup function
2384 * @data: the lookup data
2385 *
2386 * register an external mechanism to do variable lookup
2387 */
2388void
2389xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
2390 xmlXPathVariableLookupFunc f, void *data) {
2391 if (ctxt == NULL)
2392 return;
2393 ctxt->varLookupFunc = (void *) f;
2394 ctxt->varLookupData = data;
2395}
2396
2397/**
2398 * xmlXPathVariableLookup:
2399 * @ctxt: the XPath context
2400 * @name: the variable name
2401 *
2402 * Search in the Variable array of the context for the given
2403 * variable value.
2404 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002405 * Returns a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002406 */
2407xmlXPathObjectPtr
2408xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
2409 if (ctxt == NULL)
2410 return(NULL);
2411
2412 if (ctxt->varLookupFunc != NULL) {
2413 xmlXPathObjectPtr ret;
2414
2415 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2416 (ctxt->varLookupData, name, NULL);
2417 if (ret != NULL) return(ret);
2418 }
2419 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
2420}
2421
2422/**
2423 * xmlXPathVariableLookupNS:
2424 * @ctxt: the XPath context
2425 * @name: the variable name
2426 * @ns_uri: the variable namespace URI
2427 *
2428 * Search in the Variable array of the context for the given
Daniel Veillard73c9c042001-07-05 20:02:54 +00002429 * variable value.
Owen Taylor3473f882001-02-23 17:55:21 +00002430 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002431 * Returns the a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002432 */
2433xmlXPathObjectPtr
2434xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2435 const xmlChar *ns_uri) {
2436 if (ctxt == NULL)
2437 return(NULL);
2438
2439 if (ctxt->varLookupFunc != NULL) {
2440 xmlXPathObjectPtr ret;
2441
2442 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2443 (ctxt->varLookupData, name, ns_uri);
2444 if (ret != NULL) return(ret);
2445 }
2446
2447 if (ctxt->varHash == NULL)
2448 return(NULL);
2449 if (name == NULL)
2450 return(NULL);
2451
Daniel Veillard8c357d52001-07-03 23:43:33 +00002452 return(xmlXPathObjectCopy((xmlXPathObjectPtr)
2453 xmlHashLookup2(ctxt->varHash, name, ns_uri)));
Owen Taylor3473f882001-02-23 17:55:21 +00002454}
2455
2456/**
2457 * xmlXPathRegisteredVariablesCleanup:
2458 * @ctxt: the XPath context
2459 *
2460 * Cleanup the XPath context data associated to registered variables
2461 */
2462void
2463xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
2464 if (ctxt == NULL)
2465 return;
2466
Daniel Veillard76d66f42001-05-16 21:05:17 +00002467 xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
Owen Taylor3473f882001-02-23 17:55:21 +00002468 ctxt->varHash = NULL;
2469}
2470
2471/**
2472 * xmlXPathRegisterNs:
2473 * @ctxt: the XPath context
2474 * @prefix: the namespace prefix
2475 * @ns_uri: the namespace name
2476 *
2477 * Register a new namespace. If @ns_uri is NULL it unregisters
2478 * the namespace
2479 *
2480 * Returns 0 in case of success, -1 in case of error
2481 */
2482int
2483xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
2484 const xmlChar *ns_uri) {
2485 if (ctxt == NULL)
2486 return(-1);
2487 if (prefix == NULL)
2488 return(-1);
2489
2490 if (ctxt->nsHash == NULL)
2491 ctxt->nsHash = xmlHashCreate(10);
2492 if (ctxt->nsHash == NULL)
2493 return(-1);
2494 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
2495 (xmlHashDeallocator)xmlFree));
2496}
2497
2498/**
2499 * xmlXPathNsLookup:
2500 * @ctxt: the XPath context
2501 * @prefix: the namespace prefix value
2502 *
2503 * Search in the namespace declaration array of the context for the given
2504 * namespace name associated to the given prefix
2505 *
2506 * Returns the value or NULL if not found
2507 */
2508const xmlChar *
2509xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
2510 if (ctxt == NULL)
2511 return(NULL);
2512 if (prefix == NULL)
2513 return(NULL);
2514
2515#ifdef XML_XML_NAMESPACE
2516 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
2517 return(XML_XML_NAMESPACE);
2518#endif
2519
Daniel Veillardc8f620b2001-04-30 20:31:33 +00002520 if (ctxt->namespaces != NULL) {
2521 int i;
2522
2523 for (i = 0;i < ctxt->nsNr;i++) {
2524 if ((ctxt->namespaces[i] != NULL) &&
2525 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
2526 return(ctxt->namespaces[i]->href);
2527 }
2528 }
Owen Taylor3473f882001-02-23 17:55:21 +00002529
2530 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
2531}
2532
2533/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002534 * xmlXPathRegisteredNsCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +00002535 * @ctxt: the XPath context
2536 *
2537 * Cleanup the XPath context data associated to registered variables
2538 */
2539void
2540xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
2541 if (ctxt == NULL)
2542 return;
2543
2544 xmlHashFree(ctxt->nsHash, NULL);
2545 ctxt->nsHash = NULL;
2546}
2547
2548/************************************************************************
2549 * *
2550 * Routines to handle Values *
2551 * *
2552 ************************************************************************/
2553
2554/* Allocations are terrible, one need to optimize all this !!! */
2555
2556/**
2557 * xmlXPathNewFloat:
2558 * @val: the double value
2559 *
2560 * Create a new xmlXPathObjectPtr of type double and of value @val
2561 *
2562 * Returns the newly created object.
2563 */
2564xmlXPathObjectPtr
2565xmlXPathNewFloat(double val) {
2566 xmlXPathObjectPtr ret;
2567
2568 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2569 if (ret == NULL) {
2570 xmlGenericError(xmlGenericErrorContext,
2571 "xmlXPathNewFloat: out of memory\n");
2572 return(NULL);
2573 }
2574 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2575 ret->type = XPATH_NUMBER;
2576 ret->floatval = val;
2577 return(ret);
2578}
2579
2580/**
2581 * xmlXPathNewBoolean:
2582 * @val: the boolean value
2583 *
2584 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2585 *
2586 * Returns the newly created object.
2587 */
2588xmlXPathObjectPtr
2589xmlXPathNewBoolean(int val) {
2590 xmlXPathObjectPtr ret;
2591
2592 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2593 if (ret == NULL) {
2594 xmlGenericError(xmlGenericErrorContext,
2595 "xmlXPathNewBoolean: out of memory\n");
2596 return(NULL);
2597 }
2598 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2599 ret->type = XPATH_BOOLEAN;
2600 ret->boolval = (val != 0);
2601 return(ret);
2602}
2603
2604/**
2605 * xmlXPathNewString:
2606 * @val: the xmlChar * value
2607 *
2608 * Create a new xmlXPathObjectPtr of type string and of value @val
2609 *
2610 * Returns the newly created object.
2611 */
2612xmlXPathObjectPtr
2613xmlXPathNewString(const xmlChar *val) {
2614 xmlXPathObjectPtr ret;
2615
2616 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2617 if (ret == NULL) {
2618 xmlGenericError(xmlGenericErrorContext,
2619 "xmlXPathNewString: out of memory\n");
2620 return(NULL);
2621 }
2622 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2623 ret->type = XPATH_STRING;
2624 if (val != NULL)
2625 ret->stringval = xmlStrdup(val);
2626 else
2627 ret->stringval = xmlStrdup((const xmlChar *)"");
2628 return(ret);
2629}
2630
2631/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002632 * xmlXPathWrapString:
2633 * @val: the xmlChar * value
2634 *
2635 * Wraps the @val string into an XPath object.
2636 *
2637 * Returns the newly created object.
2638 */
2639xmlXPathObjectPtr
2640xmlXPathWrapString (xmlChar *val) {
2641 xmlXPathObjectPtr ret;
2642
2643 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2644 if (ret == NULL) {
2645 xmlGenericError(xmlGenericErrorContext,
2646 "xmlXPathWrapString: out of memory\n");
2647 return(NULL);
2648 }
2649 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2650 ret->type = XPATH_STRING;
2651 ret->stringval = val;
2652 return(ret);
2653}
2654
2655/**
Owen Taylor3473f882001-02-23 17:55:21 +00002656 * xmlXPathNewCString:
2657 * @val: the char * value
2658 *
2659 * Create a new xmlXPathObjectPtr of type string and of value @val
2660 *
2661 * Returns the newly created object.
2662 */
2663xmlXPathObjectPtr
2664xmlXPathNewCString(const char *val) {
2665 xmlXPathObjectPtr ret;
2666
2667 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2668 if (ret == NULL) {
2669 xmlGenericError(xmlGenericErrorContext,
2670 "xmlXPathNewCString: out of memory\n");
2671 return(NULL);
2672 }
2673 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2674 ret->type = XPATH_STRING;
2675 ret->stringval = xmlStrdup(BAD_CAST val);
2676 return(ret);
2677}
2678
2679/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002680 * xmlXPathWrapCString:
2681 * @val: the char * value
2682 *
2683 * Wraps a string into an XPath object.
2684 *
2685 * Returns the newly created object.
2686 */
2687xmlXPathObjectPtr
2688xmlXPathWrapCString (char * val) {
2689 return(xmlXPathWrapString((xmlChar *)(val)));
2690}
2691
2692/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002693 * xmlXPathWrapExternal:
2694 * @val: the user data
2695 *
2696 * Wraps the @val data into an XPath object.
2697 *
2698 * Returns the newly created object.
2699 */
2700xmlXPathObjectPtr
2701xmlXPathWrapExternal (void *val) {
2702 xmlXPathObjectPtr ret;
2703
2704 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2705 if (ret == NULL) {
2706 xmlGenericError(xmlGenericErrorContext,
2707 "xmlXPathWrapString: out of memory\n");
2708 return(NULL);
2709 }
2710 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2711 ret->type = XPATH_USERS;
2712 ret->user = val;
2713 return(ret);
2714}
2715
2716/**
Owen Taylor3473f882001-02-23 17:55:21 +00002717 * xmlXPathObjectCopy:
2718 * @val: the original object
2719 *
2720 * allocate a new copy of a given object
2721 *
2722 * Returns the newly created object.
2723 */
2724xmlXPathObjectPtr
2725xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2726 xmlXPathObjectPtr ret;
2727
2728 if (val == NULL)
2729 return(NULL);
2730
2731 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2732 if (ret == NULL) {
2733 xmlGenericError(xmlGenericErrorContext,
2734 "xmlXPathObjectCopy: out of memory\n");
2735 return(NULL);
2736 }
2737 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2738 switch (val->type) {
2739 case XPATH_BOOLEAN:
2740 case XPATH_NUMBER:
2741 case XPATH_POINT:
2742 case XPATH_RANGE:
2743 break;
2744 case XPATH_STRING:
2745 ret->stringval = xmlStrdup(val->stringval);
2746 break;
2747 case XPATH_XSLT_TREE:
2748 if ((val->nodesetval != NULL) &&
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002749 (val->nodesetval->nodeTab != NULL)) {
2750 ret->boolval = 1;
2751 ret->user = xmlCopyNode(val->nodesetval->nodeTab[0], 1);
Owen Taylor3473f882001-02-23 17:55:21 +00002752 ret->nodesetval = xmlXPathNodeSetCreate(
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002753 (xmlNodePtr) ret->user);
2754 } else
Owen Taylor3473f882001-02-23 17:55:21 +00002755 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002756 /* Deallocate the copied tree value */
Owen Taylor3473f882001-02-23 17:55:21 +00002757 break;
2758 case XPATH_NODESET:
2759 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002760 /* Do not deallocate the copied tree value */
2761 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002762 break;
2763 case XPATH_LOCATIONSET:
2764#ifdef LIBXML_XPTR_ENABLED
2765 {
2766 xmlLocationSetPtr loc = val->user;
2767 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2768 break;
2769 }
2770#endif
2771 case XPATH_UNDEFINED:
2772 case XPATH_USERS:
2773 xmlGenericError(xmlGenericErrorContext,
2774 "xmlXPathObjectCopy: unsupported type %d\n",
2775 val->type);
2776 break;
2777 }
2778 return(ret);
2779}
2780
2781/**
2782 * xmlXPathFreeObject:
2783 * @obj: the object to free
2784 *
2785 * Free up an xmlXPathObjectPtr object.
2786 */
2787void
2788xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2789 if (obj == NULL) return;
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002790 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
Daniel Veillard77851712001-02-27 21:54:07 +00002791 if (obj->boolval) {
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002792 if (obj->user != NULL) {
2793 xmlFreeNodeList((xmlNodePtr) obj->user);
2794 xmlXPathFreeNodeSet(obj->nodesetval);
2795 } else if (obj->nodesetval != NULL)
Daniel Veillard77851712001-02-27 21:54:07 +00002796 xmlXPathFreeValueTree(obj->nodesetval);
2797 } else {
2798 if (obj->nodesetval != NULL)
2799 xmlXPathFreeNodeSet(obj->nodesetval);
2800 }
Owen Taylor3473f882001-02-23 17:55:21 +00002801#ifdef LIBXML_XPTR_ENABLED
2802 } else if (obj->type == XPATH_LOCATIONSET) {
2803 if (obj->user != NULL)
2804 xmlXPtrFreeLocationSet(obj->user);
2805#endif
2806 } else if (obj->type == XPATH_STRING) {
2807 if (obj->stringval != NULL)
2808 xmlFree(obj->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00002809 }
2810
Owen Taylor3473f882001-02-23 17:55:21 +00002811 xmlFree(obj);
2812}
2813
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002814
2815/************************************************************************
2816 * *
2817 * Type Casting Routines *
2818 * *
2819 ************************************************************************/
2820
2821/**
2822 * xmlXPathCastBooleanToString:
2823 * @val: a boolean
2824 *
2825 * Converts a boolean to its string value.
2826 *
2827 * Returns a newly allocated string.
2828 */
2829xmlChar *
2830xmlXPathCastBooleanToString (int val) {
2831 xmlChar *ret;
2832 if (val)
2833 ret = xmlStrdup((const xmlChar *) "true");
2834 else
2835 ret = xmlStrdup((const xmlChar *) "false");
2836 return(ret);
2837}
2838
2839/**
2840 * xmlXPathCastNumberToString:
2841 * @val: a number
2842 *
2843 * Converts a number to its string value.
2844 *
2845 * Returns a newly allocated string.
2846 */
2847xmlChar *
2848xmlXPathCastNumberToString (double val) {
2849 xmlChar *ret;
Daniel Veillardcda96922001-08-21 10:56:31 +00002850 switch (xmlXPathIsInf(val)) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002851 case 1:
2852 ret = xmlStrdup((const xmlChar *) "+Infinity");
2853 break;
2854 case -1:
2855 ret = xmlStrdup((const xmlChar *) "-Infinity");
2856 break;
2857 default:
Daniel Veillardcda96922001-08-21 10:56:31 +00002858 if (xmlXPathIsNaN(val)) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002859 ret = xmlStrdup((const xmlChar *) "NaN");
2860 } else {
2861 /* could be improved */
2862 char buf[100];
2863 xmlXPathFormatNumber(val, buf, 100);
2864 ret = xmlStrdup((const xmlChar *) buf);
2865 }
2866 }
2867 return(ret);
2868}
2869
2870/**
2871 * xmlXPathCastNodeToString:
2872 * @node: a node
2873 *
2874 * Converts a node to its string value.
2875 *
2876 * Returns a newly allocated string.
2877 */
2878xmlChar *
2879xmlXPathCastNodeToString (xmlNodePtr node) {
2880 return(xmlNodeGetContent(node));
2881}
2882
2883/**
2884 * xmlXPathCastNodeSetToString:
2885 * @ns: a node-set
2886 *
2887 * Converts a node-set to its string value.
2888 *
2889 * Returns a newly allocated string.
2890 */
2891xmlChar *
2892xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
2893 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
2894 return(xmlStrdup((const xmlChar *) ""));
2895
2896 xmlXPathNodeSetSort(ns);
2897 return(xmlXPathCastNodeToString(ns->nodeTab[0]));
2898}
2899
2900/**
2901 * xmlXPathCastToString:
2902 * @val: an XPath object
2903 *
2904 * Converts an existing object to its string() equivalent
2905 *
2906 * Returns the string value of the object, NULL in case of error.
2907 * A new string is allocated only if needed (val isn't a
2908 * string object).
2909 */
2910xmlChar *
2911xmlXPathCastToString(xmlXPathObjectPtr val) {
2912 xmlChar *ret = NULL;
2913
2914 if (val == NULL)
2915 return(xmlStrdup((const xmlChar *) ""));
2916 switch (val->type) {
2917 case XPATH_UNDEFINED:
2918#ifdef DEBUG_EXPR
2919 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
2920#endif
2921 ret = xmlStrdup((const xmlChar *) "");
2922 break;
2923 case XPATH_XSLT_TREE:
2924 case XPATH_NODESET:
2925 ret = xmlXPathCastNodeSetToString(val->nodesetval);
2926 break;
2927 case XPATH_STRING:
2928 return(val->stringval);
2929 case XPATH_BOOLEAN:
2930 ret = xmlXPathCastBooleanToString(val->boolval);
2931 break;
2932 case XPATH_NUMBER: {
2933 ret = xmlXPathCastNumberToString(val->floatval);
2934 break;
2935 }
2936 case XPATH_USERS:
2937 case XPATH_POINT:
2938 case XPATH_RANGE:
2939 case XPATH_LOCATIONSET:
2940 TODO
2941 ret = xmlStrdup((const xmlChar *) "");
2942 break;
2943 }
2944 return(ret);
2945}
2946
2947/**
2948 * xmlXPathConvertString:
2949 * @val: an XPath object
2950 *
2951 * Converts an existing object to its string() equivalent
2952 *
2953 * Returns the new object, the old one is freed (or the operation
2954 * is done directly on @val)
2955 */
2956xmlXPathObjectPtr
2957xmlXPathConvertString(xmlXPathObjectPtr val) {
2958 xmlChar *res = NULL;
2959
2960 if (val == NULL)
2961 return(xmlXPathNewCString(""));
2962
2963 switch (val->type) {
2964 case XPATH_UNDEFINED:
2965#ifdef DEBUG_EXPR
2966 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
2967#endif
2968 break;
2969 case XPATH_XSLT_TREE:
2970 case XPATH_NODESET:
2971 res = xmlXPathCastNodeSetToString(val->nodesetval);
2972 break;
2973 case XPATH_STRING:
2974 return(val);
2975 case XPATH_BOOLEAN:
2976 res = xmlXPathCastBooleanToString(val->boolval);
2977 break;
2978 case XPATH_NUMBER:
2979 res = xmlXPathCastNumberToString(val->floatval);
2980 break;
2981 case XPATH_USERS:
2982 case XPATH_POINT:
2983 case XPATH_RANGE:
2984 case XPATH_LOCATIONSET:
2985 TODO;
2986 break;
2987 }
2988 xmlXPathFreeObject(val);
2989 if (res == NULL)
2990 return(xmlXPathNewCString(""));
2991 return(xmlXPathWrapString(res));
2992}
2993
2994/**
2995 * xmlXPathCastBooleanToNumber:
2996 * @val: a boolean
2997 *
2998 * Converts a boolean to its number value
2999 *
3000 * Returns the number value
3001 */
3002double
3003xmlXPathCastBooleanToNumber(int val) {
3004 if (val)
3005 return(1.0);
3006 return(0.0);
3007}
3008
3009/**
3010 * xmlXPathCastStringToNumber:
3011 * @val: a string
3012 *
3013 * Converts a string to its number value
3014 *
3015 * Returns the number value
3016 */
3017double
3018xmlXPathCastStringToNumber(const xmlChar * val) {
3019 return(xmlXPathStringEvalNumber(val));
3020}
3021
3022/**
3023 * xmlXPathCastNodeToNumber:
3024 * @node: a node
3025 *
3026 * Converts a node to its number value
3027 *
3028 * Returns the number value
3029 */
3030double
3031xmlXPathCastNodeToNumber (xmlNodePtr node) {
3032 xmlChar *strval;
3033 double ret;
3034
3035 if (node == NULL)
3036 return(xmlXPathNAN);
3037 strval = xmlXPathCastNodeToString(node);
3038 if (strval == NULL)
3039 return(xmlXPathNAN);
3040 ret = xmlXPathCastStringToNumber(strval);
3041 xmlFree(strval);
3042
3043 return(ret);
3044}
3045
3046/**
3047 * xmlXPathCastNodeSetToNumber:
3048 * @ns: a node-set
3049 *
3050 * Converts a node-set to its number value
3051 *
3052 * Returns the number value
3053 */
3054double
3055xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
3056 xmlChar *str;
3057 double ret;
3058
3059 if (ns == NULL)
3060 return(xmlXPathNAN);
3061 str = xmlXPathCastNodeSetToString(ns);
3062 ret = xmlXPathCastStringToNumber(str);
3063 xmlFree(str);
3064 return(ret);
3065}
3066
3067/**
3068 * xmlXPathCastToNumber:
3069 * @val: an XPath object
3070 *
3071 * Converts an XPath object to its number value
3072 *
3073 * Returns the number value
3074 */
3075double
3076xmlXPathCastToNumber(xmlXPathObjectPtr val) {
3077 double ret = 0.0;
3078
3079 if (val == NULL)
3080 return(xmlXPathNAN);
3081 switch (val->type) {
3082 case XPATH_UNDEFINED:
3083#ifdef DEGUB_EXPR
3084 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
3085#endif
3086 ret = xmlXPathNAN;
3087 break;
3088 case XPATH_XSLT_TREE:
3089 case XPATH_NODESET:
3090 ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
3091 break;
3092 case XPATH_STRING:
3093 ret = xmlXPathCastStringToNumber(val->stringval);
3094 break;
3095 case XPATH_NUMBER:
3096 ret = val->floatval;
3097 break;
3098 case XPATH_BOOLEAN:
3099 ret = xmlXPathCastBooleanToNumber(val->boolval);
3100 break;
3101 case XPATH_USERS:
3102 case XPATH_POINT:
3103 case XPATH_RANGE:
3104 case XPATH_LOCATIONSET:
3105 TODO;
3106 ret = xmlXPathNAN;
3107 break;
3108 }
3109 return(ret);
3110}
3111
3112/**
3113 * xmlXPathConvertNumber:
3114 * @val: an XPath object
3115 *
3116 * Converts an existing object to its number() equivalent
3117 *
3118 * Returns the new object, the old one is freed (or the operation
3119 * is done directly on @val)
3120 */
3121xmlXPathObjectPtr
3122xmlXPathConvertNumber(xmlXPathObjectPtr val) {
3123 xmlXPathObjectPtr ret;
3124
3125 if (val == NULL)
3126 return(xmlXPathNewFloat(0.0));
3127 if (val->type == XPATH_NUMBER)
3128 return(val);
3129 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
3130 xmlXPathFreeObject(val);
3131 return(ret);
3132}
3133
3134/**
3135 * xmlXPathCastNumberToBoolean:
3136 * @val: a number
3137 *
3138 * Converts a number to its boolean value
3139 *
3140 * Returns the boolean value
3141 */
3142int
3143xmlXPathCastNumberToBoolean (double val) {
Daniel Veillardcda96922001-08-21 10:56:31 +00003144 if (xmlXPathIsNaN(val) || (val == 0.0))
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003145 return(0);
3146 return(1);
3147}
3148
3149/**
3150 * xmlXPathCastStringToBoolean:
3151 * @val: a string
3152 *
3153 * Converts a string to its boolean value
3154 *
3155 * Returns the boolean value
3156 */
3157int
3158xmlXPathCastStringToBoolean (const xmlChar *val) {
3159 if ((val == NULL) || (xmlStrlen(val) == 0))
3160 return(0);
3161 return(1);
3162}
3163
3164/**
3165 * xmlXPathCastNodeSetToBoolean:
3166 * @ns: a node-set
3167 *
3168 * Converts a node-set to its boolean value
3169 *
3170 * Returns the boolean value
3171 */
3172int
3173xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
3174 if ((ns == NULL) || (ns->nodeNr == 0))
3175 return(0);
3176 return(1);
3177}
3178
3179/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00003180 * xmlXPathCastToBoolean:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003181 * @val: an XPath object
3182 *
3183 * Converts an XPath object to its boolean value
3184 *
3185 * Returns the boolean value
3186 */
3187int
3188xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
3189 int ret = 0;
3190
3191 if (val == NULL)
3192 return(0);
3193 switch (val->type) {
3194 case XPATH_UNDEFINED:
3195#ifdef DEBUG_EXPR
3196 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
3197#endif
3198 ret = 0;
3199 break;
3200 case XPATH_XSLT_TREE:
3201 case XPATH_NODESET:
3202 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
3203 break;
3204 case XPATH_STRING:
3205 ret = xmlXPathCastStringToBoolean(val->stringval);
3206 break;
3207 case XPATH_NUMBER:
3208 ret = xmlXPathCastNumberToBoolean(val->floatval);
3209 break;
3210 case XPATH_BOOLEAN:
3211 ret = val->boolval;
3212 break;
3213 case XPATH_USERS:
3214 case XPATH_POINT:
3215 case XPATH_RANGE:
3216 case XPATH_LOCATIONSET:
3217 TODO;
3218 ret = 0;
3219 break;
3220 }
3221 return(ret);
3222}
3223
3224
3225/**
3226 * xmlXPathConvertBoolean:
3227 * @val: an XPath object
3228 *
3229 * Converts an existing object to its boolean() equivalent
3230 *
3231 * Returns the new object, the old one is freed (or the operation
3232 * is done directly on @val)
3233 */
3234xmlXPathObjectPtr
3235xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
3236 xmlXPathObjectPtr ret;
3237
3238 if (val == NULL)
3239 return(xmlXPathNewBoolean(0));
3240 if (val->type == XPATH_BOOLEAN)
3241 return(val);
3242 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
3243 xmlXPathFreeObject(val);
3244 return(ret);
3245}
3246
Owen Taylor3473f882001-02-23 17:55:21 +00003247/************************************************************************
3248 * *
3249 * Routines to handle XPath contexts *
3250 * *
3251 ************************************************************************/
3252
3253/**
3254 * xmlXPathNewContext:
3255 * @doc: the XML document
3256 *
3257 * Create a new xmlXPathContext
3258 *
3259 * Returns the xmlXPathContext just allocated.
3260 */
3261xmlXPathContextPtr
3262xmlXPathNewContext(xmlDocPtr doc) {
3263 xmlXPathContextPtr ret;
3264
3265 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
3266 if (ret == NULL) {
3267 xmlGenericError(xmlGenericErrorContext,
3268 "xmlXPathNewContext: out of memory\n");
3269 return(NULL);
3270 }
3271 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
3272 ret->doc = doc;
3273 ret->node = NULL;
3274
3275 ret->varHash = NULL;
3276
3277 ret->nb_types = 0;
3278 ret->max_types = 0;
3279 ret->types = NULL;
3280
3281 ret->funcHash = xmlHashCreate(0);
3282
3283 ret->nb_axis = 0;
3284 ret->max_axis = 0;
3285 ret->axis = NULL;
3286
3287 ret->nsHash = NULL;
3288 ret->user = NULL;
3289
3290 ret->contextSize = -1;
3291 ret->proximityPosition = -1;
3292
3293 xmlXPathRegisterAllFunctions(ret);
3294
3295 return(ret);
3296}
3297
3298/**
3299 * xmlXPathFreeContext:
3300 * @ctxt: the context to free
3301 *
3302 * Free up an xmlXPathContext
3303 */
3304void
3305xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
3306 xmlXPathRegisteredNsCleanup(ctxt);
3307 xmlXPathRegisteredFuncsCleanup(ctxt);
3308 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00003309 xmlFree(ctxt);
3310}
3311
3312/************************************************************************
3313 * *
3314 * Routines to handle XPath parser contexts *
3315 * *
3316 ************************************************************************/
3317
3318#define CHECK_CTXT(ctxt) \
3319 if (ctxt == NULL) { \
3320 xmlGenericError(xmlGenericErrorContext, \
3321 "%s:%d Internal error: ctxt == NULL\n", \
3322 __FILE__, __LINE__); \
3323 } \
3324
3325
3326#define CHECK_CONTEXT(ctxt) \
3327 if (ctxt == NULL) { \
3328 xmlGenericError(xmlGenericErrorContext, \
3329 "%s:%d Internal error: no context\n", \
3330 __FILE__, __LINE__); \
3331 } \
3332 else if (ctxt->doc == NULL) { \
3333 xmlGenericError(xmlGenericErrorContext, \
3334 "%s:%d Internal error: no document\n", \
3335 __FILE__, __LINE__); \
3336 } \
3337 else if (ctxt->doc->children == NULL) { \
3338 xmlGenericError(xmlGenericErrorContext, \
3339 "%s:%d Internal error: document without root\n", \
3340 __FILE__, __LINE__); \
3341 } \
3342
3343
3344/**
3345 * xmlXPathNewParserContext:
3346 * @str: the XPath expression
3347 * @ctxt: the XPath context
3348 *
3349 * Create a new xmlXPathParserContext
3350 *
3351 * Returns the xmlXPathParserContext just allocated.
3352 */
3353xmlXPathParserContextPtr
3354xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
3355 xmlXPathParserContextPtr ret;
3356
3357 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3358 if (ret == NULL) {
3359 xmlGenericError(xmlGenericErrorContext,
3360 "xmlXPathNewParserContext: out of memory\n");
3361 return(NULL);
3362 }
3363 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3364 ret->cur = ret->base = str;
3365 ret->context = ctxt;
3366
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003367 ret->comp = xmlXPathNewCompExpr();
3368 if (ret->comp == NULL) {
3369 xmlFree(ret->valueTab);
3370 xmlFree(ret);
3371 return(NULL);
3372 }
3373
3374 return(ret);
3375}
3376
3377/**
3378 * xmlXPathCompParserContext:
3379 * @comp: the XPath compiled expression
3380 * @ctxt: the XPath context
3381 *
3382 * Create a new xmlXPathParserContext when processing a compiled expression
3383 *
3384 * Returns the xmlXPathParserContext just allocated.
3385 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003386static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003387xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
3388 xmlXPathParserContextPtr ret;
3389
3390 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3391 if (ret == NULL) {
3392 xmlGenericError(xmlGenericErrorContext,
3393 "xmlXPathNewParserContext: out of memory\n");
3394 return(NULL);
3395 }
3396 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3397
Owen Taylor3473f882001-02-23 17:55:21 +00003398 /* Allocate the value stack */
3399 ret->valueTab = (xmlXPathObjectPtr *)
3400 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003401 if (ret->valueTab == NULL) {
3402 xmlFree(ret);
3403 xmlGenericError(xmlGenericErrorContext,
3404 "xmlXPathNewParserContext: out of memory\n");
3405 return(NULL);
3406 }
Owen Taylor3473f882001-02-23 17:55:21 +00003407 ret->valueNr = 0;
3408 ret->valueMax = 10;
3409 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003410
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00003411 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003412 ret->comp = comp;
3413
Owen Taylor3473f882001-02-23 17:55:21 +00003414 return(ret);
3415}
3416
3417/**
3418 * xmlXPathFreeParserContext:
3419 * @ctxt: the context to free
3420 *
3421 * Free up an xmlXPathParserContext
3422 */
3423void
3424xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
3425 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00003426 xmlFree(ctxt->valueTab);
3427 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003428 if (ctxt->comp)
3429 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00003430 xmlFree(ctxt);
3431}
3432
3433/************************************************************************
3434 * *
3435 * The implicit core function library *
3436 * *
3437 ************************************************************************/
3438
Owen Taylor3473f882001-02-23 17:55:21 +00003439/**
Daniel Veillardf06307e2001-07-03 10:35:50 +00003440 * xmlXPathNodeStringHash:
3441 * @node: a node pointer
3442 *
3443 * Function computing the beginning of the string value of the node,
3444 * used to speed up comparisons
3445 *
3446 * Returns an int usable as a hash
3447 */
3448static unsigned int
3449xmlXPathNodeValHash(xmlNodePtr node) {
3450 int len = 2;
3451 const xmlChar * string = NULL;
3452 xmlNodePtr tmp = NULL;
3453 unsigned int ret = 0;
3454
3455 if (node == NULL)
3456 return(0);
3457
3458
3459 switch (node->type) {
3460 case XML_COMMENT_NODE:
3461 case XML_PI_NODE:
3462 case XML_CDATA_SECTION_NODE:
3463 case XML_TEXT_NODE:
3464 string = node->content;
3465 if (string == NULL)
3466 return(0);
3467 if (string[0] == 0)
3468 return(0);
3469 return(((unsigned int) string[0]) +
3470 (((unsigned int) string[1]) << 8));
3471 case XML_NAMESPACE_DECL:
3472 string = ((xmlNsPtr)node)->href;
3473 if (string == NULL)
3474 return(0);
3475 if (string[0] == 0)
3476 return(0);
3477 return(((unsigned int) string[0]) +
3478 (((unsigned int) string[1]) << 8));
3479 case XML_ATTRIBUTE_NODE:
3480 tmp = ((xmlAttrPtr) node)->children;
3481 break;
3482 case XML_ELEMENT_NODE:
3483 tmp = node->children;
3484 break;
3485 default:
3486 return(0);
3487 }
3488 while (tmp != NULL) {
3489 switch (tmp->type) {
3490 case XML_COMMENT_NODE:
3491 case XML_PI_NODE:
3492 case XML_CDATA_SECTION_NODE:
3493 case XML_TEXT_NODE:
3494 string = tmp->content;
3495 break;
3496 case XML_NAMESPACE_DECL:
3497 string = ((xmlNsPtr)tmp)->href;
3498 break;
3499 default:
3500 break;
3501 }
3502 if ((string != NULL) && (string[0] != 0)) {
3503 if (string[0] == 0)
3504 return(0);
3505 if (len == 1) {
3506 return(ret + (((unsigned int) string[0]) << 8));
3507 }
3508 if (string[1] == 0) {
3509 len = 1;
3510 ret = (unsigned int) string[0];
3511 } else {
3512 return(((unsigned int) string[0]) +
3513 (((unsigned int) string[1]) << 8));
3514 }
3515 }
3516 /*
3517 * Skip to next node
3518 */
3519 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
3520 if (tmp->children->type != XML_ENTITY_DECL) {
3521 tmp = tmp->children;
3522 continue;
3523 }
3524 }
3525 if (tmp == node)
3526 break;
3527
3528 if (tmp->next != NULL) {
3529 tmp = tmp->next;
3530 continue;
3531 }
3532
3533 do {
3534 tmp = tmp->parent;
3535 if (tmp == NULL)
3536 break;
3537 if (tmp == node) {
3538 tmp = NULL;
3539 break;
3540 }
3541 if (tmp->next != NULL) {
3542 tmp = tmp->next;
3543 break;
3544 }
3545 } while (tmp != NULL);
3546 }
3547 return(ret);
3548}
3549
3550/**
3551 * xmlXPathStringHash:
3552 * @string: a string
3553 *
3554 * Function computing the beginning of the string value of the node,
3555 * used to speed up comparisons
3556 *
3557 * Returns an int usable as a hash
3558 */
3559static unsigned int
3560xmlXPathStringHash(const xmlChar * string) {
3561 if (string == NULL)
3562 return((unsigned int) 0);
3563 if (string[0] == 0)
3564 return(0);
3565 return(((unsigned int) string[0]) +
3566 (((unsigned int) string[1]) << 8));
3567}
3568
3569/**
Owen Taylor3473f882001-02-23 17:55:21 +00003570 * xmlXPathCompareNodeSetFloat:
3571 * @ctxt: the XPath Parser context
3572 * @inf: less than (1) or greater than (0)
3573 * @strict: is the comparison strict
3574 * @arg: the node set
3575 * @f: the value
3576 *
3577 * Implement the compare operation between a nodeset and a number
3578 * @ns < @val (1, 1, ...
3579 * @ns <= @val (1, 0, ...
3580 * @ns > @val (0, 1, ...
3581 * @ns >= @val (0, 0, ...
3582 *
3583 * If one object to be compared is a node-set and the other is a number,
3584 * then the comparison will be true if and only if there is a node in the
3585 * node-set such that the result of performing the comparison on the number
3586 * to be compared and on the result of converting the string-value of that
3587 * node to a number using the number function is true.
3588 *
3589 * Returns 0 or 1 depending on the results of the test.
3590 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003591static int
Owen Taylor3473f882001-02-23 17:55:21 +00003592xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
3593 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
3594 int i, ret = 0;
3595 xmlNodeSetPtr ns;
3596 xmlChar *str2;
3597
3598 if ((f == NULL) || (arg == NULL) ||
3599 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3600 xmlXPathFreeObject(arg);
3601 xmlXPathFreeObject(f);
3602 return(0);
3603 }
3604 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003605 if (ns != NULL) {
3606 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003607 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003608 if (str2 != NULL) {
3609 valuePush(ctxt,
3610 xmlXPathNewString(str2));
3611 xmlFree(str2);
3612 xmlXPathNumberFunction(ctxt, 1);
3613 valuePush(ctxt, xmlXPathObjectCopy(f));
3614 ret = xmlXPathCompareValues(ctxt, inf, strict);
3615 if (ret)
3616 break;
3617 }
3618 }
Owen Taylor3473f882001-02-23 17:55:21 +00003619 }
3620 xmlXPathFreeObject(arg);
3621 xmlXPathFreeObject(f);
3622 return(ret);
3623}
3624
3625/**
3626 * xmlXPathCompareNodeSetString:
3627 * @ctxt: the XPath Parser context
3628 * @inf: less than (1) or greater than (0)
3629 * @strict: is the comparison strict
3630 * @arg: the node set
3631 * @s: the value
3632 *
3633 * Implement the compare operation between a nodeset and a string
3634 * @ns < @val (1, 1, ...
3635 * @ns <= @val (1, 0, ...
3636 * @ns > @val (0, 1, ...
3637 * @ns >= @val (0, 0, ...
3638 *
3639 * If one object to be compared is a node-set and the other is a string,
3640 * then the comparison will be true if and only if there is a node in
3641 * the node-set such that the result of performing the comparison on the
3642 * string-value of the node and the other string is true.
3643 *
3644 * Returns 0 or 1 depending on the results of the test.
3645 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003646static int
Owen Taylor3473f882001-02-23 17:55:21 +00003647xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
3648 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
3649 int i, ret = 0;
3650 xmlNodeSetPtr ns;
3651 xmlChar *str2;
3652
3653 if ((s == NULL) || (arg == NULL) ||
3654 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3655 xmlXPathFreeObject(arg);
3656 xmlXPathFreeObject(s);
3657 return(0);
3658 }
3659 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003660 if (ns != NULL) {
3661 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003662 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003663 if (str2 != NULL) {
3664 valuePush(ctxt,
3665 xmlXPathNewString(str2));
3666 xmlFree(str2);
3667 valuePush(ctxt, xmlXPathObjectCopy(s));
3668 ret = xmlXPathCompareValues(ctxt, inf, strict);
3669 if (ret)
3670 break;
3671 }
3672 }
Owen Taylor3473f882001-02-23 17:55:21 +00003673 }
3674 xmlXPathFreeObject(arg);
3675 xmlXPathFreeObject(s);
3676 return(ret);
3677}
3678
3679/**
3680 * xmlXPathCompareNodeSets:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003681 * @inf: less than (1) or greater than (0)
Owen Taylor3473f882001-02-23 17:55:21 +00003682 * @strict: is the comparison strict
3683 * @arg1: the fist node set object
3684 * @arg2: the second node set object
3685 *
3686 * Implement the compare operation on nodesets:
3687 *
3688 * If both objects to be compared are node-sets, then the comparison
3689 * will be true if and only if there is a node in the first node-set
3690 * and a node in the second node-set such that the result of performing
3691 * the comparison on the string-values of the two nodes is true.
3692 * ....
3693 * When neither object to be compared is a node-set and the operator
3694 * is <=, <, >= or >, then the objects are compared by converting both
3695 * objects to numbers and comparing the numbers according to IEEE 754.
3696 * ....
3697 * The number function converts its argument to a number as follows:
3698 * - a string that consists of optional whitespace followed by an
3699 * optional minus sign followed by a Number followed by whitespace
3700 * is converted to the IEEE 754 number that is nearest (according
3701 * to the IEEE 754 round-to-nearest rule) to the mathematical value
3702 * represented by the string; any other string is converted to NaN
3703 *
3704 * Conclusion all nodes need to be converted first to their string value
3705 * and then the comparison must be done when possible
3706 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003707static int
3708xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00003709 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3710 int i, j, init = 0;
3711 double val1;
3712 double *values2;
3713 int ret = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00003714 xmlNodeSetPtr ns1;
3715 xmlNodeSetPtr ns2;
3716
3717 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003718 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
3719 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003720 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003721 }
Owen Taylor3473f882001-02-23 17:55:21 +00003722 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003723 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
3724 xmlXPathFreeObject(arg1);
3725 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003726 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003727 }
Owen Taylor3473f882001-02-23 17:55:21 +00003728
3729 ns1 = arg1->nodesetval;
3730 ns2 = arg2->nodesetval;
3731
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003732 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003733 xmlXPathFreeObject(arg1);
3734 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003735 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003736 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003737 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003738 xmlXPathFreeObject(arg1);
3739 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003740 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003741 }
Owen Taylor3473f882001-02-23 17:55:21 +00003742
3743 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
3744 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003745 xmlXPathFreeObject(arg1);
3746 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003747 return(0);
3748 }
3749 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003750 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
Daniel Veillardcda96922001-08-21 10:56:31 +00003751 if (xmlXPathIsNaN(val1))
Owen Taylor3473f882001-02-23 17:55:21 +00003752 continue;
3753 for (j = 0;j < ns2->nodeNr;j++) {
3754 if (init == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003755 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
Owen Taylor3473f882001-02-23 17:55:21 +00003756 }
Daniel Veillardcda96922001-08-21 10:56:31 +00003757 if (xmlXPathIsNaN(values2[j]))
Owen Taylor3473f882001-02-23 17:55:21 +00003758 continue;
3759 if (inf && strict)
3760 ret = (val1 < values2[j]);
3761 else if (inf && !strict)
3762 ret = (val1 <= values2[j]);
3763 else if (!inf && strict)
3764 ret = (val1 > values2[j]);
3765 else if (!inf && !strict)
3766 ret = (val1 >= values2[j]);
3767 if (ret)
3768 break;
3769 }
3770 if (ret)
3771 break;
3772 init = 1;
3773 }
3774 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003775 xmlXPathFreeObject(arg1);
3776 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003777 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00003778}
3779
3780/**
3781 * xmlXPathCompareNodeSetValue:
3782 * @ctxt: the XPath Parser context
3783 * @inf: less than (1) or greater than (0)
3784 * @strict: is the comparison strict
3785 * @arg: the node set
3786 * @val: the value
3787 *
3788 * Implement the compare operation between a nodeset and a value
3789 * @ns < @val (1, 1, ...
3790 * @ns <= @val (1, 0, ...
3791 * @ns > @val (0, 1, ...
3792 * @ns >= @val (0, 0, ...
3793 *
3794 * If one object to be compared is a node-set and the other is a boolean,
3795 * then the comparison will be true if and only if the result of performing
3796 * the comparison on the boolean and on the result of converting
3797 * the node-set to a boolean using the boolean function is true.
3798 *
3799 * Returns 0 or 1 depending on the results of the test.
3800 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003801static int
Owen Taylor3473f882001-02-23 17:55:21 +00003802xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
3803 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
3804 if ((val == NULL) || (arg == NULL) ||
3805 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3806 return(0);
3807
3808 switch(val->type) {
3809 case XPATH_NUMBER:
3810 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
3811 case XPATH_NODESET:
3812 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003813 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00003814 case XPATH_STRING:
3815 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
3816 case XPATH_BOOLEAN:
3817 valuePush(ctxt, arg);
3818 xmlXPathBooleanFunction(ctxt, 1);
3819 valuePush(ctxt, val);
3820 return(xmlXPathCompareValues(ctxt, inf, strict));
3821 default:
3822 TODO
3823 return(0);
3824 }
3825 return(0);
3826}
3827
3828/**
3829 * xmlXPathEqualNodeSetString
3830 * @arg: the nodeset object argument
3831 * @str: the string to compare to.
3832 *
3833 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3834 * If one object to be compared is a node-set and the other is a string,
3835 * then the comparison will be true if and only if there is a node in
3836 * the node-set such that the result of performing the comparison on the
3837 * string-value of the node and the other string is true.
3838 *
3839 * Returns 0 or 1 depending on the results of the test.
3840 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003841static int
Daniel Veillardf06307e2001-07-03 10:35:50 +00003842xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str)
3843{
Owen Taylor3473f882001-02-23 17:55:21 +00003844 int i;
3845 xmlNodeSetPtr ns;
3846 xmlChar *str2;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003847 unsigned int hash;
Owen Taylor3473f882001-02-23 17:55:21 +00003848
3849 if ((str == NULL) || (arg == NULL) ||
Daniel Veillardf06307e2001-07-03 10:35:50 +00003850 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3851 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003852 ns = arg->nodesetval;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003853 hash = xmlXPathStringHash(str);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003854 if (ns == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00003855 return (0);
Daniel Veillard73c9c042001-07-05 20:02:54 +00003856 if (ns->nodeNr <= 0) {
3857 if (hash == 0)
3858 return(1);
3859 return(0);
3860 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003861 for (i = 0; i < ns->nodeNr; i++) {
3862 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
3863 str2 = xmlNodeGetContent(ns->nodeTab[i]);
3864 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
3865 xmlFree(str2);
3866 return (1);
3867 }
3868 if (str2 != NULL)
3869 xmlFree(str2);
3870 }
Owen Taylor3473f882001-02-23 17:55:21 +00003871 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003872 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003873}
3874
3875/**
3876 * xmlXPathEqualNodeSetFloat
3877 * @arg: the nodeset object argument
3878 * @f: the float to compare to
3879 *
3880 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3881 * If one object to be compared is a node-set and the other is a number,
3882 * then the comparison will be true if and only if there is a node in
3883 * the node-set such that the result of performing the comparison on the
3884 * number to be compared and on the result of converting the string-value
3885 * of that node to a number using the number function is true.
3886 *
3887 * Returns 0 or 1 depending on the results of the test.
3888 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003889static int
Owen Taylor3473f882001-02-23 17:55:21 +00003890xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
3891 char buf[100] = "";
3892
3893 if ((arg == NULL) ||
3894 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3895 return(0);
3896
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003897 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00003898 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
3899}
3900
3901
3902/**
3903 * xmlXPathEqualNodeSets
3904 * @arg1: first nodeset object argument
3905 * @arg2: second nodeset object argument
3906 *
3907 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
3908 * If both objects to be compared are node-sets, then the comparison
3909 * will be true if and only if there is a node in the first node-set and
3910 * a node in the second node-set such that the result of performing the
3911 * comparison on the string-values of the two nodes is true.
3912 *
3913 * (needless to say, this is a costly operation)
3914 *
3915 * Returns 0 or 1 depending on the results of the test.
3916 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003917static int
Owen Taylor3473f882001-02-23 17:55:21 +00003918xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3919 int i, j;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003920 unsigned int *hashs1;
3921 unsigned int *hashs2;
Owen Taylor3473f882001-02-23 17:55:21 +00003922 xmlChar **values1;
3923 xmlChar **values2;
3924 int ret = 0;
3925 xmlNodeSetPtr ns1;
3926 xmlNodeSetPtr ns2;
3927
3928 if ((arg1 == NULL) ||
3929 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
3930 return(0);
3931 if ((arg2 == NULL) ||
3932 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
3933 return(0);
3934
3935 ns1 = arg1->nodesetval;
3936 ns2 = arg2->nodesetval;
3937
Daniel Veillard911f49a2001-04-07 15:39:35 +00003938 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00003939 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003940 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00003941 return(0);
3942
3943 /*
3944 * check if there is a node pertaining to both sets
3945 */
3946 for (i = 0;i < ns1->nodeNr;i++)
3947 for (j = 0;j < ns2->nodeNr;j++)
3948 if (ns1->nodeTab[i] == ns2->nodeTab[j])
3949 return(1);
3950
3951 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
3952 if (values1 == NULL)
3953 return(0);
Daniel Veillardf06307e2001-07-03 10:35:50 +00003954 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
3955 if (hashs1 == NULL) {
3956 xmlFree(values1);
3957 return(0);
3958 }
Owen Taylor3473f882001-02-23 17:55:21 +00003959 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
3960 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
3961 if (values2 == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00003962 xmlFree(hashs1);
Owen Taylor3473f882001-02-23 17:55:21 +00003963 xmlFree(values1);
3964 return(0);
3965 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003966 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
3967 if (hashs2 == NULL) {
3968 xmlFree(hashs1);
3969 xmlFree(values1);
3970 xmlFree(values2);
3971 return(0);
3972 }
Owen Taylor3473f882001-02-23 17:55:21 +00003973 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
3974 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00003975 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00003976 for (j = 0;j < ns2->nodeNr;j++) {
3977 if (i == 0)
Daniel Veillardf06307e2001-07-03 10:35:50 +00003978 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
3979 if (hashs1[i] == hashs2[j]) {
3980 if (values1[i] == NULL)
3981 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
3982 if (values2[j] == NULL)
3983 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
3984 ret = xmlStrEqual(values1[i], values2[j]);
3985 if (ret)
3986 break;
3987 }
Owen Taylor3473f882001-02-23 17:55:21 +00003988 }
3989 if (ret)
3990 break;
3991 }
3992 for (i = 0;i < ns1->nodeNr;i++)
3993 if (values1[i] != NULL)
3994 xmlFree(values1[i]);
3995 for (j = 0;j < ns2->nodeNr;j++)
3996 if (values2[j] != NULL)
3997 xmlFree(values2[j]);
3998 xmlFree(values1);
3999 xmlFree(values2);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004000 xmlFree(hashs1);
4001 xmlFree(hashs2);
Owen Taylor3473f882001-02-23 17:55:21 +00004002 return(ret);
4003}
4004
4005/**
4006 * xmlXPathEqualValues:
4007 * @ctxt: the XPath Parser context
4008 *
4009 * Implement the equal operation on XPath objects content: @arg1 == @arg2
4010 *
4011 * Returns 0 or 1 depending on the results of the test.
4012 */
4013int
4014xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
4015 xmlXPathObjectPtr arg1, arg2;
4016 int ret = 0;
4017
4018 arg1 = valuePop(ctxt);
4019 if (arg1 == NULL)
4020 XP_ERROR0(XPATH_INVALID_OPERAND);
4021
4022 arg2 = valuePop(ctxt);
4023 if (arg2 == NULL) {
4024 xmlXPathFreeObject(arg1);
4025 XP_ERROR0(XPATH_INVALID_OPERAND);
4026 }
4027
4028 if (arg1 == arg2) {
4029#ifdef DEBUG_EXPR
4030 xmlGenericError(xmlGenericErrorContext,
4031 "Equal: by pointer\n");
4032#endif
4033 return(1);
4034 }
4035
4036 switch (arg1->type) {
4037 case XPATH_UNDEFINED:
4038#ifdef DEBUG_EXPR
4039 xmlGenericError(xmlGenericErrorContext,
4040 "Equal: undefined\n");
4041#endif
4042 break;
4043 case XPATH_XSLT_TREE:
4044 case XPATH_NODESET:
4045 switch (arg2->type) {
4046 case XPATH_UNDEFINED:
4047#ifdef DEBUG_EXPR
4048 xmlGenericError(xmlGenericErrorContext,
4049 "Equal: undefined\n");
4050#endif
4051 break;
4052 case XPATH_XSLT_TREE:
4053 case XPATH_NODESET:
4054 ret = xmlXPathEqualNodeSets(arg1, arg2);
4055 break;
4056 case XPATH_BOOLEAN:
4057 if ((arg1->nodesetval == NULL) ||
4058 (arg1->nodesetval->nodeNr == 0)) ret = 0;
4059 else
4060 ret = 1;
4061 ret = (ret == arg2->boolval);
4062 break;
4063 case XPATH_NUMBER:
4064 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
4065 break;
4066 case XPATH_STRING:
4067 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
4068 break;
4069 case XPATH_USERS:
4070 case XPATH_POINT:
4071 case XPATH_RANGE:
4072 case XPATH_LOCATIONSET:
4073 TODO
4074 break;
4075 }
4076 break;
4077 case XPATH_BOOLEAN:
4078 switch (arg2->type) {
4079 case XPATH_UNDEFINED:
4080#ifdef DEBUG_EXPR
4081 xmlGenericError(xmlGenericErrorContext,
4082 "Equal: undefined\n");
4083#endif
4084 break;
4085 case XPATH_NODESET:
4086 case XPATH_XSLT_TREE:
4087 if ((arg2->nodesetval == NULL) ||
4088 (arg2->nodesetval->nodeNr == 0)) ret = 0;
4089 else
4090 ret = 1;
4091 break;
4092 case XPATH_BOOLEAN:
4093#ifdef DEBUG_EXPR
4094 xmlGenericError(xmlGenericErrorContext,
4095 "Equal: %d boolean %d \n",
4096 arg1->boolval, arg2->boolval);
4097#endif
4098 ret = (arg1->boolval == arg2->boolval);
4099 break;
4100 case XPATH_NUMBER:
4101 if (arg2->floatval) ret = 1;
4102 else ret = 0;
4103 ret = (arg1->boolval == ret);
4104 break;
4105 case XPATH_STRING:
4106 if ((arg2->stringval == NULL) ||
4107 (arg2->stringval[0] == 0)) ret = 0;
4108 else
4109 ret = 1;
4110 ret = (arg1->boolval == ret);
4111 break;
4112 case XPATH_USERS:
4113 case XPATH_POINT:
4114 case XPATH_RANGE:
4115 case XPATH_LOCATIONSET:
4116 TODO
4117 break;
4118 }
4119 break;
4120 case XPATH_NUMBER:
4121 switch (arg2->type) {
4122 case XPATH_UNDEFINED:
4123#ifdef DEBUG_EXPR
4124 xmlGenericError(xmlGenericErrorContext,
4125 "Equal: undefined\n");
4126#endif
4127 break;
4128 case XPATH_NODESET:
4129 case XPATH_XSLT_TREE:
4130 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
4131 break;
4132 case XPATH_BOOLEAN:
4133 if (arg1->floatval) ret = 1;
4134 else ret = 0;
4135 ret = (arg2->boolval == ret);
4136 break;
4137 case XPATH_STRING:
4138 valuePush(ctxt, arg2);
4139 xmlXPathNumberFunction(ctxt, 1);
4140 arg2 = valuePop(ctxt);
4141 /* no break on purpose */
4142 case XPATH_NUMBER:
4143 ret = (arg1->floatval == arg2->floatval);
4144 break;
4145 case XPATH_USERS:
4146 case XPATH_POINT:
4147 case XPATH_RANGE:
4148 case XPATH_LOCATIONSET:
4149 TODO
4150 break;
4151 }
4152 break;
4153 case XPATH_STRING:
4154 switch (arg2->type) {
4155 case XPATH_UNDEFINED:
4156#ifdef DEBUG_EXPR
4157 xmlGenericError(xmlGenericErrorContext,
4158 "Equal: undefined\n");
4159#endif
4160 break;
4161 case XPATH_NODESET:
4162 case XPATH_XSLT_TREE:
4163 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
4164 break;
4165 case XPATH_BOOLEAN:
4166 if ((arg1->stringval == NULL) ||
4167 (arg1->stringval[0] == 0)) ret = 0;
4168 else
4169 ret = 1;
4170 ret = (arg2->boolval == ret);
4171 break;
4172 case XPATH_STRING:
4173 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
4174 break;
4175 case XPATH_NUMBER:
4176 valuePush(ctxt, arg1);
4177 xmlXPathNumberFunction(ctxt, 1);
4178 arg1 = valuePop(ctxt);
4179 ret = (arg1->floatval == arg2->floatval);
4180 break;
4181 case XPATH_USERS:
4182 case XPATH_POINT:
4183 case XPATH_RANGE:
4184 case XPATH_LOCATIONSET:
4185 TODO
4186 break;
4187 }
4188 break;
4189 case XPATH_USERS:
4190 case XPATH_POINT:
4191 case XPATH_RANGE:
4192 case XPATH_LOCATIONSET:
4193 TODO
4194 break;
4195 }
4196 xmlXPathFreeObject(arg1);
4197 xmlXPathFreeObject(arg2);
4198 return(ret);
4199}
4200
4201
4202/**
4203 * xmlXPathCompareValues:
4204 * @ctxt: the XPath Parser context
4205 * @inf: less than (1) or greater than (0)
4206 * @strict: is the comparison strict
4207 *
4208 * Implement the compare operation on XPath objects:
4209 * @arg1 < @arg2 (1, 1, ...
4210 * @arg1 <= @arg2 (1, 0, ...
4211 * @arg1 > @arg2 (0, 1, ...
4212 * @arg1 >= @arg2 (0, 0, ...
4213 *
4214 * When neither object to be compared is a node-set and the operator is
4215 * <=, <, >=, >, then the objects are compared by converted both objects
4216 * to numbers and comparing the numbers according to IEEE 754. The <
4217 * comparison will be true if and only if the first number is less than the
4218 * second number. The <= comparison will be true if and only if the first
4219 * number is less than or equal to the second number. The > comparison
4220 * will be true if and only if the first number is greater than the second
4221 * number. The >= comparison will be true if and only if the first number
4222 * is greater than or equal to the second number.
4223 *
4224 * Returns 1 if the comparaison succeeded, 0 if it failed
4225 */
4226int
4227xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
4228 int ret = 0;
4229 xmlXPathObjectPtr arg1, arg2;
4230
4231 arg2 = valuePop(ctxt);
4232 if (arg2 == NULL) {
4233 XP_ERROR0(XPATH_INVALID_OPERAND);
4234 }
4235
4236 arg1 = valuePop(ctxt);
4237 if (arg1 == NULL) {
4238 xmlXPathFreeObject(arg2);
4239 XP_ERROR0(XPATH_INVALID_OPERAND);
4240 }
4241
4242 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
4243 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004244 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004245 } else {
4246 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004247 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
4248 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004249 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004250 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
4251 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00004252 }
4253 }
4254 return(ret);
4255 }
4256
4257 if (arg1->type != XPATH_NUMBER) {
4258 valuePush(ctxt, arg1);
4259 xmlXPathNumberFunction(ctxt, 1);
4260 arg1 = valuePop(ctxt);
4261 }
4262 if (arg1->type != XPATH_NUMBER) {
4263 xmlXPathFreeObject(arg1);
4264 xmlXPathFreeObject(arg2);
4265 XP_ERROR0(XPATH_INVALID_OPERAND);
4266 }
4267 if (arg2->type != XPATH_NUMBER) {
4268 valuePush(ctxt, arg2);
4269 xmlXPathNumberFunction(ctxt, 1);
4270 arg2 = valuePop(ctxt);
4271 }
4272 if (arg2->type != XPATH_NUMBER) {
4273 xmlXPathFreeObject(arg1);
4274 xmlXPathFreeObject(arg2);
4275 XP_ERROR0(XPATH_INVALID_OPERAND);
4276 }
4277 /*
4278 * Add tests for infinity and nan
4279 * => feedback on 3.4 for Inf and NaN
4280 */
4281 if (inf && strict)
4282 ret = (arg1->floatval < arg2->floatval);
4283 else if (inf && !strict)
4284 ret = (arg1->floatval <= arg2->floatval);
4285 else if (!inf && strict)
4286 ret = (arg1->floatval > arg2->floatval);
4287 else if (!inf && !strict)
4288 ret = (arg1->floatval >= arg2->floatval);
4289 xmlXPathFreeObject(arg1);
4290 xmlXPathFreeObject(arg2);
4291 return(ret);
4292}
4293
4294/**
4295 * xmlXPathValueFlipSign:
4296 * @ctxt: the XPath Parser context
4297 *
4298 * Implement the unary - operation on an XPath object
4299 * The numeric operators convert their operands to numbers as if
4300 * by calling the number function.
4301 */
4302void
4303xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004304 CAST_TO_NUMBER;
4305 CHECK_TYPE(XPATH_NUMBER);
4306 ctxt->value->floatval = - ctxt->value->floatval;
Owen Taylor3473f882001-02-23 17:55:21 +00004307}
4308
4309/**
4310 * xmlXPathAddValues:
4311 * @ctxt: the XPath Parser context
4312 *
4313 * Implement the add operation on XPath objects:
4314 * The numeric operators convert their operands to numbers as if
4315 * by calling the number function.
4316 */
4317void
4318xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
4319 xmlXPathObjectPtr arg;
4320 double val;
4321
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004322 arg = valuePop(ctxt);
4323 if (arg == NULL)
4324 XP_ERROR(XPATH_INVALID_OPERAND);
4325 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004326 xmlXPathFreeObject(arg);
4327
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004328 CAST_TO_NUMBER;
4329 CHECK_TYPE(XPATH_NUMBER);
4330 ctxt->value->floatval += val;
Owen Taylor3473f882001-02-23 17:55:21 +00004331}
4332
4333/**
4334 * xmlXPathSubValues:
4335 * @ctxt: the XPath Parser context
4336 *
4337 * Implement the substraction operation on XPath objects:
4338 * The numeric operators convert their operands to numbers as if
4339 * by calling the number function.
4340 */
4341void
4342xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
4343 xmlXPathObjectPtr arg;
4344 double val;
4345
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004346 arg = valuePop(ctxt);
4347 if (arg == NULL)
4348 XP_ERROR(XPATH_INVALID_OPERAND);
4349 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004350 xmlXPathFreeObject(arg);
4351
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004352 CAST_TO_NUMBER;
4353 CHECK_TYPE(XPATH_NUMBER);
4354 ctxt->value->floatval -= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004355}
4356
4357/**
4358 * xmlXPathMultValues:
4359 * @ctxt: the XPath Parser context
4360 *
4361 * Implement the multiply operation on XPath objects:
4362 * The numeric operators convert their operands to numbers as if
4363 * by calling the number function.
4364 */
4365void
4366xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
4367 xmlXPathObjectPtr arg;
4368 double val;
4369
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004370 arg = valuePop(ctxt);
4371 if (arg == NULL)
4372 XP_ERROR(XPATH_INVALID_OPERAND);
4373 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004374 xmlXPathFreeObject(arg);
4375
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004376 CAST_TO_NUMBER;
4377 CHECK_TYPE(XPATH_NUMBER);
4378 ctxt->value->floatval *= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004379}
4380
4381/**
4382 * xmlXPathDivValues:
4383 * @ctxt: the XPath Parser context
4384 *
4385 * Implement the div operation on XPath objects @arg1 / @arg2:
4386 * The numeric operators convert their operands to numbers as if
4387 * by calling the number function.
4388 */
4389void
4390xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
4391 xmlXPathObjectPtr arg;
4392 double val;
4393
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004394 arg = valuePop(ctxt);
4395 if (arg == NULL)
4396 XP_ERROR(XPATH_INVALID_OPERAND);
4397 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004398 xmlXPathFreeObject(arg);
4399
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004400 CAST_TO_NUMBER;
4401 CHECK_TYPE(XPATH_NUMBER);
4402 ctxt->value->floatval /= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004403}
4404
4405/**
4406 * xmlXPathModValues:
4407 * @ctxt: the XPath Parser context
4408 *
4409 * Implement the mod operation on XPath objects: @arg1 / @arg2
4410 * The numeric operators convert their operands to numbers as if
4411 * by calling the number function.
4412 */
4413void
4414xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
4415 xmlXPathObjectPtr arg;
4416 int arg1, arg2;
4417
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004418 arg = valuePop(ctxt);
4419 if (arg == NULL)
4420 XP_ERROR(XPATH_INVALID_OPERAND);
4421 arg2 = (int) xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004422 xmlXPathFreeObject(arg);
4423
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004424 CAST_TO_NUMBER;
4425 CHECK_TYPE(XPATH_NUMBER);
4426 arg1 = (int) ctxt->value->floatval;
Daniel Veillard268fd1b2001-08-26 18:46:36 +00004427 if (arg2 == 0)
4428 ctxt->value->floatval = xmlXPathNAN;
4429 else
4430 ctxt->value->floatval = arg1 % arg2;
Owen Taylor3473f882001-02-23 17:55:21 +00004431}
4432
4433/************************************************************************
4434 * *
4435 * The traversal functions *
4436 * *
4437 ************************************************************************/
4438
Owen Taylor3473f882001-02-23 17:55:21 +00004439/*
4440 * A traversal function enumerates nodes along an axis.
4441 * Initially it must be called with NULL, and it indicates
4442 * termination on the axis by returning NULL.
4443 */
4444typedef xmlNodePtr (*xmlXPathTraversalFunction)
4445 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
4446
4447/**
4448 * xmlXPathNextSelf:
4449 * @ctxt: the XPath Parser context
4450 * @cur: the current node in the traversal
4451 *
4452 * Traversal function for the "self" direction
4453 * The self axis contains just the context node itself
4454 *
4455 * Returns the next element following that axis
4456 */
4457xmlNodePtr
4458xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4459 if (cur == NULL)
4460 return(ctxt->context->node);
4461 return(NULL);
4462}
4463
4464/**
4465 * xmlXPathNextChild:
4466 * @ctxt: the XPath Parser context
4467 * @cur: the current node in the traversal
4468 *
4469 * Traversal function for the "child" direction
4470 * The child axis contains the children of the context node in document order.
4471 *
4472 * Returns the next element following that axis
4473 */
4474xmlNodePtr
4475xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4476 if (cur == NULL) {
4477 if (ctxt->context->node == NULL) return(NULL);
4478 switch (ctxt->context->node->type) {
4479 case XML_ELEMENT_NODE:
4480 case XML_TEXT_NODE:
4481 case XML_CDATA_SECTION_NODE:
4482 case XML_ENTITY_REF_NODE:
4483 case XML_ENTITY_NODE:
4484 case XML_PI_NODE:
4485 case XML_COMMENT_NODE:
4486 case XML_NOTATION_NODE:
4487 case XML_DTD_NODE:
4488 return(ctxt->context->node->children);
4489 case XML_DOCUMENT_NODE:
4490 case XML_DOCUMENT_TYPE_NODE:
4491 case XML_DOCUMENT_FRAG_NODE:
4492 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004493#ifdef LIBXML_DOCB_ENABLED
4494 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004495#endif
4496 return(((xmlDocPtr) ctxt->context->node)->children);
4497 case XML_ELEMENT_DECL:
4498 case XML_ATTRIBUTE_DECL:
4499 case XML_ENTITY_DECL:
4500 case XML_ATTRIBUTE_NODE:
4501 case XML_NAMESPACE_DECL:
4502 case XML_XINCLUDE_START:
4503 case XML_XINCLUDE_END:
4504 return(NULL);
4505 }
4506 return(NULL);
4507 }
4508 if ((cur->type == XML_DOCUMENT_NODE) ||
4509 (cur->type == XML_HTML_DOCUMENT_NODE))
4510 return(NULL);
4511 return(cur->next);
4512}
4513
4514/**
4515 * xmlXPathNextDescendant:
4516 * @ctxt: the XPath Parser context
4517 * @cur: the current node in the traversal
4518 *
4519 * Traversal function for the "descendant" direction
4520 * the descendant axis contains the descendants of the context node in document
4521 * order; a descendant is a child or a child of a child and so on.
4522 *
4523 * Returns the next element following that axis
4524 */
4525xmlNodePtr
4526xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4527 if (cur == NULL) {
4528 if (ctxt->context->node == NULL)
4529 return(NULL);
4530 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4531 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4532 return(NULL);
4533
4534 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
4535 return(ctxt->context->doc->children);
4536 return(ctxt->context->node->children);
4537 }
4538
Daniel Veillard567e1b42001-08-01 15:53:47 +00004539 if (cur->children != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004540 if (cur->children->type != XML_ENTITY_DECL)
Daniel Veillard567e1b42001-08-01 15:53:47 +00004541 return(cur->children);
4542 }
4543
4544 if (cur == ctxt->context->node) return(NULL);
4545
Owen Taylor3473f882001-02-23 17:55:21 +00004546 if (cur->next != NULL) return(cur->next);
4547
4548 do {
4549 cur = cur->parent;
4550 if (cur == NULL) return(NULL);
4551 if (cur == ctxt->context->node) return(NULL);
4552 if (cur->next != NULL) {
4553 cur = cur->next;
4554 return(cur);
4555 }
4556 } while (cur != NULL);
4557 return(cur);
4558}
4559
4560/**
4561 * xmlXPathNextDescendantOrSelf:
4562 * @ctxt: the XPath Parser context
4563 * @cur: the current node in the traversal
4564 *
4565 * Traversal function for the "descendant-or-self" direction
4566 * the descendant-or-self axis contains the context node and the descendants
4567 * of the context node in document order; thus the context node is the first
4568 * node on the axis, and the first child of the context node is the second node
4569 * on the axis
4570 *
4571 * Returns the next element following that axis
4572 */
4573xmlNodePtr
4574xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4575 if (cur == NULL) {
4576 if (ctxt->context->node == NULL)
4577 return(NULL);
4578 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4579 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4580 return(NULL);
4581 return(ctxt->context->node);
4582 }
4583
4584 return(xmlXPathNextDescendant(ctxt, cur));
4585}
4586
4587/**
4588 * xmlXPathNextParent:
4589 * @ctxt: the XPath Parser context
4590 * @cur: the current node in the traversal
4591 *
4592 * Traversal function for the "parent" direction
4593 * The parent axis contains the parent of the context node, if there is one.
4594 *
4595 * Returns the next element following that axis
4596 */
4597xmlNodePtr
4598xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4599 /*
4600 * the parent of an attribute or namespace node is the element
4601 * to which the attribute or namespace node is attached
4602 * Namespace handling !!!
4603 */
4604 if (cur == NULL) {
4605 if (ctxt->context->node == NULL) return(NULL);
4606 switch (ctxt->context->node->type) {
4607 case XML_ELEMENT_NODE:
4608 case XML_TEXT_NODE:
4609 case XML_CDATA_SECTION_NODE:
4610 case XML_ENTITY_REF_NODE:
4611 case XML_ENTITY_NODE:
4612 case XML_PI_NODE:
4613 case XML_COMMENT_NODE:
4614 case XML_NOTATION_NODE:
4615 case XML_DTD_NODE:
4616 case XML_ELEMENT_DECL:
4617 case XML_ATTRIBUTE_DECL:
4618 case XML_XINCLUDE_START:
4619 case XML_XINCLUDE_END:
4620 case XML_ENTITY_DECL:
4621 if (ctxt->context->node->parent == NULL)
4622 return((xmlNodePtr) ctxt->context->doc);
4623 return(ctxt->context->node->parent);
4624 case XML_ATTRIBUTE_NODE: {
4625 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4626
4627 return(att->parent);
4628 }
4629 case XML_DOCUMENT_NODE:
4630 case XML_DOCUMENT_TYPE_NODE:
4631 case XML_DOCUMENT_FRAG_NODE:
4632 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004633#ifdef LIBXML_DOCB_ENABLED
4634 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004635#endif
4636 return(NULL);
4637 case XML_NAMESPACE_DECL:
4638 /*
4639 * TODO !!! may require extending struct _xmlNs with
4640 * parent field
4641 * C.f. Infoset case...
4642 */
4643 return(NULL);
4644 }
4645 }
4646 return(NULL);
4647}
4648
4649/**
4650 * xmlXPathNextAncestor:
4651 * @ctxt: the XPath Parser context
4652 * @cur: the current node in the traversal
4653 *
4654 * Traversal function for the "ancestor" direction
4655 * the ancestor axis contains the ancestors of the context node; the ancestors
4656 * of the context node consist of the parent of context node and the parent's
4657 * parent and so on; the nodes are ordered in reverse document order; thus the
4658 * parent is the first node on the axis, and the parent's parent is the second
4659 * node on the axis
4660 *
4661 * Returns the next element following that axis
4662 */
4663xmlNodePtr
4664xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4665 /*
4666 * the parent of an attribute or namespace node is the element
4667 * to which the attribute or namespace node is attached
4668 * !!!!!!!!!!!!!
4669 */
4670 if (cur == NULL) {
4671 if (ctxt->context->node == NULL) return(NULL);
4672 switch (ctxt->context->node->type) {
4673 case XML_ELEMENT_NODE:
4674 case XML_TEXT_NODE:
4675 case XML_CDATA_SECTION_NODE:
4676 case XML_ENTITY_REF_NODE:
4677 case XML_ENTITY_NODE:
4678 case XML_PI_NODE:
4679 case XML_COMMENT_NODE:
4680 case XML_DTD_NODE:
4681 case XML_ELEMENT_DECL:
4682 case XML_ATTRIBUTE_DECL:
4683 case XML_ENTITY_DECL:
4684 case XML_NOTATION_NODE:
4685 case XML_XINCLUDE_START:
4686 case XML_XINCLUDE_END:
4687 if (ctxt->context->node->parent == NULL)
4688 return((xmlNodePtr) ctxt->context->doc);
4689 return(ctxt->context->node->parent);
4690 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004691 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00004692
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004693 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00004694 }
4695 case XML_DOCUMENT_NODE:
4696 case XML_DOCUMENT_TYPE_NODE:
4697 case XML_DOCUMENT_FRAG_NODE:
4698 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004699#ifdef LIBXML_DOCB_ENABLED
4700 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004701#endif
4702 return(NULL);
4703 case XML_NAMESPACE_DECL:
4704 /*
4705 * TODO !!! may require extending struct _xmlNs with
4706 * parent field
4707 * C.f. Infoset case...
4708 */
4709 return(NULL);
4710 }
4711 return(NULL);
4712 }
4713 if (cur == ctxt->context->doc->children)
4714 return((xmlNodePtr) ctxt->context->doc);
4715 if (cur == (xmlNodePtr) ctxt->context->doc)
4716 return(NULL);
4717 switch (cur->type) {
4718 case XML_ELEMENT_NODE:
4719 case XML_TEXT_NODE:
4720 case XML_CDATA_SECTION_NODE:
4721 case XML_ENTITY_REF_NODE:
4722 case XML_ENTITY_NODE:
4723 case XML_PI_NODE:
4724 case XML_COMMENT_NODE:
4725 case XML_NOTATION_NODE:
4726 case XML_DTD_NODE:
4727 case XML_ELEMENT_DECL:
4728 case XML_ATTRIBUTE_DECL:
4729 case XML_ENTITY_DECL:
4730 case XML_XINCLUDE_START:
4731 case XML_XINCLUDE_END:
4732 return(cur->parent);
4733 case XML_ATTRIBUTE_NODE: {
4734 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4735
4736 return(att->parent);
4737 }
4738 case XML_DOCUMENT_NODE:
4739 case XML_DOCUMENT_TYPE_NODE:
4740 case XML_DOCUMENT_FRAG_NODE:
4741 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004742#ifdef LIBXML_DOCB_ENABLED
4743 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004744#endif
4745 return(NULL);
4746 case XML_NAMESPACE_DECL:
4747 /*
4748 * TODO !!! may require extending struct _xmlNs with
4749 * parent field
4750 * C.f. Infoset case...
4751 */
4752 return(NULL);
4753 }
4754 return(NULL);
4755}
4756
4757/**
4758 * xmlXPathNextAncestorOrSelf:
4759 * @ctxt: the XPath Parser context
4760 * @cur: the current node in the traversal
4761 *
4762 * Traversal function for the "ancestor-or-self" direction
4763 * he ancestor-or-self axis contains the context node and ancestors of
4764 * the context node in reverse document order; thus the context node is
4765 * the first node on the axis, and the context node's parent the second;
4766 * parent here is defined the same as with the parent axis.
4767 *
4768 * Returns the next element following that axis
4769 */
4770xmlNodePtr
4771xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4772 if (cur == NULL)
4773 return(ctxt->context->node);
4774 return(xmlXPathNextAncestor(ctxt, cur));
4775}
4776
4777/**
4778 * xmlXPathNextFollowingSibling:
4779 * @ctxt: the XPath Parser context
4780 * @cur: the current node in the traversal
4781 *
4782 * Traversal function for the "following-sibling" direction
4783 * The following-sibling axis contains the following siblings of the context
4784 * node in document order.
4785 *
4786 * Returns the next element following that axis
4787 */
4788xmlNodePtr
4789xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4790 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4791 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4792 return(NULL);
4793 if (cur == (xmlNodePtr) ctxt->context->doc)
4794 return(NULL);
4795 if (cur == NULL)
4796 return(ctxt->context->node->next);
4797 return(cur->next);
4798}
4799
4800/**
4801 * xmlXPathNextPrecedingSibling:
4802 * @ctxt: the XPath Parser context
4803 * @cur: the current node in the traversal
4804 *
4805 * Traversal function for the "preceding-sibling" direction
4806 * The preceding-sibling axis contains the preceding siblings of the context
4807 * node in reverse document order; the first preceding sibling is first on the
4808 * axis; the sibling preceding that node is the second on the axis and so on.
4809 *
4810 * Returns the next element following that axis
4811 */
4812xmlNodePtr
4813xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4814 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4815 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4816 return(NULL);
4817 if (cur == (xmlNodePtr) ctxt->context->doc)
4818 return(NULL);
4819 if (cur == NULL)
4820 return(ctxt->context->node->prev);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004821 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
4822 cur = cur->prev;
4823 if (cur == NULL)
4824 return(ctxt->context->node->prev);
4825 }
Owen Taylor3473f882001-02-23 17:55:21 +00004826 return(cur->prev);
4827}
4828
4829/**
4830 * xmlXPathNextFollowing:
4831 * @ctxt: the XPath Parser context
4832 * @cur: the current node in the traversal
4833 *
4834 * Traversal function for the "following" direction
4835 * The following axis contains all nodes in the same document as the context
4836 * node that are after the context node in document order, excluding any
4837 * descendants and excluding attribute nodes and namespace nodes; the nodes
4838 * are ordered in document order
4839 *
4840 * Returns the next element following that axis
4841 */
4842xmlNodePtr
4843xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4844 if (cur != NULL && cur->children != NULL)
4845 return cur->children ;
4846 if (cur == NULL) cur = ctxt->context->node;
4847 if (cur == NULL) return(NULL) ; /* ERROR */
4848 if (cur->next != NULL) return(cur->next) ;
4849 do {
4850 cur = cur->parent;
4851 if (cur == NULL) return(NULL);
4852 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
4853 if (cur->next != NULL) return(cur->next);
4854 } while (cur != NULL);
4855 return(cur);
4856}
4857
4858/*
4859 * xmlXPathIsAncestor:
4860 * @ancestor: the ancestor node
4861 * @node: the current node
4862 *
4863 * Check that @ancestor is a @node's ancestor
4864 *
4865 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
4866 */
4867static int
4868xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
4869 if ((ancestor == NULL) || (node == NULL)) return(0);
4870 /* nodes need to be in the same document */
4871 if (ancestor->doc != node->doc) return(0);
4872 /* avoid searching if ancestor or node is the root node */
4873 if (ancestor == (xmlNodePtr) node->doc) return(1);
4874 if (node == (xmlNodePtr) ancestor->doc) return(0);
4875 while (node->parent != NULL) {
4876 if (node->parent == ancestor)
4877 return(1);
4878 node = node->parent;
4879 }
4880 return(0);
4881}
4882
4883/**
4884 * xmlXPathNextPreceding:
4885 * @ctxt: the XPath Parser context
4886 * @cur: the current node in the traversal
4887 *
4888 * Traversal function for the "preceding" direction
4889 * the preceding axis contains all nodes in the same document as the context
4890 * node that are before the context node in document order, excluding any
4891 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4892 * ordered in reverse document order
4893 *
4894 * Returns the next element following that axis
4895 */
4896xmlNodePtr
Daniel Veillardf06307e2001-07-03 10:35:50 +00004897xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
4898{
Owen Taylor3473f882001-02-23 17:55:21 +00004899 if (cur == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004900 cur = ctxt->context->node;
4901 if (cur == NULL)
4902 return (NULL);
4903 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4904 cur = cur->prev;
Owen Taylor3473f882001-02-23 17:55:21 +00004905 do {
4906 if (cur->prev != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004907 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
4908 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004909 }
4910
4911 cur = cur->parent;
Daniel Veillardf06307e2001-07-03 10:35:50 +00004912 if (cur == NULL)
4913 return (NULL);
4914 if (cur == ctxt->context->doc->children)
4915 return (NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004916 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
Daniel Veillardf06307e2001-07-03 10:35:50 +00004917 return (cur);
4918}
4919
4920/**
4921 * xmlXPathNextPrecedingInternal:
4922 * @ctxt: the XPath Parser context
4923 * @cur: the current node in the traversal
4924 *
4925 * Traversal function for the "preceding" direction
4926 * the preceding axis contains all nodes in the same document as the context
4927 * node that are before the context node in document order, excluding any
4928 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4929 * ordered in reverse document order
4930 * This is a faster implementation but internal only since it requires a
4931 * state kept in the parser context: ctxt->ancestor.
4932 *
4933 * Returns the next element following that axis
4934 */
4935static xmlNodePtr
4936xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
4937 xmlNodePtr cur)
4938{
4939 if (cur == NULL) {
4940 cur = ctxt->context->node;
4941 if (cur == NULL)
4942 return (NULL);
4943 ctxt->ancestor = cur->parent;
4944 }
4945 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4946 cur = cur->prev;
4947 while (cur->prev == NULL) {
4948 cur = cur->parent;
4949 if (cur == NULL)
4950 return (NULL);
4951 if (cur == ctxt->context->doc->children)
4952 return (NULL);
4953 if (cur != ctxt->ancestor)
4954 return (cur);
4955 ctxt->ancestor = cur->parent;
4956 }
4957 cur = cur->prev;
4958 while (cur->last != NULL)
4959 cur = cur->last;
4960 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004961}
4962
4963/**
4964 * xmlXPathNextNamespace:
4965 * @ctxt: the XPath Parser context
4966 * @cur: the current attribute in the traversal
4967 *
4968 * Traversal function for the "namespace" direction
4969 * the namespace axis contains the namespace nodes of the context node;
4970 * the order of nodes on this axis is implementation-defined; the axis will
4971 * be empty unless the context node is an element
4972 *
4973 * Returns the next element following that axis
4974 */
4975xmlNodePtr
4976xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004977 xmlNodePtr ret;
4978
Owen Taylor3473f882001-02-23 17:55:21 +00004979 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004980 if ((cur == NULL) || (ctxt->context->tmpNsList == NULL)) {
4981 if (ctxt->context->tmpNsList != NULL)
4982 xmlFree(ctxt->context->tmpNsList);
4983 ctxt->context->tmpNsList =
Owen Taylor3473f882001-02-23 17:55:21 +00004984 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004985 if (ctxt->context->tmpNsList == NULL) return(NULL);
4986 ctxt->context->tmpNsNr = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004987 }
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004988 ret = (xmlNodePtr)ctxt->context->tmpNsList[ctxt->context->tmpNsNr++];
4989 if (ret == NULL) {
4990 xmlFree(ctxt->context->tmpNsList);
4991 ctxt->context->tmpNsList = NULL;
4992 }
4993 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00004994}
4995
4996/**
4997 * xmlXPathNextAttribute:
4998 * @ctxt: the XPath Parser context
4999 * @cur: the current attribute in the traversal
5000 *
5001 * Traversal function for the "attribute" direction
5002 * TODO: support DTD inherited default attributes
5003 *
5004 * Returns the next element following that axis
5005 */
5006xmlNodePtr
5007xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00005008 if (ctxt->context->node == NULL)
5009 return(NULL);
5010 if (ctxt->context->node->type != XML_ELEMENT_NODE)
5011 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005012 if (cur == NULL) {
5013 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
5014 return(NULL);
5015 return((xmlNodePtr)ctxt->context->node->properties);
5016 }
5017 return((xmlNodePtr)cur->next);
5018}
5019
5020/************************************************************************
5021 * *
5022 * NodeTest Functions *
5023 * *
5024 ************************************************************************/
5025
Owen Taylor3473f882001-02-23 17:55:21 +00005026#define IS_FUNCTION 200
5027
Owen Taylor3473f882001-02-23 17:55:21 +00005028
5029/************************************************************************
5030 * *
5031 * Implicit tree core function library *
5032 * *
5033 ************************************************************************/
5034
5035/**
5036 * xmlXPathRoot:
5037 * @ctxt: the XPath Parser context
5038 *
5039 * Initialize the context to the root of the document
5040 */
5041void
5042xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
5043 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
5044 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5045}
5046
5047/************************************************************************
5048 * *
5049 * The explicit core function library *
5050 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
5051 * *
5052 ************************************************************************/
5053
5054
5055/**
5056 * xmlXPathLastFunction:
5057 * @ctxt: the XPath Parser context
5058 * @nargs: the number of arguments
5059 *
5060 * Implement the last() XPath function
5061 * number last()
5062 * The last function returns the number of nodes in the context node list.
5063 */
5064void
5065xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5066 CHECK_ARITY(0);
5067 if (ctxt->context->contextSize >= 0) {
5068 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
5069#ifdef DEBUG_EXPR
5070 xmlGenericError(xmlGenericErrorContext,
5071 "last() : %d\n", ctxt->context->contextSize);
5072#endif
5073 } else {
5074 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
5075 }
5076}
5077
5078/**
5079 * xmlXPathPositionFunction:
5080 * @ctxt: the XPath Parser context
5081 * @nargs: the number of arguments
5082 *
5083 * Implement the position() XPath function
5084 * number position()
5085 * The position function returns the position of the context node in the
5086 * context node list. The first position is 1, and so the last positionr
5087 * will be equal to last().
5088 */
5089void
5090xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5091 CHECK_ARITY(0);
5092 if (ctxt->context->proximityPosition >= 0) {
5093 valuePush(ctxt,
5094 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
5095#ifdef DEBUG_EXPR
5096 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
5097 ctxt->context->proximityPosition);
5098#endif
5099 } else {
5100 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
5101 }
5102}
5103
5104/**
5105 * xmlXPathCountFunction:
5106 * @ctxt: the XPath Parser context
5107 * @nargs: the number of arguments
5108 *
5109 * Implement the count() XPath function
5110 * number count(node-set)
5111 */
5112void
5113xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5114 xmlXPathObjectPtr cur;
5115
5116 CHECK_ARITY(1);
5117 if ((ctxt->value == NULL) ||
5118 ((ctxt->value->type != XPATH_NODESET) &&
5119 (ctxt->value->type != XPATH_XSLT_TREE)))
5120 XP_ERROR(XPATH_INVALID_TYPE);
5121 cur = valuePop(ctxt);
5122
Daniel Veillard911f49a2001-04-07 15:39:35 +00005123 if ((cur == NULL) || (cur->nodesetval == NULL))
5124 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillardfe703322001-08-14 12:18:09 +00005125 else if (cur->type == XPATH_NODESET) {
Daniel Veillard911f49a2001-04-07 15:39:35 +00005126 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillardfe703322001-08-14 12:18:09 +00005127 } else {
5128 if ((cur->nodesetval->nodeNr != 1) ||
5129 (cur->nodesetval->nodeTab == NULL)) {
5130 valuePush(ctxt, xmlXPathNewFloat((double) 0));
5131 } else {
5132 xmlNodePtr tmp;
5133 int i = 0;
5134
5135 tmp = cur->nodesetval->nodeTab[0];
5136 if (tmp != NULL) {
5137 tmp = tmp->children;
5138 while (tmp != NULL) {
5139 tmp = tmp->next;
5140 i++;
5141 }
5142 }
5143 valuePush(ctxt, xmlXPathNewFloat((double) i));
5144 }
5145 }
Owen Taylor3473f882001-02-23 17:55:21 +00005146 xmlXPathFreeObject(cur);
5147}
5148
5149/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005150 * xmlXPathGetElementsByIds:
5151 * @doc: the document
5152 * @ids: a whitespace separated list of IDs
5153 *
5154 * Selects elements by their unique ID.
5155 *
5156 * Returns a node-set of selected elements.
5157 */
5158static xmlNodeSetPtr
5159xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
5160 xmlNodeSetPtr ret;
5161 const xmlChar *cur = ids;
5162 xmlChar *ID;
5163 xmlAttrPtr attr;
5164 xmlNodePtr elem = NULL;
5165
5166 ret = xmlXPathNodeSetCreate(NULL);
5167
5168 while (IS_BLANK(*cur)) cur++;
5169 while (*cur != 0) {
5170 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
5171 (*cur == '.') || (*cur == '-') ||
5172 (*cur == '_') || (*cur == ':') ||
5173 (IS_COMBINING(*cur)) ||
5174 (IS_EXTENDER(*cur)))
5175 cur++;
5176
5177 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
5178
5179 ID = xmlStrndup(ids, cur - ids);
5180 attr = xmlGetID(doc, ID);
5181 if (attr != NULL) {
5182 elem = attr->parent;
5183 xmlXPathNodeSetAdd(ret, elem);
5184 }
5185 if (ID != NULL)
5186 xmlFree(ID);
5187
5188 while (IS_BLANK(*cur)) cur++;
5189 ids = cur;
5190 }
5191 return(ret);
5192}
5193
5194/**
Owen Taylor3473f882001-02-23 17:55:21 +00005195 * xmlXPathIdFunction:
5196 * @ctxt: the XPath Parser context
5197 * @nargs: the number of arguments
5198 *
5199 * Implement the id() XPath function
5200 * node-set id(object)
5201 * The id function selects elements by their unique ID
5202 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
5203 * then the result is the union of the result of applying id to the
5204 * string value of each of the nodes in the argument node-set. When the
5205 * argument to id is of any other type, the argument is converted to a
5206 * string as if by a call to the string function; the string is split
5207 * into a whitespace-separated list of tokens (whitespace is any sequence
5208 * of characters matching the production S); the result is a node-set
5209 * containing the elements in the same document as the context node that
5210 * have a unique ID equal to any of the tokens in the list.
5211 */
5212void
5213xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005214 xmlChar *tokens;
5215 xmlNodeSetPtr ret;
5216 xmlXPathObjectPtr obj;
Owen Taylor3473f882001-02-23 17:55:21 +00005217
5218 CHECK_ARITY(1);
5219 obj = valuePop(ctxt);
5220 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
5221 if (obj->type == XPATH_NODESET) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005222 xmlNodeSetPtr ns;
Owen Taylor3473f882001-02-23 17:55:21 +00005223 int i;
5224
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005225 ret = xmlXPathNodeSetCreate(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005226
Daniel Veillard911f49a2001-04-07 15:39:35 +00005227 if (obj->nodesetval != NULL) {
5228 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005229 tokens =
5230 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
5231 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
5232 ret = xmlXPathNodeSetMerge(ret, ns);
5233 xmlXPathFreeNodeSet(ns);
5234 if (tokens != NULL)
5235 xmlFree(tokens);
Daniel Veillard911f49a2001-04-07 15:39:35 +00005236 }
Owen Taylor3473f882001-02-23 17:55:21 +00005237 }
5238
5239 xmlXPathFreeObject(obj);
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005240 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005241 return;
5242 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005243 obj = xmlXPathConvertString(obj);
Owen Taylor3473f882001-02-23 17:55:21 +00005244
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005245 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
5246 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005247
Owen Taylor3473f882001-02-23 17:55:21 +00005248 xmlXPathFreeObject(obj);
5249 return;
5250}
5251
5252/**
5253 * xmlXPathLocalNameFunction:
5254 * @ctxt: the XPath Parser context
5255 * @nargs: the number of arguments
5256 *
5257 * Implement the local-name() XPath function
5258 * string local-name(node-set?)
5259 * The local-name function returns a string containing the local part
5260 * of the name of the node in the argument node-set that is first in
5261 * document order. If the node-set is empty or the first node has no
5262 * name, an empty string is returned. If the argument is omitted it
5263 * defaults to the context node.
5264 */
5265void
5266xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5267 xmlXPathObjectPtr cur;
5268
5269 if (nargs == 0) {
5270 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5271 nargs = 1;
5272 }
5273
5274 CHECK_ARITY(1);
5275 if ((ctxt->value == NULL) ||
5276 ((ctxt->value->type != XPATH_NODESET) &&
5277 (ctxt->value->type != XPATH_XSLT_TREE)))
5278 XP_ERROR(XPATH_INVALID_TYPE);
5279 cur = valuePop(ctxt);
5280
Daniel Veillard911f49a2001-04-07 15:39:35 +00005281 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005282 valuePush(ctxt, xmlXPathNewCString(""));
5283 } else {
5284 int i = 0; /* Should be first in document order !!!!! */
5285 switch (cur->nodesetval->nodeTab[i]->type) {
5286 case XML_ELEMENT_NODE:
5287 case XML_ATTRIBUTE_NODE:
5288 case XML_PI_NODE:
5289 valuePush(ctxt,
5290 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
5291 break;
5292 case XML_NAMESPACE_DECL:
5293 valuePush(ctxt, xmlXPathNewString(
5294 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
5295 break;
5296 default:
5297 valuePush(ctxt, xmlXPathNewCString(""));
5298 }
5299 }
5300 xmlXPathFreeObject(cur);
5301}
5302
5303/**
5304 * xmlXPathNamespaceURIFunction:
5305 * @ctxt: the XPath Parser context
5306 * @nargs: the number of arguments
5307 *
5308 * Implement the namespace-uri() XPath function
5309 * string namespace-uri(node-set?)
5310 * The namespace-uri function returns a string containing the
5311 * namespace URI of the expanded name of the node in the argument
5312 * node-set that is first in document order. If the node-set is empty,
5313 * the first node has no name, or the expanded name has no namespace
5314 * URI, an empty string is returned. If the argument is omitted it
5315 * defaults to the context node.
5316 */
5317void
5318xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5319 xmlXPathObjectPtr cur;
5320
5321 if (nargs == 0) {
5322 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5323 nargs = 1;
5324 }
5325 CHECK_ARITY(1);
5326 if ((ctxt->value == NULL) ||
5327 ((ctxt->value->type != XPATH_NODESET) &&
5328 (ctxt->value->type != XPATH_XSLT_TREE)))
5329 XP_ERROR(XPATH_INVALID_TYPE);
5330 cur = valuePop(ctxt);
5331
Daniel Veillard911f49a2001-04-07 15:39:35 +00005332 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005333 valuePush(ctxt, xmlXPathNewCString(""));
5334 } else {
5335 int i = 0; /* Should be first in document order !!!!! */
5336 switch (cur->nodesetval->nodeTab[i]->type) {
5337 case XML_ELEMENT_NODE:
5338 case XML_ATTRIBUTE_NODE:
5339 if (cur->nodesetval->nodeTab[i]->ns == NULL)
5340 valuePush(ctxt, xmlXPathNewCString(""));
5341 else
5342 valuePush(ctxt, xmlXPathNewString(
5343 cur->nodesetval->nodeTab[i]->ns->href));
5344 break;
5345 default:
5346 valuePush(ctxt, xmlXPathNewCString(""));
5347 }
5348 }
5349 xmlXPathFreeObject(cur);
5350}
5351
5352/**
5353 * xmlXPathNameFunction:
5354 * @ctxt: the XPath Parser context
5355 * @nargs: the number of arguments
5356 *
5357 * Implement the name() XPath function
5358 * string name(node-set?)
5359 * The name function returns a string containing a QName representing
5360 * the name of the node in the argument node-set that is first in documenti
5361 * order. The QName must represent the name with respect to the namespace
5362 * declarations in effect on the node whose name is being represented.
5363 * Typically, this will be the form in which the name occurred in the XML
5364 * source. This need not be the case if there are namespace declarations
5365 * in effect on the node that associate multiple prefixes with the same
5366 * namespace. However, an implementation may include information about
5367 * the original prefix in its representation of nodes; in this case, an
5368 * implementation can ensure that the returned string is always the same
5369 * as the QName used in the XML source. If the argument it omitted it
5370 * defaults to the context node.
5371 * Libxml keep the original prefix so the "real qualified name" used is
5372 * returned.
5373 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005374static void
Daniel Veillard04383752001-07-08 14:27:15 +00005375xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
5376{
Owen Taylor3473f882001-02-23 17:55:21 +00005377 xmlXPathObjectPtr cur;
5378
5379 if (nargs == 0) {
Daniel Veillard04383752001-07-08 14:27:15 +00005380 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5381 nargs = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005382 }
5383
5384 CHECK_ARITY(1);
Daniel Veillard04383752001-07-08 14:27:15 +00005385 if ((ctxt->value == NULL) ||
5386 ((ctxt->value->type != XPATH_NODESET) &&
5387 (ctxt->value->type != XPATH_XSLT_TREE)))
5388 XP_ERROR(XPATH_INVALID_TYPE);
Owen Taylor3473f882001-02-23 17:55:21 +00005389 cur = valuePop(ctxt);
5390
Daniel Veillard911f49a2001-04-07 15:39:35 +00005391 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Daniel Veillard04383752001-07-08 14:27:15 +00005392 valuePush(ctxt, xmlXPathNewCString(""));
Owen Taylor3473f882001-02-23 17:55:21 +00005393 } else {
Daniel Veillard04383752001-07-08 14:27:15 +00005394 int i = 0; /* Should be first in document order !!!!! */
Owen Taylor3473f882001-02-23 17:55:21 +00005395
Daniel Veillard04383752001-07-08 14:27:15 +00005396 switch (cur->nodesetval->nodeTab[i]->type) {
5397 case XML_ELEMENT_NODE:
5398 case XML_ATTRIBUTE_NODE:
5399 if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
5400 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL))
5401 valuePush(ctxt,
5402 xmlXPathNewString(cur->nodesetval->
5403 nodeTab[i]->name));
5404
5405 else {
5406 char name[2000];
5407
5408 snprintf(name, sizeof(name), "%s:%s",
5409 (char *) cur->nodesetval->nodeTab[i]->ns->
5410 prefix,
5411 (char *) cur->nodesetval->nodeTab[i]->name);
5412 name[sizeof(name) - 1] = 0;
5413 valuePush(ctxt, xmlXPathNewCString(name));
5414 }
5415 break;
5416 default:
5417 valuePush(ctxt,
5418 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5419 xmlXPathLocalNameFunction(ctxt, 1);
5420 }
Owen Taylor3473f882001-02-23 17:55:21 +00005421 }
5422 xmlXPathFreeObject(cur);
5423}
5424
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005425
5426/**
Owen Taylor3473f882001-02-23 17:55:21 +00005427 * xmlXPathStringFunction:
5428 * @ctxt: the XPath Parser context
5429 * @nargs: the number of arguments
5430 *
5431 * Implement the string() XPath function
5432 * string string(object?)
5433 * he string function converts an object to a string as follows:
5434 * - A node-set is converted to a string by returning the value of
5435 * the node in the node-set that is first in document order.
5436 * If the node-set is empty, an empty string is returned.
5437 * - A number is converted to a string as follows
5438 * + NaN is converted to the string NaN
5439 * + positive zero is converted to the string 0
5440 * + negative zero is converted to the string 0
5441 * + positive infinity is converted to the string Infinity
5442 * + negative infinity is converted to the string -Infinity
5443 * + if the number is an integer, the number is represented in
5444 * decimal form as a Number with no decimal point and no leading
5445 * zeros, preceded by a minus sign (-) if the number is negative
5446 * + otherwise, the number is represented in decimal form as a
5447 * Number including a decimal point with at least one digit
5448 * before the decimal point and at least one digit after the
5449 * decimal point, preceded by a minus sign (-) if the number
5450 * is negative; there must be no leading zeros before the decimal
5451 * point apart possibly from the one required digit immediatelyi
5452 * before the decimal point; beyond the one required digit
5453 * after the decimal point there must be as many, but only as
5454 * many, more digits as are needed to uniquely distinguish the
5455 * number from all other IEEE 754 numeric values.
5456 * - The boolean false value is converted to the string false.
5457 * The boolean true value is converted to the string true.
5458 *
5459 * If the argument is omitted, it defaults to a node-set with the
5460 * context node as its only member.
5461 */
5462void
5463xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5464 xmlXPathObjectPtr cur;
5465
5466 if (nargs == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005467 valuePush(ctxt,
5468 xmlXPathWrapString(
5469 xmlXPathCastNodeToString(ctxt->context->node)));
5470 return;
Owen Taylor3473f882001-02-23 17:55:21 +00005471 }
5472
5473 CHECK_ARITY(1);
5474 cur = valuePop(ctxt);
5475 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005476 cur = xmlXPathConvertString(cur);
5477 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005478}
5479
5480/**
5481 * xmlXPathStringLengthFunction:
5482 * @ctxt: the XPath Parser context
5483 * @nargs: the number of arguments
5484 *
5485 * Implement the string-length() XPath function
5486 * number string-length(string?)
5487 * The string-length returns the number of characters in the string
5488 * (see [3.6 Strings]). If the argument is omitted, it defaults to
5489 * the context node converted to a string, in other words the value
5490 * of the context node.
5491 */
5492void
5493xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5494 xmlXPathObjectPtr cur;
5495
5496 if (nargs == 0) {
5497 if (ctxt->context->node == NULL) {
5498 valuePush(ctxt, xmlXPathNewFloat(0));
5499 } else {
5500 xmlChar *content;
5501
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005502 content = xmlXPathCastNodeToString(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005503 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00005504 xmlFree(content);
5505 }
5506 return;
5507 }
5508 CHECK_ARITY(1);
5509 CAST_TO_STRING;
5510 CHECK_TYPE(XPATH_STRING);
5511 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005512 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00005513 xmlXPathFreeObject(cur);
5514}
5515
5516/**
5517 * xmlXPathConcatFunction:
5518 * @ctxt: the XPath Parser context
5519 * @nargs: the number of arguments
5520 *
5521 * Implement the concat() XPath function
5522 * string concat(string, string, string*)
5523 * The concat function returns the concatenation of its arguments.
5524 */
5525void
5526xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5527 xmlXPathObjectPtr cur, newobj;
5528 xmlChar *tmp;
5529
5530 if (nargs < 2) {
5531 CHECK_ARITY(2);
5532 }
5533
5534 CAST_TO_STRING;
5535 cur = valuePop(ctxt);
5536 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
5537 xmlXPathFreeObject(cur);
5538 return;
5539 }
5540 nargs--;
5541
5542 while (nargs > 0) {
5543 CAST_TO_STRING;
5544 newobj = valuePop(ctxt);
5545 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
5546 xmlXPathFreeObject(newobj);
5547 xmlXPathFreeObject(cur);
5548 XP_ERROR(XPATH_INVALID_TYPE);
5549 }
5550 tmp = xmlStrcat(newobj->stringval, cur->stringval);
5551 newobj->stringval = cur->stringval;
5552 cur->stringval = tmp;
5553
5554 xmlXPathFreeObject(newobj);
5555 nargs--;
5556 }
5557 valuePush(ctxt, cur);
5558}
5559
5560/**
5561 * xmlXPathContainsFunction:
5562 * @ctxt: the XPath Parser context
5563 * @nargs: the number of arguments
5564 *
5565 * Implement the contains() XPath function
5566 * boolean contains(string, string)
5567 * The contains function returns true if the first argument string
5568 * contains the second argument string, and otherwise returns false.
5569 */
5570void
5571xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5572 xmlXPathObjectPtr hay, needle;
5573
5574 CHECK_ARITY(2);
5575 CAST_TO_STRING;
5576 CHECK_TYPE(XPATH_STRING);
5577 needle = valuePop(ctxt);
5578 CAST_TO_STRING;
5579 hay = valuePop(ctxt);
5580 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5581 xmlXPathFreeObject(hay);
5582 xmlXPathFreeObject(needle);
5583 XP_ERROR(XPATH_INVALID_TYPE);
5584 }
5585 if (xmlStrstr(hay->stringval, needle->stringval))
5586 valuePush(ctxt, xmlXPathNewBoolean(1));
5587 else
5588 valuePush(ctxt, xmlXPathNewBoolean(0));
5589 xmlXPathFreeObject(hay);
5590 xmlXPathFreeObject(needle);
5591}
5592
5593/**
5594 * xmlXPathStartsWithFunction:
5595 * @ctxt: the XPath Parser context
5596 * @nargs: the number of arguments
5597 *
5598 * Implement the starts-with() XPath function
5599 * boolean starts-with(string, string)
5600 * The starts-with function returns true if the first argument string
5601 * starts with the second argument string, and otherwise returns false.
5602 */
5603void
5604xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5605 xmlXPathObjectPtr hay, needle;
5606 int n;
5607
5608 CHECK_ARITY(2);
5609 CAST_TO_STRING;
5610 CHECK_TYPE(XPATH_STRING);
5611 needle = valuePop(ctxt);
5612 CAST_TO_STRING;
5613 hay = valuePop(ctxt);
5614 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5615 xmlXPathFreeObject(hay);
5616 xmlXPathFreeObject(needle);
5617 XP_ERROR(XPATH_INVALID_TYPE);
5618 }
5619 n = xmlStrlen(needle->stringval);
5620 if (xmlStrncmp(hay->stringval, needle->stringval, n))
5621 valuePush(ctxt, xmlXPathNewBoolean(0));
5622 else
5623 valuePush(ctxt, xmlXPathNewBoolean(1));
5624 xmlXPathFreeObject(hay);
5625 xmlXPathFreeObject(needle);
5626}
5627
5628/**
5629 * xmlXPathSubstringFunction:
5630 * @ctxt: the XPath Parser context
5631 * @nargs: the number of arguments
5632 *
5633 * Implement the substring() XPath function
5634 * string substring(string, number, number?)
5635 * The substring function returns the substring of the first argument
5636 * starting at the position specified in the second argument with
5637 * length specified in the third argument. For example,
5638 * substring("12345",2,3) returns "234". If the third argument is not
5639 * specified, it returns the substring starting at the position specified
5640 * in the second argument and continuing to the end of the string. For
5641 * example, substring("12345",2) returns "2345". More precisely, each
5642 * character in the string (see [3.6 Strings]) is considered to have a
5643 * numeric position: the position of the first character is 1, the position
5644 * of the second character is 2 and so on. The returned substring contains
5645 * those characters for which the position of the character is greater than
5646 * or equal to the second argument and, if the third argument is specified,
5647 * less than the sum of the second and third arguments; the comparisons
5648 * and addition used for the above follow the standard IEEE 754 rules. Thus:
5649 * - substring("12345", 1.5, 2.6) returns "234"
5650 * - substring("12345", 0, 3) returns "12"
5651 * - substring("12345", 0 div 0, 3) returns ""
5652 * - substring("12345", 1, 0 div 0) returns ""
5653 * - substring("12345", -42, 1 div 0) returns "12345"
5654 * - substring("12345", -1 div 0, 1 div 0) returns ""
5655 */
5656void
5657xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5658 xmlXPathObjectPtr str, start, len;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005659 double le=0, in;
5660 int i, l, m;
Owen Taylor3473f882001-02-23 17:55:21 +00005661 xmlChar *ret;
5662
Owen Taylor3473f882001-02-23 17:55:21 +00005663 if (nargs < 2) {
5664 CHECK_ARITY(2);
5665 }
5666 if (nargs > 3) {
5667 CHECK_ARITY(3);
5668 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005669 /*
5670 * take care of possible last (position) argument
5671 */
Owen Taylor3473f882001-02-23 17:55:21 +00005672 if (nargs == 3) {
5673 CAST_TO_NUMBER;
5674 CHECK_TYPE(XPATH_NUMBER);
5675 len = valuePop(ctxt);
5676 le = len->floatval;
5677 xmlXPathFreeObject(len);
Owen Taylor3473f882001-02-23 17:55:21 +00005678 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005679
Owen Taylor3473f882001-02-23 17:55:21 +00005680 CAST_TO_NUMBER;
5681 CHECK_TYPE(XPATH_NUMBER);
5682 start = valuePop(ctxt);
5683 in = start->floatval;
5684 xmlXPathFreeObject(start);
5685 CAST_TO_STRING;
5686 CHECK_TYPE(XPATH_STRING);
5687 str = valuePop(ctxt);
Daniel Veillard97ac1312001-05-30 19:14:17 +00005688 m = xmlUTF8Strlen((const unsigned char *)str->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00005689
Daniel Veillard97ac1312001-05-30 19:14:17 +00005690 /*
5691 * If last pos not present, calculate last position
5692 */
5693 if (nargs != 3)
5694 le = m;
5695
5696 /*
5697 * To meet our requirements, initial index calculations
5698 * must be done before we convert to integer format
5699 *
5700 * First we normalize indices
5701 */
5702 in -= 1.0;
5703 le += in;
5704 if (in < 0.0)
5705 in = 0.0;
5706 if (le > (double)m)
5707 le = (double)m;
5708
5709 /*
5710 * Now we go to integer form, rounding up
5711 */
Owen Taylor3473f882001-02-23 17:55:21 +00005712 i = (int) in;
5713 if (((double)i) != in) i++;
5714
Owen Taylor3473f882001-02-23 17:55:21 +00005715 l = (int) le;
5716 if (((double)l) != le) l++;
5717
Daniel Veillard97ac1312001-05-30 19:14:17 +00005718 if (l > m) l=m;
Owen Taylor3473f882001-02-23 17:55:21 +00005719
5720 /* number of chars to copy */
5721 l -= i;
5722
Daniel Veillard97ac1312001-05-30 19:14:17 +00005723 ret = xmlUTF8Strsub(str->stringval, i, l);
Owen Taylor3473f882001-02-23 17:55:21 +00005724 if (ret == NULL)
5725 valuePush(ctxt, xmlXPathNewCString(""));
5726 else {
5727 valuePush(ctxt, xmlXPathNewString(ret));
5728 xmlFree(ret);
5729 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005730
Owen Taylor3473f882001-02-23 17:55:21 +00005731 xmlXPathFreeObject(str);
5732}
5733
5734/**
5735 * xmlXPathSubstringBeforeFunction:
5736 * @ctxt: the XPath Parser context
5737 * @nargs: the number of arguments
5738 *
5739 * Implement the substring-before() XPath function
5740 * string substring-before(string, string)
5741 * The substring-before function returns the substring of the first
5742 * argument string that precedes the first occurrence of the second
5743 * argument string in the first argument string, or the empty string
5744 * if the first argument string does not contain the second argument
5745 * string. For example, substring-before("1999/04/01","/") returns 1999.
5746 */
5747void
5748xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5749 xmlXPathObjectPtr str;
5750 xmlXPathObjectPtr find;
5751 xmlBufferPtr target;
5752 const xmlChar *point;
5753 int offset;
5754
5755 CHECK_ARITY(2);
5756 CAST_TO_STRING;
5757 find = valuePop(ctxt);
5758 CAST_TO_STRING;
5759 str = valuePop(ctxt);
5760
5761 target = xmlBufferCreate();
5762 if (target) {
5763 point = xmlStrstr(str->stringval, find->stringval);
5764 if (point) {
5765 offset = (int)(point - str->stringval);
5766 xmlBufferAdd(target, str->stringval, offset);
5767 }
5768 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5769 xmlBufferFree(target);
5770 }
5771
5772 xmlXPathFreeObject(str);
5773 xmlXPathFreeObject(find);
5774}
5775
5776/**
5777 * xmlXPathSubstringAfterFunction:
5778 * @ctxt: the XPath Parser context
5779 * @nargs: the number of arguments
5780 *
5781 * Implement the substring-after() XPath function
5782 * string substring-after(string, string)
5783 * The substring-after function returns the substring of the first
5784 * argument string that follows the first occurrence of the second
5785 * argument string in the first argument string, or the empty stringi
5786 * if the first argument string does not contain the second argument
5787 * string. For example, substring-after("1999/04/01","/") returns 04/01,
5788 * and substring-after("1999/04/01","19") returns 99/04/01.
5789 */
5790void
5791xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5792 xmlXPathObjectPtr str;
5793 xmlXPathObjectPtr find;
5794 xmlBufferPtr target;
5795 const xmlChar *point;
5796 int offset;
5797
5798 CHECK_ARITY(2);
5799 CAST_TO_STRING;
5800 find = valuePop(ctxt);
5801 CAST_TO_STRING;
5802 str = valuePop(ctxt);
5803
5804 target = xmlBufferCreate();
5805 if (target) {
5806 point = xmlStrstr(str->stringval, find->stringval);
5807 if (point) {
5808 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
5809 xmlBufferAdd(target, &str->stringval[offset],
5810 xmlStrlen(str->stringval) - offset);
5811 }
5812 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5813 xmlBufferFree(target);
5814 }
5815
5816 xmlXPathFreeObject(str);
5817 xmlXPathFreeObject(find);
5818}
5819
5820/**
5821 * xmlXPathNormalizeFunction:
5822 * @ctxt: the XPath Parser context
5823 * @nargs: the number of arguments
5824 *
5825 * Implement the normalize-space() XPath function
5826 * string normalize-space(string?)
5827 * The normalize-space function returns the argument string with white
5828 * space normalized by stripping leading and trailing whitespace
5829 * and replacing sequences of whitespace characters by a single
5830 * space. Whitespace characters are the same allowed by the S production
5831 * in XML. If the argument is omitted, it defaults to the context
5832 * node converted to a string, in other words the value of the context node.
5833 */
5834void
5835xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5836 xmlXPathObjectPtr obj = NULL;
5837 xmlChar *source = NULL;
5838 xmlBufferPtr target;
5839 xmlChar blank;
5840
5841 if (nargs == 0) {
5842 /* Use current context node */
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005843 valuePush(ctxt,
5844 xmlXPathWrapString(
5845 xmlXPathCastNodeToString(ctxt->context->node)));
Owen Taylor3473f882001-02-23 17:55:21 +00005846 nargs = 1;
5847 }
5848
5849 CHECK_ARITY(1);
5850 CAST_TO_STRING;
5851 CHECK_TYPE(XPATH_STRING);
5852 obj = valuePop(ctxt);
5853 source = obj->stringval;
5854
5855 target = xmlBufferCreate();
5856 if (target && source) {
5857
5858 /* Skip leading whitespaces */
5859 while (IS_BLANK(*source))
5860 source++;
5861
5862 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
5863 blank = 0;
5864 while (*source) {
5865 if (IS_BLANK(*source)) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005866 blank = 0x20;
Owen Taylor3473f882001-02-23 17:55:21 +00005867 } else {
5868 if (blank) {
5869 xmlBufferAdd(target, &blank, 1);
5870 blank = 0;
5871 }
5872 xmlBufferAdd(target, source, 1);
5873 }
5874 source++;
5875 }
5876
5877 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5878 xmlBufferFree(target);
5879 }
5880 xmlXPathFreeObject(obj);
5881}
5882
5883/**
5884 * xmlXPathTranslateFunction:
5885 * @ctxt: the XPath Parser context
5886 * @nargs: the number of arguments
5887 *
5888 * Implement the translate() XPath function
5889 * string translate(string, string, string)
5890 * The translate function returns the first argument string with
5891 * occurrences of characters in the second argument string replaced
5892 * by the character at the corresponding position in the third argument
5893 * string. For example, translate("bar","abc","ABC") returns the string
5894 * BAr. If there is a character in the second argument string with no
5895 * character at a corresponding position in the third argument string
5896 * (because the second argument string is longer than the third argument
5897 * string), then occurrences of that character in the first argument
5898 * string are removed. For example, translate("--aaa--","abc-","ABC")
5899 * returns "AAA". If a character occurs more than once in second
5900 * argument string, then the first occurrence determines the replacement
5901 * character. If the third argument string is longer than the second
5902 * argument string, then excess characters are ignored.
5903 */
5904void
5905xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00005906 xmlXPathObjectPtr str;
5907 xmlXPathObjectPtr from;
5908 xmlXPathObjectPtr to;
5909 xmlBufferPtr target;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005910 int offset, max;
Daniel Veillarde043ee12001-04-16 14:08:07 +00005911 xmlChar ch;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005912 xmlChar *point;
5913 xmlChar *cptr;
Owen Taylor3473f882001-02-23 17:55:21 +00005914
Daniel Veillarde043ee12001-04-16 14:08:07 +00005915 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00005916
Daniel Veillarde043ee12001-04-16 14:08:07 +00005917 CAST_TO_STRING;
5918 to = valuePop(ctxt);
5919 CAST_TO_STRING;
5920 from = valuePop(ctxt);
5921 CAST_TO_STRING;
5922 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005923
Daniel Veillarde043ee12001-04-16 14:08:07 +00005924 target = xmlBufferCreate();
5925 if (target) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005926 max = xmlUTF8Strlen(to->stringval);
5927 for (cptr = str->stringval; (ch=*cptr); ) {
5928 offset = xmlUTF8Strloc(from->stringval, cptr);
5929 if (offset >= 0) {
5930 if (offset < max) {
5931 point = xmlUTF8Strpos(to->stringval, offset);
5932 if (point)
5933 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
5934 }
5935 } else
5936 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
5937
5938 /* Step to next character in input */
5939 cptr++;
5940 if ( ch & 0x80 ) {
5941 /* if not simple ascii, verify proper format */
5942 if ( (ch & 0xc0) != 0xc0 ) {
5943 xmlGenericError(xmlGenericErrorContext,
5944 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
5945 break;
5946 }
5947 /* then skip over remaining bytes for this char */
5948 while ( (ch <<= 1) & 0x80 )
5949 if ( (*cptr++ & 0xc0) != 0x80 ) {
5950 xmlGenericError(xmlGenericErrorContext,
5951 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
5952 break;
5953 }
5954 if (ch & 0x80) /* must have had error encountered */
5955 break;
5956 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00005957 }
Owen Taylor3473f882001-02-23 17:55:21 +00005958 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00005959 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5960 xmlBufferFree(target);
5961 xmlXPathFreeObject(str);
5962 xmlXPathFreeObject(from);
5963 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00005964}
5965
5966/**
5967 * xmlXPathBooleanFunction:
5968 * @ctxt: the XPath Parser context
5969 * @nargs: the number of arguments
5970 *
5971 * Implement the boolean() XPath function
5972 * boolean boolean(object)
5973 * he boolean function converts its argument to a boolean as follows:
5974 * - a number is true if and only if it is neither positive or
5975 * negative zero nor NaN
5976 * - a node-set is true if and only if it is non-empty
5977 * - a string is true if and only if its length is non-zero
5978 */
5979void
5980xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5981 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00005982
5983 CHECK_ARITY(1);
5984 cur = valuePop(ctxt);
5985 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005986 cur = xmlXPathConvertBoolean(cur);
5987 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005988}
5989
5990/**
5991 * xmlXPathNotFunction:
5992 * @ctxt: the XPath Parser context
5993 * @nargs: the number of arguments
5994 *
5995 * Implement the not() XPath function
5996 * boolean not(boolean)
5997 * The not function returns true if its argument is false,
5998 * and false otherwise.
5999 */
6000void
6001xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6002 CHECK_ARITY(1);
6003 CAST_TO_BOOLEAN;
6004 CHECK_TYPE(XPATH_BOOLEAN);
6005 ctxt->value->boolval = ! ctxt->value->boolval;
6006}
6007
6008/**
6009 * xmlXPathTrueFunction:
6010 * @ctxt: the XPath Parser context
6011 * @nargs: the number of arguments
6012 *
6013 * Implement the true() XPath function
6014 * boolean true()
6015 */
6016void
6017xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6018 CHECK_ARITY(0);
6019 valuePush(ctxt, xmlXPathNewBoolean(1));
6020}
6021
6022/**
6023 * xmlXPathFalseFunction:
6024 * @ctxt: the XPath Parser context
6025 * @nargs: the number of arguments
6026 *
6027 * Implement the false() XPath function
6028 * boolean false()
6029 */
6030void
6031xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6032 CHECK_ARITY(0);
6033 valuePush(ctxt, xmlXPathNewBoolean(0));
6034}
6035
6036/**
6037 * xmlXPathLangFunction:
6038 * @ctxt: the XPath Parser context
6039 * @nargs: the number of arguments
6040 *
6041 * Implement the lang() XPath function
6042 * boolean lang(string)
6043 * The lang function returns true or false depending on whether the
6044 * language of the context node as specified by xml:lang attributes
6045 * is the same as or is a sublanguage of the language specified by
6046 * the argument string. The language of the context node is determined
6047 * by the value of the xml:lang attribute on the context node, or, if
6048 * the context node has no xml:lang attribute, by the value of the
6049 * xml:lang attribute on the nearest ancestor of the context node that
6050 * has an xml:lang attribute. If there is no such attribute, then lang
6051 * returns false. If there is such an attribute, then lang returns
6052 * true if the attribute value is equal to the argument ignoring case,
6053 * or if there is some suffix starting with - such that the attribute
6054 * value is equal to the argument ignoring that suffix of the attribute
6055 * value and ignoring case.
6056 */
6057void
6058xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6059 xmlXPathObjectPtr val;
6060 const xmlChar *theLang;
6061 const xmlChar *lang;
6062 int ret = 0;
6063 int i;
6064
6065 CHECK_ARITY(1);
6066 CAST_TO_STRING;
6067 CHECK_TYPE(XPATH_STRING);
6068 val = valuePop(ctxt);
6069 lang = val->stringval;
6070 theLang = xmlNodeGetLang(ctxt->context->node);
6071 if ((theLang != NULL) && (lang != NULL)) {
6072 for (i = 0;lang[i] != 0;i++)
6073 if (toupper(lang[i]) != toupper(theLang[i]))
6074 goto not_equal;
6075 ret = 1;
6076 }
6077not_equal:
6078 xmlXPathFreeObject(val);
6079 valuePush(ctxt, xmlXPathNewBoolean(ret));
6080}
6081
6082/**
6083 * xmlXPathNumberFunction:
6084 * @ctxt: the XPath Parser context
6085 * @nargs: the number of arguments
6086 *
6087 * Implement the number() XPath function
6088 * number number(object?)
6089 */
6090void
6091xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6092 xmlXPathObjectPtr cur;
6093 double res;
6094
6095 if (nargs == 0) {
6096 if (ctxt->context->node == NULL) {
6097 valuePush(ctxt, xmlXPathNewFloat(0.0));
6098 } else {
6099 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
6100
6101 res = xmlXPathStringEvalNumber(content);
6102 valuePush(ctxt, xmlXPathNewFloat(res));
6103 xmlFree(content);
6104 }
6105 return;
6106 }
6107
6108 CHECK_ARITY(1);
6109 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006110 cur = xmlXPathConvertNumber(cur);
6111 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006112}
6113
6114/**
6115 * xmlXPathSumFunction:
6116 * @ctxt: the XPath Parser context
6117 * @nargs: the number of arguments
6118 *
6119 * Implement the sum() XPath function
6120 * number sum(node-set)
6121 * The sum function returns the sum of the values of the nodes in
6122 * the argument node-set.
6123 */
6124void
6125xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6126 xmlXPathObjectPtr cur;
6127 int i;
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006128 double res = 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +00006129
6130 CHECK_ARITY(1);
6131 if ((ctxt->value == NULL) ||
6132 ((ctxt->value->type != XPATH_NODESET) &&
6133 (ctxt->value->type != XPATH_XSLT_TREE)))
6134 XP_ERROR(XPATH_INVALID_TYPE);
6135 cur = valuePop(ctxt);
6136
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006137 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006138 valuePush(ctxt, xmlXPathNewFloat(0.0));
6139 } else {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006140 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
6141 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00006142 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006143 valuePush(ctxt, xmlXPathNewFloat(res));
Owen Taylor3473f882001-02-23 17:55:21 +00006144 }
6145 xmlXPathFreeObject(cur);
6146}
6147
6148/**
6149 * xmlXPathFloorFunction:
6150 * @ctxt: the XPath Parser context
6151 * @nargs: the number of arguments
6152 *
6153 * Implement the floor() XPath function
6154 * number floor(number)
6155 * The floor function returns the largest (closest to positive infinity)
6156 * number that is not greater than the argument and that is an integer.
6157 */
6158void
6159xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6160 CHECK_ARITY(1);
6161 CAST_TO_NUMBER;
6162 CHECK_TYPE(XPATH_NUMBER);
6163#if 0
6164 ctxt->value->floatval = floor(ctxt->value->floatval);
6165#else
6166 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
6167 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
6168#endif
6169}
6170
6171/**
6172 * xmlXPathCeilingFunction:
6173 * @ctxt: the XPath Parser context
6174 * @nargs: the number of arguments
6175 *
6176 * Implement the ceiling() XPath function
6177 * number ceiling(number)
6178 * The ceiling function returns the smallest (closest to negative infinity)
6179 * number that is not less than the argument and that is an integer.
6180 */
6181void
6182xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6183 double f;
6184
6185 CHECK_ARITY(1);
6186 CAST_TO_NUMBER;
6187 CHECK_TYPE(XPATH_NUMBER);
6188
6189#if 0
6190 ctxt->value->floatval = ceil(ctxt->value->floatval);
6191#else
6192 f = (double)((int) ctxt->value->floatval);
6193 if (f != ctxt->value->floatval)
6194 ctxt->value->floatval = f + 1;
6195#endif
6196}
6197
6198/**
6199 * xmlXPathRoundFunction:
6200 * @ctxt: the XPath Parser context
6201 * @nargs: the number of arguments
6202 *
6203 * Implement the round() XPath function
6204 * number round(number)
6205 * The round function returns the number that is closest to the
6206 * argument and that is an integer. If there are two such numbers,
6207 * then the one that is even is returned.
6208 */
6209void
6210xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6211 double f;
6212
6213 CHECK_ARITY(1);
6214 CAST_TO_NUMBER;
6215 CHECK_TYPE(XPATH_NUMBER);
6216
Daniel Veillardcda96922001-08-21 10:56:31 +00006217 if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
6218 (xmlXPathIsInf(ctxt->value->floatval) == 1) ||
6219 (xmlXPathIsInf(ctxt->value->floatval) == -1) ||
Owen Taylor3473f882001-02-23 17:55:21 +00006220 (ctxt->value->floatval == 0.0))
6221 return;
6222
6223#if 0
6224 f = floor(ctxt->value->floatval);
6225#else
6226 f = (double)((int) ctxt->value->floatval);
6227#endif
6228 if (ctxt->value->floatval < f + 0.5)
6229 ctxt->value->floatval = f;
6230 else
6231 ctxt->value->floatval = f + 1;
6232}
6233
6234/************************************************************************
6235 * *
6236 * The Parser *
6237 * *
6238 ************************************************************************/
6239
6240/*
6241 * a couple of forward declarations since we use a recursive call based
6242 * implementation.
6243 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006244static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006245static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006246static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006247#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006248static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
6249#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006250#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006251static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006252#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006253static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
6254 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00006255
6256/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00006257 * xmlXPathCurrentChar:
6258 * @ctxt: the XPath parser context
6259 * @cur: pointer to the beginning of the char
6260 * @len: pointer to the length of the char read
6261 *
6262 * The current char value, if using UTF-8 this may actaully span multiple
6263 * bytes in the input buffer.
6264 *
6265 * Returns the current char value and its lenght
6266 */
6267
6268static int
6269xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
6270 unsigned char c;
6271 unsigned int val;
6272 const xmlChar *cur;
6273
6274 if (ctxt == NULL)
6275 return(0);
6276 cur = ctxt->cur;
6277
6278 /*
6279 * We are supposed to handle UTF8, check it's valid
6280 * From rfc2044: encoding of the Unicode values on UTF-8:
6281 *
6282 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
6283 * 0000 0000-0000 007F 0xxxxxxx
6284 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
6285 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
6286 *
6287 * Check for the 0x110000 limit too
6288 */
6289 c = *cur;
6290 if (c & 0x80) {
6291 if ((cur[1] & 0xc0) != 0x80)
6292 goto encoding_error;
6293 if ((c & 0xe0) == 0xe0) {
6294
6295 if ((cur[2] & 0xc0) != 0x80)
6296 goto encoding_error;
6297 if ((c & 0xf0) == 0xf0) {
6298 if (((c & 0xf8) != 0xf0) ||
6299 ((cur[3] & 0xc0) != 0x80))
6300 goto encoding_error;
6301 /* 4-byte code */
6302 *len = 4;
6303 val = (cur[0] & 0x7) << 18;
6304 val |= (cur[1] & 0x3f) << 12;
6305 val |= (cur[2] & 0x3f) << 6;
6306 val |= cur[3] & 0x3f;
6307 } else {
6308 /* 3-byte code */
6309 *len = 3;
6310 val = (cur[0] & 0xf) << 12;
6311 val |= (cur[1] & 0x3f) << 6;
6312 val |= cur[2] & 0x3f;
6313 }
6314 } else {
6315 /* 2-byte code */
6316 *len = 2;
6317 val = (cur[0] & 0x1f) << 6;
6318 val |= cur[1] & 0x3f;
6319 }
6320 if (!IS_CHAR(val)) {
6321 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
6322 }
6323 return(val);
6324 } else {
6325 /* 1-byte code */
6326 *len = 1;
6327 return((int) *cur);
6328 }
6329encoding_error:
6330 /*
6331 * If we detect an UTF8 error that probably mean that the
6332 * input encoding didn't get properly advertized in the
6333 * declaration header. Report the error and switch the encoding
6334 * to ISO-Latin-1 (if you don't like this policy, just declare the
6335 * encoding !)
6336 */
Daniel Veillard42596ad2001-05-22 16:57:14 +00006337 *len = 0;
Daniel Veillard61d80a22001-04-27 17:13:01 +00006338 XP_ERROR0(XPATH_ENCODING_ERROR);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006339}
6340
6341/**
Owen Taylor3473f882001-02-23 17:55:21 +00006342 * xmlXPathParseNCName:
6343 * @ctxt: the XPath Parser context
6344 *
6345 * parse an XML namespace non qualified name.
6346 *
6347 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
6348 *
6349 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
6350 * CombiningChar | Extender
6351 *
6352 * Returns the namespace name or NULL
6353 */
6354
6355xmlChar *
6356xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00006357 const xmlChar *in;
6358 xmlChar *ret;
6359 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006360
Daniel Veillard2156a562001-04-28 12:24:34 +00006361 /*
6362 * Accelerator for simple ASCII names
6363 */
6364 in = ctxt->cur;
6365 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6366 ((*in >= 0x41) && (*in <= 0x5A)) ||
6367 (*in == '_')) {
6368 in++;
6369 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6370 ((*in >= 0x41) && (*in <= 0x5A)) ||
6371 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard9a89a8a2001-06-27 11:13:35 +00006372 (*in == '_') || (*in == '.') ||
6373 (*in == '-'))
Daniel Veillard2156a562001-04-28 12:24:34 +00006374 in++;
6375 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
6376 (*in == '[') || (*in == ']') || (*in == ':') ||
6377 (*in == '@') || (*in == '*')) {
6378 count = in - ctxt->cur;
6379 if (count == 0)
6380 return(NULL);
6381 ret = xmlStrndup(ctxt->cur, count);
6382 ctxt->cur = in;
6383 return(ret);
6384 }
6385 }
6386 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00006387}
6388
Daniel Veillard2156a562001-04-28 12:24:34 +00006389
Owen Taylor3473f882001-02-23 17:55:21 +00006390/**
6391 * xmlXPathParseQName:
6392 * @ctxt: the XPath Parser context
6393 * @prefix: a xmlChar **
6394 *
6395 * parse an XML qualified name
6396 *
6397 * [NS 5] QName ::= (Prefix ':')? LocalPart
6398 *
6399 * [NS 6] Prefix ::= NCName
6400 *
6401 * [NS 7] LocalPart ::= NCName
6402 *
6403 * Returns the function returns the local part, and prefix is updated
6404 * to get the Prefix if any.
6405 */
6406
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006407static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006408xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
6409 xmlChar *ret = NULL;
6410
6411 *prefix = NULL;
6412 ret = xmlXPathParseNCName(ctxt);
6413 if (CUR == ':') {
6414 *prefix = ret;
6415 NEXT;
6416 ret = xmlXPathParseNCName(ctxt);
6417 }
6418 return(ret);
6419}
6420
6421/**
6422 * xmlXPathParseName:
6423 * @ctxt: the XPath Parser context
6424 *
6425 * parse an XML name
6426 *
6427 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6428 * CombiningChar | Extender
6429 *
6430 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6431 *
6432 * Returns the namespace name or NULL
6433 */
6434
6435xmlChar *
6436xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006437 const xmlChar *in;
6438 xmlChar *ret;
6439 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006440
Daniel Veillard61d80a22001-04-27 17:13:01 +00006441 /*
6442 * Accelerator for simple ASCII names
6443 */
6444 in = ctxt->cur;
6445 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6446 ((*in >= 0x41) && (*in <= 0x5A)) ||
6447 (*in == '_') || (*in == ':')) {
6448 in++;
6449 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6450 ((*in >= 0x41) && (*in <= 0x5A)) ||
6451 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard76d66f42001-05-16 21:05:17 +00006452 (*in == '_') || (*in == '-') ||
6453 (*in == ':') || (*in == '.'))
Daniel Veillard61d80a22001-04-27 17:13:01 +00006454 in++;
Daniel Veillard76d66f42001-05-16 21:05:17 +00006455 if ((*in > 0) && (*in < 0x80)) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006456 count = in - ctxt->cur;
6457 ret = xmlStrndup(ctxt->cur, count);
6458 ctxt->cur = in;
6459 return(ret);
6460 }
6461 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006462 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00006463}
6464
Daniel Veillard61d80a22001-04-27 17:13:01 +00006465static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00006466xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006467 xmlChar buf[XML_MAX_NAMELEN + 5];
6468 int len = 0, l;
6469 int c;
6470
6471 /*
6472 * Handler for more complex cases
6473 */
6474 c = CUR_CHAR(l);
6475 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00006476 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
6477 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00006478 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00006479 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006480 return(NULL);
6481 }
6482
6483 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
6484 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
6485 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006486 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006487 (IS_COMBINING(c)) ||
6488 (IS_EXTENDER(c)))) {
6489 COPY_BUF(l,buf,len,c);
6490 NEXTL(l);
6491 c = CUR_CHAR(l);
6492 if (len >= XML_MAX_NAMELEN) {
6493 /*
6494 * Okay someone managed to make a huge name, so he's ready to pay
6495 * for the processing speed.
6496 */
6497 xmlChar *buffer;
6498 int max = len * 2;
6499
6500 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
6501 if (buffer == NULL) {
6502 XP_ERROR0(XPATH_MEMORY_ERROR);
6503 }
6504 memcpy(buffer, buf, len);
6505 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
6506 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006507 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006508 (IS_COMBINING(c)) ||
6509 (IS_EXTENDER(c))) {
6510 if (len + 10 > max) {
6511 max *= 2;
6512 buffer = (xmlChar *) xmlRealloc(buffer,
6513 max * sizeof(xmlChar));
Daniel Veillard61d80a22001-04-27 17:13:01 +00006514 if (buffer == NULL) {
6515 XP_ERROR0(XPATH_MEMORY_ERROR);
6516 }
6517 }
6518 COPY_BUF(l,buffer,len,c);
6519 NEXTL(l);
6520 c = CUR_CHAR(l);
6521 }
6522 buffer[len] = 0;
6523 return(buffer);
6524 }
6525 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006526 if (len == 0)
6527 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006528 return(xmlStrndup(buf, len));
6529}
Owen Taylor3473f882001-02-23 17:55:21 +00006530/**
6531 * xmlXPathStringEvalNumber:
6532 * @str: A string to scan
6533 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00006534 * [30a] Float ::= Number ('e' Digits?)?
6535 *
Owen Taylor3473f882001-02-23 17:55:21 +00006536 * [30] Number ::= Digits ('.' Digits?)?
6537 * | '.' Digits
6538 * [31] Digits ::= [0-9]+
6539 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006540 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00006541 * In complement of the Number expression, this function also handles
6542 * negative values : '-' Number.
6543 *
6544 * Returns the double value.
6545 */
6546double
6547xmlXPathStringEvalNumber(const xmlChar *str) {
6548 const xmlChar *cur = str;
6549 double ret = 0.0;
6550 double mult = 1;
Daniel Veillardb06c6142001-08-27 14:26:30 +00006551 int ok = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006552 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006553 int exponent = 0;
6554 int is_exponent_negative = 0;
Daniel Veillardb06c6142001-08-27 14:26:30 +00006555#ifdef __GNUC__
6556 unsigned long tmp = 0;
6557#endif
Bjorn Reese70a9da52001-04-21 16:57:29 +00006558
Owen Taylor3473f882001-02-23 17:55:21 +00006559 while (IS_BLANK(*cur)) cur++;
6560 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
6561 return(xmlXPathNAN);
6562 }
6563 if (*cur == '-') {
6564 isneg = 1;
6565 cur++;
6566 }
Daniel Veillardb06c6142001-08-27 14:26:30 +00006567
6568#ifdef __GNUC__
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006569 /*
Daniel Veillardb06c6142001-08-27 14:26:30 +00006570 * tmp is a workaround against a gcc compiler bug
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006571 */
Owen Taylor3473f882001-02-23 17:55:21 +00006572 while ((*cur >= '0') && (*cur <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006573 tmp = tmp * 10 + (*cur - '0');
Owen Taylor3473f882001-02-23 17:55:21 +00006574 ok = 1;
6575 cur++;
6576 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006577 ret = (double) tmp;
Daniel Veillardb06c6142001-08-27 14:26:30 +00006578#else
6579 while ((*cur >= '0') && (*cur <= '9')) {
6580 ret = ret * 10 + (*cur - '0');
6581 ok = 1;
6582 cur++;
6583 }
6584#endif
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006585
Owen Taylor3473f882001-02-23 17:55:21 +00006586 if (*cur == '.') {
6587 cur++;
6588 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
6589 return(xmlXPathNAN);
6590 }
6591 while ((*cur >= '0') && (*cur <= '9')) {
6592 mult /= 10;
6593 ret = ret + (*cur - '0') * mult;
6594 cur++;
6595 }
6596 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006597 if ((*cur == 'e') || (*cur == 'E')) {
6598 cur++;
6599 if (*cur == '-') {
6600 is_exponent_negative = 1;
6601 cur++;
6602 }
6603 while ((*cur >= '0') && (*cur <= '9')) {
6604 exponent = exponent * 10 + (*cur - '0');
6605 cur++;
6606 }
6607 }
Owen Taylor3473f882001-02-23 17:55:21 +00006608 while (IS_BLANK(*cur)) cur++;
6609 if (*cur != 0) return(xmlXPathNAN);
6610 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006611 if (is_exponent_negative) exponent = -exponent;
6612 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00006613 return(ret);
6614}
6615
6616/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006617 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00006618 * @ctxt: the XPath Parser context
6619 *
6620 * [30] Number ::= Digits ('.' Digits?)?
6621 * | '.' Digits
6622 * [31] Digits ::= [0-9]+
6623 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006624 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00006625 *
6626 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006627static void
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006628xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
6629{
Owen Taylor3473f882001-02-23 17:55:21 +00006630 double ret = 0.0;
6631 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006632 int ok = 0, tmp = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006633 int exponent = 0;
6634 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006635
6636 CHECK_ERROR;
6637 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
6638 XP_ERROR(XPATH_NUMBER_ERROR);
6639 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006640 /*
6641 * Try to work around a gcc optimizer bug
6642 */
Owen Taylor3473f882001-02-23 17:55:21 +00006643 while ((CUR >= '0') && (CUR <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006644 tmp = tmp * 10 + (CUR - '0');
6645 ok = 1;
6646 NEXT;
Owen Taylor3473f882001-02-23 17:55:21 +00006647 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006648 ret = (double) tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006649 if (CUR == '.') {
6650 NEXT;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006651 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
6652 XP_ERROR(XPATH_NUMBER_ERROR);
6653 }
6654 while ((CUR >= '0') && (CUR <= '9')) {
6655 mult /= 10;
6656 ret = ret + (CUR - '0') * mult;
6657 NEXT;
6658 }
Owen Taylor3473f882001-02-23 17:55:21 +00006659 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006660 if ((CUR == 'e') || (CUR == 'E')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006661 NEXT;
6662 if (CUR == '-') {
6663 is_exponent_negative = 1;
6664 NEXT;
6665 }
6666 while ((CUR >= '0') && (CUR <= '9')) {
6667 exponent = exponent * 10 + (CUR - '0');
6668 NEXT;
6669 }
6670 if (is_exponent_negative)
6671 exponent = -exponent;
6672 ret *= pow(10.0, (double) exponent);
Bjorn Reese70a9da52001-04-21 16:57:29 +00006673 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006674 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006675 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006676}
6677
6678/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006679 * xmlXPathParseLiteral:
6680 * @ctxt: the XPath Parser context
6681 *
6682 * Parse a Literal
6683 *
6684 * [29] Literal ::= '"' [^"]* '"'
6685 * | "'" [^']* "'"
6686 *
6687 * Returns the value found or NULL in case of error
6688 */
6689static xmlChar *
6690xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
6691 const xmlChar *q;
6692 xmlChar *ret = NULL;
6693
6694 if (CUR == '"') {
6695 NEXT;
6696 q = CUR_PTR;
6697 while ((IS_CHAR(CUR)) && (CUR != '"'))
6698 NEXT;
6699 if (!IS_CHAR(CUR)) {
6700 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6701 } else {
6702 ret = xmlStrndup(q, CUR_PTR - q);
6703 NEXT;
6704 }
6705 } else if (CUR == '\'') {
6706 NEXT;
6707 q = CUR_PTR;
6708 while ((IS_CHAR(CUR)) && (CUR != '\''))
6709 NEXT;
6710 if (!IS_CHAR(CUR)) {
6711 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6712 } else {
6713 ret = xmlStrndup(q, CUR_PTR - q);
6714 NEXT;
6715 }
6716 } else {
6717 XP_ERROR0(XPATH_START_LITERAL_ERROR);
6718 }
6719 return(ret);
6720}
6721
6722/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006723 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00006724 * @ctxt: the XPath Parser context
6725 *
6726 * Parse a Literal and push it on the stack.
6727 *
6728 * [29] Literal ::= '"' [^"]* '"'
6729 * | "'" [^']* "'"
6730 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006731 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00006732 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006733static void
6734xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006735 const xmlChar *q;
6736 xmlChar *ret = NULL;
6737
6738 if (CUR == '"') {
6739 NEXT;
6740 q = CUR_PTR;
6741 while ((IS_CHAR(CUR)) && (CUR != '"'))
6742 NEXT;
6743 if (!IS_CHAR(CUR)) {
6744 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6745 } else {
6746 ret = xmlStrndup(q, CUR_PTR - q);
6747 NEXT;
6748 }
6749 } else if (CUR == '\'') {
6750 NEXT;
6751 q = CUR_PTR;
6752 while ((IS_CHAR(CUR)) && (CUR != '\''))
6753 NEXT;
6754 if (!IS_CHAR(CUR)) {
6755 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6756 } else {
6757 ret = xmlStrndup(q, CUR_PTR - q);
6758 NEXT;
6759 }
6760 } else {
6761 XP_ERROR(XPATH_START_LITERAL_ERROR);
6762 }
6763 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006764 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
6765 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006766 xmlFree(ret);
6767}
6768
6769/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006770 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00006771 * @ctxt: the XPath Parser context
6772 *
6773 * Parse a VariableReference, evaluate it and push it on the stack.
6774 *
6775 * The variable bindings consist of a mapping from variable names
6776 * to variable values. The value of a variable is an object, which
6777 * of any of the types that are possible for the value of an expression,
6778 * and may also be of additional types not specified here.
6779 *
6780 * Early evaluation is possible since:
6781 * The variable bindings [...] used to evaluate a subexpression are
6782 * always the same as those used to evaluate the containing expression.
6783 *
6784 * [36] VariableReference ::= '$' QName
6785 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006786static void
6787xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006788 xmlChar *name;
6789 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006790
6791 SKIP_BLANKS;
6792 if (CUR != '$') {
6793 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6794 }
6795 NEXT;
6796 name = xmlXPathParseQName(ctxt, &prefix);
6797 if (name == NULL) {
6798 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6799 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006800 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006801 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
6802 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006803 SKIP_BLANKS;
6804}
6805
6806/**
6807 * xmlXPathIsNodeType:
6808 * @ctxt: the XPath Parser context
6809 * @name: a name string
6810 *
6811 * Is the name given a NodeType one.
6812 *
6813 * [38] NodeType ::= 'comment'
6814 * | 'text'
6815 * | 'processing-instruction'
6816 * | 'node'
6817 *
6818 * Returns 1 if true 0 otherwise
6819 */
6820int
6821xmlXPathIsNodeType(const xmlChar *name) {
6822 if (name == NULL)
6823 return(0);
6824
6825 if (xmlStrEqual(name, BAD_CAST "comment"))
6826 return(1);
6827 if (xmlStrEqual(name, BAD_CAST "text"))
6828 return(1);
6829 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6830 return(1);
6831 if (xmlStrEqual(name, BAD_CAST "node"))
6832 return(1);
6833 return(0);
6834}
6835
6836/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006837 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00006838 * @ctxt: the XPath Parser context
6839 *
6840 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
6841 * [17] Argument ::= Expr
6842 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006843 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00006844 * pushed on the stack
6845 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006846static void
6847xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006848 xmlChar *name;
6849 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006850 int nbargs = 0;
6851
6852 name = xmlXPathParseQName(ctxt, &prefix);
6853 if (name == NULL) {
6854 XP_ERROR(XPATH_EXPR_ERROR);
6855 }
6856 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006857#ifdef DEBUG_EXPR
6858 if (prefix == NULL)
6859 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
6860 name);
6861 else
6862 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
6863 prefix, name);
6864#endif
6865
Owen Taylor3473f882001-02-23 17:55:21 +00006866 if (CUR != '(') {
6867 XP_ERROR(XPATH_EXPR_ERROR);
6868 }
6869 NEXT;
6870 SKIP_BLANKS;
6871
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006872 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006873 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006874 int op1 = ctxt->comp->last;
6875 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006876 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006877 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006878 nbargs++;
6879 if (CUR == ')') break;
6880 if (CUR != ',') {
6881 XP_ERROR(XPATH_EXPR_ERROR);
6882 }
6883 NEXT;
6884 SKIP_BLANKS;
6885 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006886 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
6887 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006888 NEXT;
6889 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006890}
6891
6892/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006893 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006894 * @ctxt: the XPath Parser context
6895 *
6896 * [15] PrimaryExpr ::= VariableReference
6897 * | '(' Expr ')'
6898 * | Literal
6899 * | Number
6900 * | FunctionCall
6901 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006902 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006903 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006904static void
6905xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006906 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006907 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006908 else if (CUR == '(') {
6909 NEXT;
6910 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006911 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006912 if (CUR != ')') {
6913 XP_ERROR(XPATH_EXPR_ERROR);
6914 }
6915 NEXT;
6916 SKIP_BLANKS;
6917 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006918 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006919 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006920 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006921 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006922 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006923 }
6924 SKIP_BLANKS;
6925}
6926
6927/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006928 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006929 * @ctxt: the XPath Parser context
6930 *
6931 * [20] FilterExpr ::= PrimaryExpr
6932 * | FilterExpr Predicate
6933 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006934 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006935 * Square brackets are used to filter expressions in the same way that
6936 * they are used in location paths. It is an error if the expression to
6937 * be filtered does not evaluate to a node-set. The context node list
6938 * used for evaluating the expression in square brackets is the node-set
6939 * to be filtered listed in document order.
6940 */
6941
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006942static void
6943xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
6944 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006945 CHECK_ERROR;
6946 SKIP_BLANKS;
6947
6948 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006949 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00006950 SKIP_BLANKS;
6951 }
6952
6953
6954}
6955
6956/**
6957 * xmlXPathScanName:
6958 * @ctxt: the XPath Parser context
6959 *
6960 * Trickery: parse an XML name but without consuming the input flow
6961 * Needed to avoid insanity in the parser state.
6962 *
6963 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6964 * CombiningChar | Extender
6965 *
6966 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6967 *
6968 * [6] Names ::= Name (S Name)*
6969 *
6970 * Returns the Name parsed or NULL
6971 */
6972
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006973static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006974xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
6975 xmlChar buf[XML_MAX_NAMELEN];
6976 int len = 0;
6977
6978 SKIP_BLANKS;
6979 if (!IS_LETTER(CUR) && (CUR != '_') &&
6980 (CUR != ':')) {
6981 return(NULL);
6982 }
6983
6984 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
6985 (NXT(len) == '.') || (NXT(len) == '-') ||
6986 (NXT(len) == '_') || (NXT(len) == ':') ||
6987 (IS_COMBINING(NXT(len))) ||
6988 (IS_EXTENDER(NXT(len)))) {
6989 buf[len] = NXT(len);
6990 len++;
6991 if (len >= XML_MAX_NAMELEN) {
6992 xmlGenericError(xmlGenericErrorContext,
6993 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
6994 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
6995 (NXT(len) == '.') || (NXT(len) == '-') ||
6996 (NXT(len) == '_') || (NXT(len) == ':') ||
6997 (IS_COMBINING(NXT(len))) ||
6998 (IS_EXTENDER(NXT(len))))
6999 len++;
7000 break;
7001 }
7002 }
7003 return(xmlStrndup(buf, len));
7004}
7005
7006/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007007 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007008 * @ctxt: the XPath Parser context
7009 *
7010 * [19] PathExpr ::= LocationPath
7011 * | FilterExpr
7012 * | FilterExpr '/' RelativeLocationPath
7013 * | FilterExpr '//' RelativeLocationPath
7014 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007015 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007016 * The / operator and // operators combine an arbitrary expression
7017 * and a relative location path. It is an error if the expression
7018 * does not evaluate to a node-set.
7019 * The / operator does composition in the same way as when / is
7020 * used in a location path. As in location paths, // is short for
7021 * /descendant-or-self::node()/.
7022 */
7023
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007024static void
7025xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007026 int lc = 1; /* Should we branch to LocationPath ? */
7027 xmlChar *name = NULL; /* we may have to preparse a name to find out */
7028
7029 SKIP_BLANKS;
7030 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
7031 (CUR == '\'') || (CUR == '"')) {
7032 lc = 0;
7033 } else if (CUR == '*') {
7034 /* relative or absolute location path */
7035 lc = 1;
7036 } else if (CUR == '/') {
7037 /* relative or absolute location path */
7038 lc = 1;
7039 } else if (CUR == '@') {
7040 /* relative abbreviated attribute location path */
7041 lc = 1;
7042 } else if (CUR == '.') {
7043 /* relative abbreviated attribute location path */
7044 lc = 1;
7045 } else {
7046 /*
7047 * Problem is finding if we have a name here whether it's:
7048 * - a nodetype
7049 * - a function call in which case it's followed by '('
7050 * - an axis in which case it's followed by ':'
7051 * - a element name
7052 * We do an a priori analysis here rather than having to
7053 * maintain parsed token content through the recursive function
7054 * calls. This looks uglier but makes the code quite easier to
7055 * read/write/debug.
7056 */
7057 SKIP_BLANKS;
7058 name = xmlXPathScanName(ctxt);
7059 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
7060#ifdef DEBUG_STEP
7061 xmlGenericError(xmlGenericErrorContext,
7062 "PathExpr: Axis\n");
7063#endif
7064 lc = 1;
7065 xmlFree(name);
7066 } else if (name != NULL) {
7067 int len =xmlStrlen(name);
7068 int blank = 0;
7069
7070
7071 while (NXT(len) != 0) {
7072 if (NXT(len) == '/') {
7073 /* element name */
7074#ifdef DEBUG_STEP
7075 xmlGenericError(xmlGenericErrorContext,
7076 "PathExpr: AbbrRelLocation\n");
7077#endif
7078 lc = 1;
7079 break;
7080 } else if (IS_BLANK(NXT(len))) {
7081 /* skip to next */
7082 blank = 1;
7083 } else if (NXT(len) == ':') {
7084#ifdef DEBUG_STEP
7085 xmlGenericError(xmlGenericErrorContext,
7086 "PathExpr: AbbrRelLocation\n");
7087#endif
7088 lc = 1;
7089 break;
7090 } else if ((NXT(len) == '(')) {
7091 /* Note Type or Function */
7092 if (xmlXPathIsNodeType(name)) {
7093#ifdef DEBUG_STEP
7094 xmlGenericError(xmlGenericErrorContext,
7095 "PathExpr: Type search\n");
7096#endif
7097 lc = 1;
7098 } else {
7099#ifdef DEBUG_STEP
7100 xmlGenericError(xmlGenericErrorContext,
7101 "PathExpr: function call\n");
7102#endif
7103 lc = 0;
7104 }
7105 break;
7106 } else if ((NXT(len) == '[')) {
7107 /* element name */
7108#ifdef DEBUG_STEP
7109 xmlGenericError(xmlGenericErrorContext,
7110 "PathExpr: AbbrRelLocation\n");
7111#endif
7112 lc = 1;
7113 break;
7114 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
7115 (NXT(len) == '=')) {
7116 lc = 1;
7117 break;
7118 } else {
7119 lc = 1;
7120 break;
7121 }
7122 len++;
7123 }
7124 if (NXT(len) == 0) {
7125#ifdef DEBUG_STEP
7126 xmlGenericError(xmlGenericErrorContext,
7127 "PathExpr: AbbrRelLocation\n");
7128#endif
7129 /* element name */
7130 lc = 1;
7131 }
7132 xmlFree(name);
7133 } else {
7134 /* make sure all cases are covered explicitely */
7135 XP_ERROR(XPATH_EXPR_ERROR);
7136 }
7137 }
7138
7139 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007140 if (CUR == '/') {
7141 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
7142 } else {
7143 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007144 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007145 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007146 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007147 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007148 CHECK_ERROR;
7149 if ((CUR == '/') && (NXT(1) == '/')) {
7150 SKIP(2);
7151 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007152
7153 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7154 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
7155 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
7156
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007157 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007158 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007159 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007160 }
7161 }
7162 SKIP_BLANKS;
7163}
7164
7165/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007166 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007167 * @ctxt: the XPath Parser context
7168 *
7169 * [18] UnionExpr ::= PathExpr
7170 * | UnionExpr '|' PathExpr
7171 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007172 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007173 */
7174
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007175static void
7176xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
7177 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007178 CHECK_ERROR;
7179 SKIP_BLANKS;
7180 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007181 int op1 = ctxt->comp->last;
7182 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007183
7184 NEXT;
7185 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007186 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007187
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007188 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
7189
Owen Taylor3473f882001-02-23 17:55:21 +00007190 SKIP_BLANKS;
7191 }
Owen Taylor3473f882001-02-23 17:55:21 +00007192}
7193
7194/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007195 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007196 * @ctxt: the XPath Parser context
7197 *
7198 * [27] UnaryExpr ::= UnionExpr
7199 * | '-' UnaryExpr
7200 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007201 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007202 */
7203
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007204static void
7205xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007206 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007207 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007208
7209 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00007210 while (CUR == '-') {
7211 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007212 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007213 NEXT;
7214 SKIP_BLANKS;
7215 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007216
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007217 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007218 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007219 if (found) {
7220 if (minus)
7221 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
7222 else
7223 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007224 }
7225}
7226
7227/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007228 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007229 * @ctxt: the XPath Parser context
7230 *
7231 * [26] MultiplicativeExpr ::= UnaryExpr
7232 * | MultiplicativeExpr MultiplyOperator UnaryExpr
7233 * | MultiplicativeExpr 'div' UnaryExpr
7234 * | MultiplicativeExpr 'mod' UnaryExpr
7235 * [34] MultiplyOperator ::= '*'
7236 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007237 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007238 */
7239
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007240static void
7241xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
7242 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007243 CHECK_ERROR;
7244 SKIP_BLANKS;
7245 while ((CUR == '*') ||
7246 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
7247 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
7248 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007249 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007250
7251 if (CUR == '*') {
7252 op = 0;
7253 NEXT;
7254 } else if (CUR == 'd') {
7255 op = 1;
7256 SKIP(3);
7257 } else if (CUR == 'm') {
7258 op = 2;
7259 SKIP(3);
7260 }
7261 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007262 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007263 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007264 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007265 SKIP_BLANKS;
7266 }
7267}
7268
7269/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007270 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007271 * @ctxt: the XPath Parser context
7272 *
7273 * [25] AdditiveExpr ::= MultiplicativeExpr
7274 * | AdditiveExpr '+' MultiplicativeExpr
7275 * | AdditiveExpr '-' MultiplicativeExpr
7276 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007277 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007278 */
7279
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007280static void
7281xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007282
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007283 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007284 CHECK_ERROR;
7285 SKIP_BLANKS;
7286 while ((CUR == '+') || (CUR == '-')) {
7287 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007288 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007289
7290 if (CUR == '+') plus = 1;
7291 else plus = 0;
7292 NEXT;
7293 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007294 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007295 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007296 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007297 SKIP_BLANKS;
7298 }
7299}
7300
7301/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007302 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007303 * @ctxt: the XPath Parser context
7304 *
7305 * [24] RelationalExpr ::= AdditiveExpr
7306 * | RelationalExpr '<' AdditiveExpr
7307 * | RelationalExpr '>' AdditiveExpr
7308 * | RelationalExpr '<=' AdditiveExpr
7309 * | RelationalExpr '>=' AdditiveExpr
7310 *
7311 * A <= B > C is allowed ? Answer from James, yes with
7312 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
7313 * which is basically what got implemented.
7314 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007315 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00007316 * on the stack
7317 */
7318
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007319static void
7320xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
7321 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007322 CHECK_ERROR;
7323 SKIP_BLANKS;
7324 while ((CUR == '<') ||
7325 (CUR == '>') ||
7326 ((CUR == '<') && (NXT(1) == '=')) ||
7327 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007328 int inf, strict;
7329 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007330
7331 if (CUR == '<') inf = 1;
7332 else inf = 0;
7333 if (NXT(1) == '=') strict = 0;
7334 else strict = 1;
7335 NEXT;
7336 if (!strict) NEXT;
7337 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007338 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007339 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007340 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00007341 SKIP_BLANKS;
7342 }
7343}
7344
7345/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007346 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007347 * @ctxt: the XPath Parser context
7348 *
7349 * [23] EqualityExpr ::= RelationalExpr
7350 * | EqualityExpr '=' RelationalExpr
7351 * | EqualityExpr '!=' RelationalExpr
7352 *
7353 * A != B != C is allowed ? Answer from James, yes with
7354 * (RelationalExpr = RelationalExpr) = RelationalExpr
7355 * (RelationalExpr != RelationalExpr) != RelationalExpr
7356 * which is basically what got implemented.
7357 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007358 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007359 *
7360 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007361static void
7362xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
7363 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007364 CHECK_ERROR;
7365 SKIP_BLANKS;
7366 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007367 int eq;
7368 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007369
7370 if (CUR == '=') eq = 1;
7371 else eq = 0;
7372 NEXT;
7373 if (!eq) NEXT;
7374 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007375 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007376 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007377 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007378 SKIP_BLANKS;
7379 }
7380}
7381
7382/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007383 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007384 * @ctxt: the XPath Parser context
7385 *
7386 * [22] AndExpr ::= EqualityExpr
7387 * | AndExpr 'and' EqualityExpr
7388 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007389 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007390 *
7391 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007392static void
7393xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
7394 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007395 CHECK_ERROR;
7396 SKIP_BLANKS;
7397 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007398 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007399 SKIP(3);
7400 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007401 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007402 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007403 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007404 SKIP_BLANKS;
7405 }
7406}
7407
7408/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007409 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007410 * @ctxt: the XPath Parser context
7411 *
7412 * [14] Expr ::= OrExpr
7413 * [21] OrExpr ::= AndExpr
7414 * | OrExpr 'or' AndExpr
7415 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007416 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00007417 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007418static void
7419xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
7420 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007421 CHECK_ERROR;
7422 SKIP_BLANKS;
7423 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007424 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007425 SKIP(2);
7426 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007427 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007428 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007429 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
7430 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00007431 SKIP_BLANKS;
7432 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007433 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
7434 /* more ops could be optimized too */
7435 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
7436 }
Owen Taylor3473f882001-02-23 17:55:21 +00007437}
7438
7439/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007440 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00007441 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007442 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00007443 *
7444 * [8] Predicate ::= '[' PredicateExpr ']'
7445 * [9] PredicateExpr ::= Expr
7446 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007447 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00007448 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007449static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007450xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007451 int op1 = ctxt->comp->last;
7452
7453 SKIP_BLANKS;
7454 if (CUR != '[') {
7455 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7456 }
7457 NEXT;
7458 SKIP_BLANKS;
7459
7460 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007461 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007462 CHECK_ERROR;
7463
7464 if (CUR != ']') {
7465 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7466 }
7467
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007468 if (filter)
7469 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
7470 else
7471 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007472
7473 NEXT;
7474 SKIP_BLANKS;
7475}
7476
7477/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007478 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00007479 * @ctxt: the XPath Parser context
7480 * @test: pointer to a xmlXPathTestVal
7481 * @type: pointer to a xmlXPathTypeVal
7482 * @prefix: placeholder for a possible name prefix
7483 *
7484 * [7] NodeTest ::= NameTest
7485 * | NodeType '(' ')'
7486 * | 'processing-instruction' '(' Literal ')'
7487 *
7488 * [37] NameTest ::= '*'
7489 * | NCName ':' '*'
7490 * | QName
7491 * [38] NodeType ::= 'comment'
7492 * | 'text'
7493 * | 'processing-instruction'
7494 * | 'node'
7495 *
7496 * Returns the name found and update @test, @type and @prefix appropriately
7497 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007498static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007499xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
7500 xmlXPathTypeVal *type, const xmlChar **prefix,
7501 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00007502 int blanks;
7503
7504 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
7505 STRANGE;
7506 return(NULL);
7507 }
7508 *type = 0;
7509 *test = 0;
7510 *prefix = NULL;
7511 SKIP_BLANKS;
7512
7513 if ((name == NULL) && (CUR == '*')) {
7514 /*
7515 * All elements
7516 */
7517 NEXT;
7518 *test = NODE_TEST_ALL;
7519 return(NULL);
7520 }
7521
7522 if (name == NULL)
7523 name = xmlXPathParseNCName(ctxt);
7524 if (name == NULL) {
7525 XP_ERROR0(XPATH_EXPR_ERROR);
7526 }
7527
7528 blanks = IS_BLANK(CUR);
7529 SKIP_BLANKS;
7530 if (CUR == '(') {
7531 NEXT;
7532 /*
7533 * NodeType or PI search
7534 */
7535 if (xmlStrEqual(name, BAD_CAST "comment"))
7536 *type = NODE_TYPE_COMMENT;
7537 else if (xmlStrEqual(name, BAD_CAST "node"))
7538 *type = NODE_TYPE_NODE;
7539 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
7540 *type = NODE_TYPE_PI;
7541 else if (xmlStrEqual(name, BAD_CAST "text"))
7542 *type = NODE_TYPE_TEXT;
7543 else {
7544 if (name != NULL)
7545 xmlFree(name);
7546 XP_ERROR0(XPATH_EXPR_ERROR);
7547 }
7548
7549 *test = NODE_TEST_TYPE;
7550
7551 SKIP_BLANKS;
7552 if (*type == NODE_TYPE_PI) {
7553 /*
7554 * Specific case: search a PI by name.
7555 */
Owen Taylor3473f882001-02-23 17:55:21 +00007556 if (name != NULL)
7557 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00007558 name = NULL;
7559 if (CUR != ')') {
7560 name = xmlXPathParseLiteral(ctxt);
7561 CHECK_ERROR 0;
7562 SKIP_BLANKS;
7563 }
Owen Taylor3473f882001-02-23 17:55:21 +00007564 }
7565 if (CUR != ')') {
7566 if (name != NULL)
7567 xmlFree(name);
7568 XP_ERROR0(XPATH_UNCLOSED_ERROR);
7569 }
7570 NEXT;
7571 return(name);
7572 }
7573 *test = NODE_TEST_NAME;
7574 if ((!blanks) && (CUR == ':')) {
7575 NEXT;
7576
7577 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007578 * Since currently the parser context don't have a
7579 * namespace list associated:
7580 * The namespace name for this prefix can be computed
7581 * only at evaluation time. The compilation is done
7582 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00007583 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007584#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00007585 *prefix = xmlXPathNsLookup(ctxt->context, name);
7586 if (name != NULL)
7587 xmlFree(name);
7588 if (*prefix == NULL) {
7589 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
7590 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007591#else
7592 *prefix = name;
7593#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007594
7595 if (CUR == '*') {
7596 /*
7597 * All elements
7598 */
7599 NEXT;
7600 *test = NODE_TEST_ALL;
7601 return(NULL);
7602 }
7603
7604 name = xmlXPathParseNCName(ctxt);
7605 if (name == NULL) {
7606 XP_ERROR0(XPATH_EXPR_ERROR);
7607 }
7608 }
7609 return(name);
7610}
7611
7612/**
7613 * xmlXPathIsAxisName:
7614 * @name: a preparsed name token
7615 *
7616 * [6] AxisName ::= 'ancestor'
7617 * | 'ancestor-or-self'
7618 * | 'attribute'
7619 * | 'child'
7620 * | 'descendant'
7621 * | 'descendant-or-self'
7622 * | 'following'
7623 * | 'following-sibling'
7624 * | 'namespace'
7625 * | 'parent'
7626 * | 'preceding'
7627 * | 'preceding-sibling'
7628 * | 'self'
7629 *
7630 * Returns the axis or 0
7631 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007632static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00007633xmlXPathIsAxisName(const xmlChar *name) {
7634 xmlXPathAxisVal ret = 0;
7635 switch (name[0]) {
7636 case 'a':
7637 if (xmlStrEqual(name, BAD_CAST "ancestor"))
7638 ret = AXIS_ANCESTOR;
7639 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
7640 ret = AXIS_ANCESTOR_OR_SELF;
7641 if (xmlStrEqual(name, BAD_CAST "attribute"))
7642 ret = AXIS_ATTRIBUTE;
7643 break;
7644 case 'c':
7645 if (xmlStrEqual(name, BAD_CAST "child"))
7646 ret = AXIS_CHILD;
7647 break;
7648 case 'd':
7649 if (xmlStrEqual(name, BAD_CAST "descendant"))
7650 ret = AXIS_DESCENDANT;
7651 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
7652 ret = AXIS_DESCENDANT_OR_SELF;
7653 break;
7654 case 'f':
7655 if (xmlStrEqual(name, BAD_CAST "following"))
7656 ret = AXIS_FOLLOWING;
7657 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
7658 ret = AXIS_FOLLOWING_SIBLING;
7659 break;
7660 case 'n':
7661 if (xmlStrEqual(name, BAD_CAST "namespace"))
7662 ret = AXIS_NAMESPACE;
7663 break;
7664 case 'p':
7665 if (xmlStrEqual(name, BAD_CAST "parent"))
7666 ret = AXIS_PARENT;
7667 if (xmlStrEqual(name, BAD_CAST "preceding"))
7668 ret = AXIS_PRECEDING;
7669 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
7670 ret = AXIS_PRECEDING_SIBLING;
7671 break;
7672 case 's':
7673 if (xmlStrEqual(name, BAD_CAST "self"))
7674 ret = AXIS_SELF;
7675 break;
7676 }
7677 return(ret);
7678}
7679
7680/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007681 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00007682 * @ctxt: the XPath Parser context
7683 *
7684 * [4] Step ::= AxisSpecifier NodeTest Predicate*
7685 * | AbbreviatedStep
7686 *
7687 * [12] AbbreviatedStep ::= '.' | '..'
7688 *
7689 * [5] AxisSpecifier ::= AxisName '::'
7690 * | AbbreviatedAxisSpecifier
7691 *
7692 * [13] AbbreviatedAxisSpecifier ::= '@'?
7693 *
7694 * Modified for XPtr range support as:
7695 *
7696 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
7697 * | AbbreviatedStep
7698 * | 'range-to' '(' Expr ')' Predicate*
7699 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007700 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00007701 * A location step of . is short for self::node(). This is
7702 * particularly useful in conjunction with //. For example, the
7703 * location path .//para is short for
7704 * self::node()/descendant-or-self::node()/child::para
7705 * and so will select all para descendant elements of the context
7706 * node.
7707 * Similarly, a location step of .. is short for parent::node().
7708 * For example, ../title is short for parent::node()/child::title
7709 * and so will select the title children of the parent of the context
7710 * node.
7711 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007712static void
7713xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007714#ifdef LIBXML_XPTR_ENABLED
7715 int rangeto = 0;
7716 int op2 = -1;
7717#endif
7718
Owen Taylor3473f882001-02-23 17:55:21 +00007719 SKIP_BLANKS;
7720 if ((CUR == '.') && (NXT(1) == '.')) {
7721 SKIP(2);
7722 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007723 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
7724 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007725 } else if (CUR == '.') {
7726 NEXT;
7727 SKIP_BLANKS;
7728 } else {
7729 xmlChar *name = NULL;
7730 const xmlChar *prefix = NULL;
7731 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007732 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007733 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007734 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00007735
7736 /*
7737 * The modification needed for XPointer change to the production
7738 */
7739#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007740 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00007741 name = xmlXPathParseNCName(ctxt);
7742 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007743 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007744 xmlFree(name);
7745 SKIP_BLANKS;
7746 if (CUR != '(') {
7747 XP_ERROR(XPATH_EXPR_ERROR);
7748 }
7749 NEXT;
7750 SKIP_BLANKS;
7751
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007752 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007753 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00007754 CHECK_ERROR;
7755
7756 SKIP_BLANKS;
7757 if (CUR != ')') {
7758 XP_ERROR(XPATH_EXPR_ERROR);
7759 }
7760 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007761 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007762 goto eval_predicates;
7763 }
7764 }
7765#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00007766 if (CUR == '*') {
7767 axis = AXIS_CHILD;
7768 } else {
7769 if (name == NULL)
7770 name = xmlXPathParseNCName(ctxt);
7771 if (name != NULL) {
7772 axis = xmlXPathIsAxisName(name);
7773 if (axis != 0) {
7774 SKIP_BLANKS;
7775 if ((CUR == ':') && (NXT(1) == ':')) {
7776 SKIP(2);
7777 xmlFree(name);
7778 name = NULL;
7779 } else {
7780 /* an element name can conflict with an axis one :-\ */
7781 axis = AXIS_CHILD;
7782 }
Owen Taylor3473f882001-02-23 17:55:21 +00007783 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00007784 axis = AXIS_CHILD;
7785 }
Daniel Veillard2156a562001-04-28 12:24:34 +00007786 } else if (CUR == '@') {
7787 NEXT;
7788 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00007789 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00007790 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00007791 }
Owen Taylor3473f882001-02-23 17:55:21 +00007792 }
7793
7794 CHECK_ERROR;
7795
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007796 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00007797 if (test == 0)
7798 return;
7799
7800#ifdef DEBUG_STEP
7801 xmlGenericError(xmlGenericErrorContext,
7802 "Basis : computing new set\n");
7803#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007804
Owen Taylor3473f882001-02-23 17:55:21 +00007805#ifdef DEBUG_STEP
7806 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007807 if (ctxt->value == NULL)
7808 xmlGenericError(xmlGenericErrorContext, "no value\n");
7809 else if (ctxt->value->nodesetval == NULL)
7810 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7811 else
7812 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007813#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007814
7815eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007816 op1 = ctxt->comp->last;
7817 ctxt->comp->last = -1;
7818
Owen Taylor3473f882001-02-23 17:55:21 +00007819 SKIP_BLANKS;
7820 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007821 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007822 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007823
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007824#ifdef LIBXML_XPTR_ENABLED
7825 if (rangeto) {
7826 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
7827 } else
7828#endif
7829 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
7830 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007831
Owen Taylor3473f882001-02-23 17:55:21 +00007832 }
7833#ifdef DEBUG_STEP
7834 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007835 if (ctxt->value == NULL)
7836 xmlGenericError(xmlGenericErrorContext, "no value\n");
7837 else if (ctxt->value->nodesetval == NULL)
7838 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7839 else
7840 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
7841 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007842#endif
7843}
7844
7845/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007846 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007847 * @ctxt: the XPath Parser context
7848 *
7849 * [3] RelativeLocationPath ::= Step
7850 * | RelativeLocationPath '/' Step
7851 * | AbbreviatedRelativeLocationPath
7852 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
7853 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007854 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00007855 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007856static void
Owen Taylor3473f882001-02-23 17:55:21 +00007857#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007858xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007859#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007860xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007861#endif
7862(xmlXPathParserContextPtr ctxt) {
7863 SKIP_BLANKS;
7864 if ((CUR == '/') && (NXT(1) == '/')) {
7865 SKIP(2);
7866 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007867 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7868 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007869 } else if (CUR == '/') {
7870 NEXT;
7871 SKIP_BLANKS;
7872 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007873 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007874 SKIP_BLANKS;
7875 while (CUR == '/') {
7876 if ((CUR == '/') && (NXT(1) == '/')) {
7877 SKIP(2);
7878 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007879 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00007880 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007881 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007882 } else if (CUR == '/') {
7883 NEXT;
7884 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007885 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007886 }
7887 SKIP_BLANKS;
7888 }
7889}
7890
7891/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007892 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007893 * @ctxt: the XPath Parser context
7894 *
7895 * [1] LocationPath ::= RelativeLocationPath
7896 * | AbsoluteLocationPath
7897 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
7898 * | AbbreviatedAbsoluteLocationPath
7899 * [10] AbbreviatedAbsoluteLocationPath ::=
7900 * '//' RelativeLocationPath
7901 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007902 * Compile a location path
7903 *
Owen Taylor3473f882001-02-23 17:55:21 +00007904 * // is short for /descendant-or-self::node()/. For example,
7905 * //para is short for /descendant-or-self::node()/child::para and
7906 * so will select any para element in the document (even a para element
7907 * that is a document element will be selected by //para since the
7908 * document element node is a child of the root node); div//para is
7909 * short for div/descendant-or-self::node()/child::para and so will
7910 * select all para descendants of div children.
7911 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007912static void
7913xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007914 SKIP_BLANKS;
7915 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007916 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007917 } else {
7918 while (CUR == '/') {
7919 if ((CUR == '/') && (NXT(1) == '/')) {
7920 SKIP(2);
7921 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007922 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7923 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007924 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007925 } else if (CUR == '/') {
7926 NEXT;
Daniel Veillard608ad072001-06-14 08:32:28 +00007927 SKIP_BLANKS;
7928 if ((CUR != 0 ) &&
7929 ((IS_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
7930 (CUR == '@') || (CUR == '*')))
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007931 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007932 }
7933 }
7934 }
7935}
7936
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007937/************************************************************************
7938 * *
7939 * XPath precompiled expression evaluation *
7940 * *
7941 ************************************************************************/
7942
Daniel Veillardf06307e2001-07-03 10:35:50 +00007943static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007944xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
7945
7946/**
7947 * xmlXPathNodeCollectAndTest:
7948 * @ctxt: the XPath Parser context
7949 * @op: the XPath precompiled step operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00007950 * @first: pointer to the first element in document order
7951 * @last: pointer to the last element in document order
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007952 *
7953 * This is the function implementing a step: based on the current list
7954 * of nodes, it builds up a new list, looking at all nodes under that
7955 * axis and selecting them it also do the predicate filtering
7956 *
7957 * Pushes the new NodeSet resulting from the search.
Daniel Veillardf06307e2001-07-03 10:35:50 +00007958 *
7959 * Returns the number of node traversed
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007960 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00007961static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007962xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
Daniel Veillardf06307e2001-07-03 10:35:50 +00007963 xmlXPathStepOpPtr op,
7964 xmlNodePtr * first, xmlNodePtr * last)
7965{
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007966 xmlXPathAxisVal axis = op->value;
7967 xmlXPathTestVal test = op->value2;
7968 xmlXPathTypeVal type = op->value3;
7969 const xmlChar *prefix = op->value4;
7970 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00007971 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007972
7973#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00007974 int n = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007975#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00007976 int i, t = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007977 xmlNodeSetPtr ret, list;
7978 xmlXPathTraversalFunction next = NULL;
Daniel Veillardf06307e2001-07-03 10:35:50 +00007979 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007980 xmlNodePtr cur = NULL;
7981 xmlXPathObjectPtr obj;
7982 xmlNodeSetPtr nodelist;
7983 xmlNodePtr tmp;
7984
Daniel Veillardf06307e2001-07-03 10:35:50 +00007985 CHECK_TYPE0(XPATH_NODESET);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007986 obj = valuePop(ctxt);
7987 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00007988 if (prefix != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00007989 URI = xmlXPathNsLookup(ctxt->context, prefix);
7990 if (URI == NULL)
7991 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
Daniel Veillarde043ee12001-04-16 14:08:07 +00007992 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007993#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00007994 xmlGenericError(xmlGenericErrorContext, "new step : ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007995#endif
7996 switch (axis) {
7997 case AXIS_ANCESTOR:
7998#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00007999 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008000#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008001 first = NULL;
8002 next = xmlXPathNextAncestor;
8003 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008004 case AXIS_ANCESTOR_OR_SELF:
8005#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008006 xmlGenericError(xmlGenericErrorContext,
8007 "axis 'ancestors-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008008#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008009 first = NULL;
8010 next = xmlXPathNextAncestorOrSelf;
8011 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008012 case AXIS_ATTRIBUTE:
8013#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008014 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008015#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008016 first = NULL;
8017 last = NULL;
8018 next = xmlXPathNextAttribute;
8019 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008020 case AXIS_CHILD:
8021#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008022 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008023#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008024 last = NULL;
8025 next = xmlXPathNextChild;
8026 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008027 case AXIS_DESCENDANT:
8028#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008029 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008030#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008031 last = NULL;
8032 next = xmlXPathNextDescendant;
8033 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008034 case AXIS_DESCENDANT_OR_SELF:
8035#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008036 xmlGenericError(xmlGenericErrorContext,
8037 "axis 'descendant-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008038#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008039 last = NULL;
8040 next = xmlXPathNextDescendantOrSelf;
8041 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008042 case AXIS_FOLLOWING:
8043#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008044 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008045#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008046 last = NULL;
8047 next = xmlXPathNextFollowing;
8048 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008049 case AXIS_FOLLOWING_SIBLING:
8050#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008051 xmlGenericError(xmlGenericErrorContext,
8052 "axis 'following-siblings' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008053#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008054 last = NULL;
8055 next = xmlXPathNextFollowingSibling;
8056 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008057 case AXIS_NAMESPACE:
8058#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008059 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008060#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008061 first = NULL;
8062 last = NULL;
8063 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8064 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008065 case AXIS_PARENT:
8066#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008067 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008068#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008069 first = NULL;
8070 next = xmlXPathNextParent;
8071 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008072 case AXIS_PRECEDING:
8073#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008074 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008075#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008076 first = NULL;
8077 next = xmlXPathNextPrecedingInternal;
8078 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008079 case AXIS_PRECEDING_SIBLING:
8080#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008081 xmlGenericError(xmlGenericErrorContext,
8082 "axis 'preceding-sibling' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008083#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008084 first = NULL;
8085 next = xmlXPathNextPrecedingSibling;
8086 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008087 case AXIS_SELF:
8088#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008089 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008090#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008091 first = NULL;
8092 last = NULL;
8093 next = xmlXPathNextSelf;
8094 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008095 }
8096 if (next == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00008097 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008098
8099 nodelist = obj->nodesetval;
8100 if (nodelist == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008101 xmlXPathFreeObject(obj);
8102 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8103 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008104 }
8105 addNode = xmlXPathNodeSetAddUnique;
8106 ret = NULL;
8107#ifdef DEBUG_STEP
8108 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008109 " context contains %d nodes\n", nodelist->nodeNr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008110 switch (test) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008111 case NODE_TEST_NONE:
8112 xmlGenericError(xmlGenericErrorContext,
8113 " searching for none !!!\n");
8114 break;
8115 case NODE_TEST_TYPE:
8116 xmlGenericError(xmlGenericErrorContext,
8117 " searching for type %d\n", type);
8118 break;
8119 case NODE_TEST_PI:
8120 xmlGenericError(xmlGenericErrorContext,
8121 " searching for PI !!!\n");
8122 break;
8123 case NODE_TEST_ALL:
8124 xmlGenericError(xmlGenericErrorContext,
8125 " searching for *\n");
8126 break;
8127 case NODE_TEST_NS:
8128 xmlGenericError(xmlGenericErrorContext,
8129 " searching for namespace %s\n",
8130 prefix);
8131 break;
8132 case NODE_TEST_NAME:
8133 xmlGenericError(xmlGenericErrorContext,
8134 " searching for name %s\n", name);
8135 if (prefix != NULL)
8136 xmlGenericError(xmlGenericErrorContext,
8137 " with namespace %s\n", prefix);
8138 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008139 }
8140 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8141#endif
8142 /*
8143 * 2.3 Node Tests
8144 * - For the attribute axis, the principal node type is attribute.
8145 * - For the namespace axis, the principal node type is namespace.
8146 * - For other axes, the principal node type is element.
8147 *
8148 * A node test * is true for any node of the
8149 * principal node type. For example, child::* willi
8150 * select all element children of the context node
8151 */
8152 tmp = ctxt->context->node;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008153 for (i = 0; i < nodelist->nodeNr; i++) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008154 ctxt->context->node = nodelist->nodeTab[i];
8155
Daniel Veillardf06307e2001-07-03 10:35:50 +00008156 cur = NULL;
8157 list = xmlXPathNodeSetCreate(NULL);
8158 do {
8159 cur = next(ctxt, cur);
8160 if (cur == NULL)
8161 break;
8162 if ((first != NULL) && (*first == cur))
8163 break;
8164 if (((t % 256) == 0) &&
8165 (first != NULL) && (*first != NULL) &&
8166 (xmlXPathCmpNodes(*first, cur) >= 0))
8167 break;
8168 if ((last != NULL) && (*last == cur))
8169 break;
8170 if (((t % 256) == 0) &&
8171 (last != NULL) && (*last != NULL) &&
8172 (xmlXPathCmpNodes(cur, *last) >= 0))
8173 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008174 t++;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008175#ifdef DEBUG_STEP
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008176 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
8177#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008178 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008179 case NODE_TEST_NONE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008180 ctxt->context->node = tmp;
8181 STRANGE return(t);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008182 case NODE_TEST_TYPE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008183 if ((cur->type == type) ||
8184 ((type == NODE_TYPE_NODE) &&
8185 ((cur->type == XML_DOCUMENT_NODE) ||
8186 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8187 (cur->type == XML_ELEMENT_NODE) ||
8188 (cur->type == XML_PI_NODE) ||
8189 (cur->type == XML_COMMENT_NODE) ||
8190 (cur->type == XML_CDATA_SECTION_NODE) ||
Daniel Veillard7583a592001-07-08 13:15:55 +00008191 (cur->type == XML_TEXT_NODE))) ||
8192 ((type == NODE_TYPE_TEXT) &&
8193 (cur->type == XML_CDATA_SECTION_NODE))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008194#ifdef DEBUG_STEP
8195 n++;
8196#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008197 addNode(list, cur);
8198 }
8199 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008200 case NODE_TEST_PI:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008201 if (cur->type == XML_PI_NODE) {
8202 if ((name != NULL) &&
8203 (!xmlStrEqual(name, cur->name)))
8204 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008205#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008206 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008207#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008208 addNode(list, cur);
8209 }
8210 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008211 case NODE_TEST_ALL:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008212 if (axis == AXIS_ATTRIBUTE) {
8213 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008214#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008215 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008216#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008217 addNode(list, cur);
8218 }
8219 } else if (axis == AXIS_NAMESPACE) {
8220 if (cur->type == XML_NAMESPACE_DECL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008221#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008222 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008223#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008224 addNode(list, cur);
8225 }
8226 } else {
8227 if (cur->type == XML_ELEMENT_NODE) {
8228 if (prefix == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008229#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008230 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008231#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008232 addNode(list, cur);
8233 } else if ((cur->ns != NULL) &&
8234 (xmlStrEqual(URI, cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008235#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008236 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008237#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008238 addNode(list, cur);
8239 }
8240 }
8241 }
8242 break;
8243 case NODE_TEST_NS:{
8244 TODO;
8245 break;
8246 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008247 case NODE_TEST_NAME:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008248 switch (cur->type) {
8249 case XML_ELEMENT_NODE:
8250 if (xmlStrEqual(name, cur->name)) {
8251 if (prefix == NULL) {
8252 if (cur->ns == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008253#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008254 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008255#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008256 addNode(list, cur);
8257 }
8258 } else {
8259 if ((cur->ns != NULL) &&
8260 (xmlStrEqual(URI,
8261 cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008262#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008263 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008264#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008265 addNode(list, cur);
8266 }
8267 }
8268 }
8269 break;
8270 case XML_ATTRIBUTE_NODE:{
8271 xmlAttrPtr attr = (xmlAttrPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008272
Daniel Veillardf06307e2001-07-03 10:35:50 +00008273 if (xmlStrEqual(name, attr->name)) {
8274 if (prefix == NULL) {
8275 if ((attr->ns == NULL) ||
8276 (attr->ns->prefix == NULL)) {
8277#ifdef DEBUG_STEP
8278 n++;
8279#endif
8280 addNode(list,
8281 (xmlNodePtr) attr);
8282 }
8283 } else {
8284 if ((attr->ns != NULL) &&
8285 (xmlStrEqual(URI,
8286 attr->ns->
8287 href))) {
8288#ifdef DEBUG_STEP
8289 n++;
8290#endif
8291 addNode(list,
8292 (xmlNodePtr) attr);
8293 }
8294 }
8295 }
8296 break;
8297 }
8298 case XML_NAMESPACE_DECL:
8299 if (cur->type == XML_NAMESPACE_DECL) {
8300 xmlNsPtr ns = (xmlNsPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008301
Daniel Veillardf06307e2001-07-03 10:35:50 +00008302 if ((ns->prefix != NULL) && (name != NULL)
8303 && (xmlStrEqual(ns->prefix, name))) {
8304#ifdef DEBUG_STEP
8305 n++;
8306#endif
8307 addNode(list, cur);
8308 }
8309 }
8310 break;
8311 default:
8312 break;
8313 }
8314 break;
8315 break;
8316 }
8317 } while (cur != NULL);
8318
8319 /*
8320 * If there is some predicate filtering do it now
8321 */
8322 if (op->ch2 != -1) {
8323 xmlXPathObjectPtr obj2;
8324
8325 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8326 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
8327 CHECK_TYPE0(XPATH_NODESET);
8328 obj2 = valuePop(ctxt);
8329 list = obj2->nodesetval;
8330 obj2->nodesetval = NULL;
8331 xmlXPathFreeObject(obj2);
8332 }
8333 if (ret == NULL) {
8334 ret = list;
8335 } else {
8336 ret = xmlXPathNodeSetMerge(ret, list);
8337 xmlXPathFreeNodeSet(list);
8338 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008339 }
8340 ctxt->context->node = tmp;
8341#ifdef DEBUG_STEP
8342 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008343 "\nExamined %d nodes, found %d nodes at that step\n",
8344 t, n);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008345#endif
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008346 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00008347 if ((obj->boolval) && (obj->user != NULL)) {
8348 ctxt->value->boolval = 1;
8349 ctxt->value->user = obj->user;
8350 obj->user = NULL;
8351 obj->boolval = 0;
8352 }
8353 xmlXPathFreeObject(obj);
Daniel Veillardf06307e2001-07-03 10:35:50 +00008354 return(t);
8355}
8356
8357/**
8358 * xmlXPathNodeCollectAndTestNth:
8359 * @ctxt: the XPath Parser context
8360 * @op: the XPath precompiled step operation
8361 * @indx: the index to collect
8362 * @first: pointer to the first element in document order
8363 * @last: pointer to the last element in document order
8364 *
8365 * This is the function implementing a step: based on the current list
8366 * of nodes, it builds up a new list, looking at all nodes under that
8367 * axis and selecting them it also do the predicate filtering
8368 *
8369 * Pushes the new NodeSet resulting from the search.
8370 * Returns the number of node traversed
8371 */
8372static int
8373xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
8374 xmlXPathStepOpPtr op, int indx,
8375 xmlNodePtr * first, xmlNodePtr * last)
8376{
8377 xmlXPathAxisVal axis = op->value;
8378 xmlXPathTestVal test = op->value2;
8379 xmlXPathTypeVal type = op->value3;
8380 const xmlChar *prefix = op->value4;
8381 const xmlChar *name = op->value5;
8382 const xmlChar *URI = NULL;
8383 int n = 0, t = 0;
8384
8385 int i;
8386 xmlNodeSetPtr list;
8387 xmlXPathTraversalFunction next = NULL;
8388 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
8389 xmlNodePtr cur = NULL;
8390 xmlXPathObjectPtr obj;
8391 xmlNodeSetPtr nodelist;
8392 xmlNodePtr tmp;
8393
8394 CHECK_TYPE0(XPATH_NODESET);
8395 obj = valuePop(ctxt);
8396 addNode = xmlXPathNodeSetAdd;
8397 if (prefix != NULL) {
8398 URI = xmlXPathNsLookup(ctxt->context, prefix);
8399 if (URI == NULL)
8400 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
8401 }
8402#ifdef DEBUG_STEP_NTH
8403 xmlGenericError(xmlGenericErrorContext, "new step : ");
8404 if (first != NULL) {
8405 if (*first != NULL)
8406 xmlGenericError(xmlGenericErrorContext, "first = %s ",
8407 (*first)->name);
8408 else
8409 xmlGenericError(xmlGenericErrorContext, "first = NULL ");
8410 }
8411 if (last != NULL) {
8412 if (*last != NULL)
8413 xmlGenericError(xmlGenericErrorContext, "last = %s ",
8414 (*last)->name);
8415 else
8416 xmlGenericError(xmlGenericErrorContext, "last = NULL ");
8417 }
8418#endif
8419 switch (axis) {
8420 case AXIS_ANCESTOR:
8421#ifdef DEBUG_STEP_NTH
8422 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
8423#endif
8424 first = NULL;
8425 next = xmlXPathNextAncestor;
8426 break;
8427 case AXIS_ANCESTOR_OR_SELF:
8428#ifdef DEBUG_STEP_NTH
8429 xmlGenericError(xmlGenericErrorContext,
8430 "axis 'ancestors-or-self' ");
8431#endif
8432 first = NULL;
8433 next = xmlXPathNextAncestorOrSelf;
8434 break;
8435 case AXIS_ATTRIBUTE:
8436#ifdef DEBUG_STEP_NTH
8437 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
8438#endif
8439 first = NULL;
8440 last = NULL;
8441 next = xmlXPathNextAttribute;
8442 break;
8443 case AXIS_CHILD:
8444#ifdef DEBUG_STEP_NTH
8445 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
8446#endif
8447 last = NULL;
8448 next = xmlXPathNextChild;
8449 break;
8450 case AXIS_DESCENDANT:
8451#ifdef DEBUG_STEP_NTH
8452 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
8453#endif
8454 last = NULL;
8455 next = xmlXPathNextDescendant;
8456 break;
8457 case AXIS_DESCENDANT_OR_SELF:
8458#ifdef DEBUG_STEP_NTH
8459 xmlGenericError(xmlGenericErrorContext,
8460 "axis 'descendant-or-self' ");
8461#endif
8462 last = NULL;
8463 next = xmlXPathNextDescendantOrSelf;
8464 break;
8465 case AXIS_FOLLOWING:
8466#ifdef DEBUG_STEP_NTH
8467 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
8468#endif
8469 last = NULL;
8470 next = xmlXPathNextFollowing;
8471 break;
8472 case AXIS_FOLLOWING_SIBLING:
8473#ifdef DEBUG_STEP_NTH
8474 xmlGenericError(xmlGenericErrorContext,
8475 "axis 'following-siblings' ");
8476#endif
8477 last = NULL;
8478 next = xmlXPathNextFollowingSibling;
8479 break;
8480 case AXIS_NAMESPACE:
8481#ifdef DEBUG_STEP_NTH
8482 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
8483#endif
8484 last = NULL;
8485 first = NULL;
8486 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8487 break;
8488 case AXIS_PARENT:
8489#ifdef DEBUG_STEP_NTH
8490 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
8491#endif
8492 first = NULL;
8493 next = xmlXPathNextParent;
8494 break;
8495 case AXIS_PRECEDING:
8496#ifdef DEBUG_STEP_NTH
8497 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
8498#endif
8499 first = NULL;
8500 next = xmlXPathNextPrecedingInternal;
8501 break;
8502 case AXIS_PRECEDING_SIBLING:
8503#ifdef DEBUG_STEP_NTH
8504 xmlGenericError(xmlGenericErrorContext,
8505 "axis 'preceding-sibling' ");
8506#endif
8507 first = NULL;
8508 next = xmlXPathNextPrecedingSibling;
8509 break;
8510 case AXIS_SELF:
8511#ifdef DEBUG_STEP_NTH
8512 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
8513#endif
8514 first = NULL;
8515 last = NULL;
8516 next = xmlXPathNextSelf;
8517 break;
8518 }
8519 if (next == NULL)
8520 return(0);
8521
8522 nodelist = obj->nodesetval;
8523 if (nodelist == NULL) {
8524 xmlXPathFreeObject(obj);
8525 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8526 return(0);
8527 }
8528 addNode = xmlXPathNodeSetAddUnique;
8529#ifdef DEBUG_STEP_NTH
8530 xmlGenericError(xmlGenericErrorContext,
8531 " context contains %d nodes\n", nodelist->nodeNr);
8532 switch (test) {
8533 case NODE_TEST_NONE:
8534 xmlGenericError(xmlGenericErrorContext,
8535 " searching for none !!!\n");
8536 break;
8537 case NODE_TEST_TYPE:
8538 xmlGenericError(xmlGenericErrorContext,
8539 " searching for type %d\n", type);
8540 break;
8541 case NODE_TEST_PI:
8542 xmlGenericError(xmlGenericErrorContext,
8543 " searching for PI !!!\n");
8544 break;
8545 case NODE_TEST_ALL:
8546 xmlGenericError(xmlGenericErrorContext,
8547 " searching for *\n");
8548 break;
8549 case NODE_TEST_NS:
8550 xmlGenericError(xmlGenericErrorContext,
8551 " searching for namespace %s\n",
8552 prefix);
8553 break;
8554 case NODE_TEST_NAME:
8555 xmlGenericError(xmlGenericErrorContext,
8556 " searching for name %s\n", name);
8557 if (prefix != NULL)
8558 xmlGenericError(xmlGenericErrorContext,
8559 " with namespace %s\n", prefix);
8560 break;
8561 }
8562 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8563#endif
8564 /*
8565 * 2.3 Node Tests
8566 * - For the attribute axis, the principal node type is attribute.
8567 * - For the namespace axis, the principal node type is namespace.
8568 * - For other axes, the principal node type is element.
8569 *
8570 * A node test * is true for any node of the
8571 * principal node type. For example, child::* willi
8572 * select all element children of the context node
8573 */
8574 tmp = ctxt->context->node;
8575 list = xmlXPathNodeSetCreate(NULL);
8576 for (i = 0; i < nodelist->nodeNr; i++) {
8577 ctxt->context->node = nodelist->nodeTab[i];
8578
8579 cur = NULL;
8580 n = 0;
8581 do {
8582 cur = next(ctxt, cur);
8583 if (cur == NULL)
8584 break;
8585 if ((first != NULL) && (*first == cur))
8586 break;
8587 if (((t % 256) == 0) &&
8588 (first != NULL) && (*first != NULL) &&
8589 (xmlXPathCmpNodes(*first, cur) >= 0))
8590 break;
8591 if ((last != NULL) && (*last == cur))
8592 break;
8593 if (((t % 256) == 0) &&
8594 (last != NULL) && (*last != NULL) &&
8595 (xmlXPathCmpNodes(cur, *last) >= 0))
8596 break;
8597 t++;
8598 switch (test) {
8599 case NODE_TEST_NONE:
8600 ctxt->context->node = tmp;
8601 STRANGE return(0);
8602 case NODE_TEST_TYPE:
8603 if ((cur->type == type) ||
8604 ((type == NODE_TYPE_NODE) &&
8605 ((cur->type == XML_DOCUMENT_NODE) ||
8606 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8607 (cur->type == XML_ELEMENT_NODE) ||
8608 (cur->type == XML_PI_NODE) ||
8609 (cur->type == XML_COMMENT_NODE) ||
8610 (cur->type == XML_CDATA_SECTION_NODE) ||
8611 (cur->type == XML_TEXT_NODE)))) {
8612 n++;
8613 if (n == indx)
8614 addNode(list, cur);
8615 }
8616 break;
8617 case NODE_TEST_PI:
8618 if (cur->type == XML_PI_NODE) {
8619 if ((name != NULL) &&
8620 (!xmlStrEqual(name, cur->name)))
8621 break;
8622 n++;
8623 if (n == indx)
8624 addNode(list, cur);
8625 }
8626 break;
8627 case NODE_TEST_ALL:
8628 if (axis == AXIS_ATTRIBUTE) {
8629 if (cur->type == XML_ATTRIBUTE_NODE) {
8630 n++;
8631 if (n == indx)
8632 addNode(list, cur);
8633 }
8634 } else if (axis == AXIS_NAMESPACE) {
8635 if (cur->type == XML_NAMESPACE_DECL) {
8636 n++;
8637 if (n == indx)
8638 addNode(list, cur);
8639 }
8640 } else {
8641 if (cur->type == XML_ELEMENT_NODE) {
8642 if (prefix == NULL) {
8643 n++;
8644 if (n == indx)
8645 addNode(list, cur);
8646 } else if ((cur->ns != NULL) &&
8647 (xmlStrEqual(URI, cur->ns->href))) {
8648 n++;
8649 if (n == indx)
8650 addNode(list, cur);
8651 }
8652 }
8653 }
8654 break;
8655 case NODE_TEST_NS:{
8656 TODO;
8657 break;
8658 }
8659 case NODE_TEST_NAME:
8660 switch (cur->type) {
8661 case XML_ELEMENT_NODE:
8662 if (xmlStrEqual(name, cur->name)) {
8663 if (prefix == NULL) {
8664 if (cur->ns == NULL) {
8665 n++;
8666 if (n == indx)
8667 addNode(list, cur);
8668 }
8669 } else {
8670 if ((cur->ns != NULL) &&
8671 (xmlStrEqual(URI,
8672 cur->ns->href))) {
8673 n++;
8674 if (n == indx)
8675 addNode(list, cur);
8676 }
8677 }
8678 }
8679 break;
8680 case XML_ATTRIBUTE_NODE:{
8681 xmlAttrPtr attr = (xmlAttrPtr) cur;
8682
8683 if (xmlStrEqual(name, attr->name)) {
8684 if (prefix == NULL) {
8685 if ((attr->ns == NULL) ||
8686 (attr->ns->prefix == NULL)) {
8687 n++;
8688 if (n == indx)
8689 addNode(list, cur);
8690 }
8691 } else {
8692 if ((attr->ns != NULL) &&
8693 (xmlStrEqual(URI,
8694 attr->ns->
8695 href))) {
8696 n++;
8697 if (n == indx)
8698 addNode(list, cur);
8699 }
8700 }
8701 }
8702 break;
8703 }
8704 case XML_NAMESPACE_DECL:
8705 if (cur->type == XML_NAMESPACE_DECL) {
8706 xmlNsPtr ns = (xmlNsPtr) cur;
8707
8708 if ((ns->prefix != NULL) && (name != NULL)
8709 && (xmlStrEqual(ns->prefix, name))) {
8710 n++;
8711 if (n == indx)
8712 addNode(list, cur);
8713 }
8714 }
8715 break;
8716 default:
8717 break;
8718 }
8719 break;
8720 break;
8721 }
8722 } while (n < indx);
8723 }
8724 ctxt->context->node = tmp;
8725#ifdef DEBUG_STEP_NTH
8726 xmlGenericError(xmlGenericErrorContext,
8727 "\nExamined %d nodes, found %d nodes at that step\n",
8728 t, list->nodeNr);
8729#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008730 valuePush(ctxt, xmlXPathWrapNodeSet(list));
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00008731 if ((obj->boolval) && (obj->user != NULL)) {
8732 ctxt->value->boolval = 1;
8733 ctxt->value->user = obj->user;
8734 obj->user = NULL;
8735 obj->boolval = 0;
8736 }
8737 xmlXPathFreeObject(obj);
Daniel Veillardf06307e2001-07-03 10:35:50 +00008738 return(t);
8739}
8740
8741/**
8742 * xmlXPathCompOpEvalFirst:
8743 * @ctxt: the XPath parser context with the compiled expression
8744 * @op: an XPath compiled operation
8745 * @first: the first elem found so far
8746 *
8747 * Evaluate the Precompiled XPath operation searching only the first
8748 * element in document order
8749 *
8750 * Returns the number of examined objects.
8751 */
8752static int
8753xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
8754 xmlXPathStepOpPtr op, xmlNodePtr * first)
8755{
8756 int total = 0, cur;
8757 xmlXPathCompExprPtr comp;
8758 xmlXPathObjectPtr arg1, arg2;
8759
8760 comp = ctxt->comp;
8761 switch (op->op) {
8762 case XPATH_OP_END:
8763 return (0);
8764 case XPATH_OP_UNION:
8765 total =
8766 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8767 first);
8768 if ((ctxt->value != NULL)
8769 && (ctxt->value->type == XPATH_NODESET)
8770 && (ctxt->value->nodesetval != NULL)
8771 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8772 /*
8773 * limit tree traversing to first node in the result
8774 */
8775 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8776 *first = ctxt->value->nodesetval->nodeTab[0];
8777 }
8778 cur =
8779 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
8780 first);
8781 CHECK_TYPE0(XPATH_NODESET);
8782 arg2 = valuePop(ctxt);
8783
8784 CHECK_TYPE0(XPATH_NODESET);
8785 arg1 = valuePop(ctxt);
8786
8787 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8788 arg2->nodesetval);
8789 valuePush(ctxt, arg1);
8790 xmlXPathFreeObject(arg2);
8791 /* optimizer */
8792 if (total > cur)
8793 xmlXPathCompSwap(op);
8794 return (total + cur);
8795 case XPATH_OP_ROOT:
8796 xmlXPathRoot(ctxt);
8797 return (0);
8798 case XPATH_OP_NODE:
8799 if (op->ch1 != -1)
8800 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8801 if (op->ch2 != -1)
8802 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8803 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8804 return (total);
8805 case XPATH_OP_RESET:
8806 if (op->ch1 != -1)
8807 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8808 if (op->ch2 != -1)
8809 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8810 ctxt->context->node = NULL;
8811 return (total);
8812 case XPATH_OP_COLLECT:{
8813 if (op->ch1 == -1)
8814 return (total);
8815
8816 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8817
8818 /*
8819 * Optimization for [n] selection where n is a number
8820 */
8821 if ((op->ch2 != -1) &&
8822 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8823 (comp->steps[op->ch2].ch1 == -1) &&
8824 (comp->steps[op->ch2].ch2 != -1) &&
8825 (comp->steps[comp->steps[op->ch2].ch2].op ==
8826 XPATH_OP_VALUE)) {
8827 xmlXPathObjectPtr val;
8828
8829 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8830 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8831 int indx = (int) val->floatval;
8832
8833 if (val->floatval == (float) indx) {
8834 xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
8835 first, NULL);
8836 return (total);
8837 }
8838 }
8839 }
8840 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
8841 return (total);
8842 }
8843 case XPATH_OP_VALUE:
8844 valuePush(ctxt,
8845 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8846 return (0);
8847 case XPATH_OP_SORT:
8848 if (op->ch1 != -1)
8849 total +=
8850 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8851 first);
8852 if ((ctxt->value != NULL)
8853 && (ctxt->value->type == XPATH_NODESET)
8854 && (ctxt->value->nodesetval != NULL))
8855 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8856 return (total);
8857 default:
8858 return (xmlXPathCompOpEval(ctxt, op));
8859 }
8860}
8861
8862/**
8863 * xmlXPathCompOpEvalLast:
8864 * @ctxt: the XPath parser context with the compiled expression
8865 * @op: an XPath compiled operation
8866 * @last: the last elem found so far
8867 *
8868 * Evaluate the Precompiled XPath operation searching only the last
8869 * element in document order
8870 *
8871 * Returns the number of node traversed
8872 */
8873static int
8874xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
8875 xmlNodePtr * last)
8876{
8877 int total = 0, cur;
8878 xmlXPathCompExprPtr comp;
8879 xmlXPathObjectPtr arg1, arg2;
8880
8881 comp = ctxt->comp;
8882 switch (op->op) {
8883 case XPATH_OP_END:
8884 return (0);
8885 case XPATH_OP_UNION:
8886 total =
8887 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
8888 if ((ctxt->value != NULL)
8889 && (ctxt->value->type == XPATH_NODESET)
8890 && (ctxt->value->nodesetval != NULL)
8891 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8892 /*
8893 * limit tree traversing to first node in the result
8894 */
8895 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8896 *last =
8897 ctxt->value->nodesetval->nodeTab[ctxt->value->
8898 nodesetval->nodeNr -
8899 1];
8900 }
8901 cur =
8902 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
8903 if ((ctxt->value != NULL)
8904 && (ctxt->value->type == XPATH_NODESET)
8905 && (ctxt->value->nodesetval != NULL)
8906 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8907 }
8908 CHECK_TYPE0(XPATH_NODESET);
8909 arg2 = valuePop(ctxt);
8910
8911 CHECK_TYPE0(XPATH_NODESET);
8912 arg1 = valuePop(ctxt);
8913
8914 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8915 arg2->nodesetval);
8916 valuePush(ctxt, arg1);
8917 xmlXPathFreeObject(arg2);
8918 /* optimizer */
8919 if (total > cur)
8920 xmlXPathCompSwap(op);
8921 return (total + cur);
8922 case XPATH_OP_ROOT:
8923 xmlXPathRoot(ctxt);
8924 return (0);
8925 case XPATH_OP_NODE:
8926 if (op->ch1 != -1)
8927 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8928 if (op->ch2 != -1)
8929 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8930 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8931 return (total);
8932 case XPATH_OP_RESET:
8933 if (op->ch1 != -1)
8934 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8935 if (op->ch2 != -1)
8936 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8937 ctxt->context->node = NULL;
8938 return (total);
8939 case XPATH_OP_COLLECT:{
8940 if (op->ch1 == -1)
8941 return (0);
8942
8943 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8944
8945 /*
8946 * Optimization for [n] selection where n is a number
8947 */
8948 if ((op->ch2 != -1) &&
8949 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8950 (comp->steps[op->ch2].ch1 == -1) &&
8951 (comp->steps[op->ch2].ch2 != -1) &&
8952 (comp->steps[comp->steps[op->ch2].ch2].op ==
8953 XPATH_OP_VALUE)) {
8954 xmlXPathObjectPtr val;
8955
8956 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8957 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8958 int indx = (int) val->floatval;
8959
8960 if (val->floatval == (float) indx) {
8961 total +=
8962 xmlXPathNodeCollectAndTestNth(ctxt, op,
8963 indx, NULL,
8964 last);
8965 return (total);
8966 }
8967 }
8968 }
8969 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
8970 return (total);
8971 }
8972 case XPATH_OP_VALUE:
8973 valuePush(ctxt,
8974 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8975 return (0);
8976 case XPATH_OP_SORT:
8977 if (op->ch1 != -1)
8978 total +=
8979 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
8980 last);
8981 if ((ctxt->value != NULL)
8982 && (ctxt->value->type == XPATH_NODESET)
8983 && (ctxt->value->nodesetval != NULL))
8984 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8985 return (total);
8986 default:
8987 return (xmlXPathCompOpEval(ctxt, op));
8988 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008989}
8990
Owen Taylor3473f882001-02-23 17:55:21 +00008991/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00008992 * xmlXPathCompOpEval:
8993 * @ctxt: the XPath parser context with the compiled expression
8994 * @op: an XPath compiled operation
8995 *
8996 * Evaluate the Precompiled XPath operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00008997 * Returns the number of node traversed
Daniel Veillard9e7160d2001-03-18 23:17:47 +00008998 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00008999static int
9000xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
9001{
9002 int total = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009003 int equal, ret;
9004 xmlXPathCompExprPtr comp;
9005 xmlXPathObjectPtr arg1, arg2;
9006
9007 comp = ctxt->comp;
9008 switch (op->op) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00009009 case XPATH_OP_END:
9010 return (0);
9011 case XPATH_OP_AND:
9012 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9013 xmlXPathBooleanFunction(ctxt, 1);
9014 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
9015 return (total);
9016 arg2 = valuePop(ctxt);
9017 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9018 xmlXPathBooleanFunction(ctxt, 1);
9019 arg1 = valuePop(ctxt);
9020 arg1->boolval &= arg2->boolval;
9021 valuePush(ctxt, arg1);
9022 xmlXPathFreeObject(arg2);
9023 return (total);
9024 case XPATH_OP_OR:
9025 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9026 xmlXPathBooleanFunction(ctxt, 1);
9027 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
9028 return (total);
9029 arg2 = valuePop(ctxt);
9030 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9031 xmlXPathBooleanFunction(ctxt, 1);
9032 arg1 = valuePop(ctxt);
9033 arg1->boolval |= arg2->boolval;
9034 valuePush(ctxt, arg1);
9035 xmlXPathFreeObject(arg2);
9036 return (total);
9037 case XPATH_OP_EQUAL:
9038 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9039 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9040 equal = xmlXPathEqualValues(ctxt);
9041 if (op->value)
9042 valuePush(ctxt, xmlXPathNewBoolean(equal));
9043 else
9044 valuePush(ctxt, xmlXPathNewBoolean(!equal));
9045 return (total);
9046 case XPATH_OP_CMP:
9047 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9048 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9049 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
9050 valuePush(ctxt, xmlXPathNewBoolean(ret));
9051 return (total);
9052 case XPATH_OP_PLUS:
9053 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9054 if (op->ch2 != -1)
9055 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9056 if (op->value == 0)
9057 xmlXPathSubValues(ctxt);
9058 else if (op->value == 1)
9059 xmlXPathAddValues(ctxt);
9060 else if (op->value == 2)
9061 xmlXPathValueFlipSign(ctxt);
9062 else if (op->value == 3) {
9063 CAST_TO_NUMBER;
9064 CHECK_TYPE0(XPATH_NUMBER);
9065 }
9066 return (total);
9067 case XPATH_OP_MULT:
9068 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9069 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9070 if (op->value == 0)
9071 xmlXPathMultValues(ctxt);
9072 else if (op->value == 1)
9073 xmlXPathDivValues(ctxt);
9074 else if (op->value == 2)
9075 xmlXPathModValues(ctxt);
9076 return (total);
9077 case XPATH_OP_UNION:
9078 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9079 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9080 CHECK_TYPE0(XPATH_NODESET);
9081 arg2 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009082
Daniel Veillardf06307e2001-07-03 10:35:50 +00009083 CHECK_TYPE0(XPATH_NODESET);
9084 arg1 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009085
Daniel Veillardf06307e2001-07-03 10:35:50 +00009086 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
9087 arg2->nodesetval);
9088 valuePush(ctxt, arg1);
9089 xmlXPathFreeObject(arg2);
9090 return (total);
9091 case XPATH_OP_ROOT:
9092 xmlXPathRoot(ctxt);
9093 return (total);
9094 case XPATH_OP_NODE:
9095 if (op->ch1 != -1)
9096 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9097 if (op->ch2 != -1)
9098 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9099 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
9100 return (total);
9101 case XPATH_OP_RESET:
9102 if (op->ch1 != -1)
9103 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9104 if (op->ch2 != -1)
9105 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9106 ctxt->context->node = NULL;
9107 return (total);
9108 case XPATH_OP_COLLECT:{
9109 if (op->ch1 == -1)
9110 return (total);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009111
Daniel Veillardf06307e2001-07-03 10:35:50 +00009112 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009113
Daniel Veillardf06307e2001-07-03 10:35:50 +00009114 /*
9115 * Optimization for [n] selection where n is a number
9116 */
9117 if ((op->ch2 != -1) &&
9118 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9119 (comp->steps[op->ch2].ch1 == -1) &&
9120 (comp->steps[op->ch2].ch2 != -1) &&
9121 (comp->steps[comp->steps[op->ch2].ch2].op ==
9122 XPATH_OP_VALUE)) {
9123 xmlXPathObjectPtr val;
Daniel Veillard42596ad2001-05-22 16:57:14 +00009124
Daniel Veillardf06307e2001-07-03 10:35:50 +00009125 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9126 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9127 int indx = (int) val->floatval;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009128
Daniel Veillardf06307e2001-07-03 10:35:50 +00009129 if (val->floatval == (float) indx) {
9130 total +=
9131 xmlXPathNodeCollectAndTestNth(ctxt, op,
9132 indx, NULL,
9133 NULL);
9134 return (total);
9135 }
9136 }
9137 }
9138 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
9139 return (total);
9140 }
9141 case XPATH_OP_VALUE:
9142 valuePush(ctxt,
9143 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9144 return (total);
9145 case XPATH_OP_VARIABLE:{
9146 if (op->ch1 != -1)
9147 total +=
9148 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9149 if (op->value5 == NULL)
9150 valuePush(ctxt,
9151 xmlXPathVariableLookup(ctxt->context,
9152 op->value4));
9153 else {
9154 const xmlChar *URI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009155
Daniel Veillardf06307e2001-07-03 10:35:50 +00009156 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9157 if (URI == NULL) {
9158 xmlGenericError(xmlGenericErrorContext,
9159 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
9160 op->value4, op->value5);
9161 return (total);
9162 }
9163 valuePush(ctxt,
9164 xmlXPathVariableLookupNS(ctxt->context,
9165 op->value4, URI));
9166 }
9167 return (total);
9168 }
9169 case XPATH_OP_FUNCTION:{
9170 xmlXPathFunction func;
9171 const xmlChar *oldFunc, *oldFuncURI;
9172
9173 if (op->ch1 != -1)
9174 total +=
9175 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9176 if (op->cache != NULL)
9177 func = (xmlXPathFunction) op->cache;
9178 else {
9179 const xmlChar *URI = NULL;
9180
9181 if (op->value5 == NULL)
9182 func =
9183 xmlXPathFunctionLookup(ctxt->context,
9184 op->value4);
9185 else {
9186 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9187 if (URI == NULL) {
9188 xmlGenericError(xmlGenericErrorContext,
9189 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
9190 op->value4, op->value5);
9191 return (total);
9192 }
9193 func = xmlXPathFunctionLookupNS(ctxt->context,
9194 op->value4, URI);
9195 }
9196 if (func == NULL) {
9197 xmlGenericError(xmlGenericErrorContext,
9198 "xmlXPathRunEval: function %s not found\n",
9199 op->value4);
9200 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
9201 return (total);
9202 }
9203 op->cache = (void *) func;
9204 op->cacheURI = (void *) URI;
9205 }
9206 oldFunc = ctxt->context->function;
9207 oldFuncURI = ctxt->context->functionURI;
9208 ctxt->context->function = op->value4;
9209 ctxt->context->functionURI = op->cacheURI;
9210 func(ctxt, op->value);
9211 ctxt->context->function = oldFunc;
9212 ctxt->context->functionURI = oldFuncURI;
9213 return (total);
9214 }
9215 case XPATH_OP_ARG:
9216 if (op->ch1 != -1)
9217 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9218 if (op->ch2 != -1)
9219 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9220 return (total);
9221 case XPATH_OP_PREDICATE:
9222 case XPATH_OP_FILTER:{
9223 xmlXPathObjectPtr res;
9224 xmlXPathObjectPtr obj, tmp;
9225 xmlNodeSetPtr newset = NULL;
9226 xmlNodeSetPtr oldset;
9227 xmlNodePtr oldnode;
9228 int i;
9229
9230 /*
9231 * Optimization for ()[1] selection i.e. the first elem
9232 */
9233 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9234 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9235 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
9236 xmlXPathObjectPtr val;
9237
9238 val = comp->steps[op->ch2].value4;
9239 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
9240 (val->floatval == 1.0)) {
9241 xmlNodePtr first = NULL;
9242
9243 total +=
9244 xmlXPathCompOpEvalFirst(ctxt,
9245 &comp->steps[op->ch1],
9246 &first);
9247 /*
9248 * The nodeset should be in document order,
9249 * Keep only the first value
9250 */
9251 if ((ctxt->value != NULL) &&
9252 (ctxt->value->type == XPATH_NODESET) &&
9253 (ctxt->value->nodesetval != NULL) &&
9254 (ctxt->value->nodesetval->nodeNr > 1))
9255 ctxt->value->nodesetval->nodeNr = 1;
9256 return (total);
9257 }
9258 }
9259 /*
9260 * Optimization for ()[last()] selection i.e. the last elem
9261 */
9262 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9263 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9264 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
9265 int f = comp->steps[op->ch2].ch1;
9266
9267 if ((f != -1) &&
9268 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
9269 (comp->steps[f].value5 == NULL) &&
9270 (comp->steps[f].value == 0) &&
9271 (comp->steps[f].value4 != NULL) &&
9272 (xmlStrEqual
9273 (comp->steps[f].value4, BAD_CAST "last"))) {
9274 xmlNodePtr last = NULL;
9275
9276 total +=
9277 xmlXPathCompOpEvalLast(ctxt,
9278 &comp->steps[op->ch1],
9279 &last);
9280 /*
9281 * The nodeset should be in document order,
9282 * Keep only the last value
9283 */
9284 if ((ctxt->value != NULL) &&
9285 (ctxt->value->type == XPATH_NODESET) &&
9286 (ctxt->value->nodesetval != NULL) &&
9287 (ctxt->value->nodesetval->nodeTab != NULL) &&
9288 (ctxt->value->nodesetval->nodeNr > 1)) {
9289 ctxt->value->nodesetval->nodeTab[0] =
9290 ctxt->value->nodesetval->nodeTab[ctxt->
9291 value->
9292 nodesetval->
9293 nodeNr -
9294 1];
9295 ctxt->value->nodesetval->nodeNr = 1;
9296 }
9297 return (total);
9298 }
9299 }
9300
9301 if (op->ch1 != -1)
9302 total +=
9303 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9304 if (op->ch2 == -1)
9305 return (total);
9306 if (ctxt->value == NULL)
9307 return (total);
9308
9309 oldnode = ctxt->context->node;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009310
9311#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009312 /*
9313 * Hum are we filtering the result of an XPointer expression
9314 */
9315 if (ctxt->value->type == XPATH_LOCATIONSET) {
9316 xmlLocationSetPtr newlocset = NULL;
9317 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009318
Daniel Veillardf06307e2001-07-03 10:35:50 +00009319 /*
9320 * Extract the old locset, and then evaluate the result of the
9321 * expression for all the element in the locset. use it to grow
9322 * up a new locset.
9323 */
9324 CHECK_TYPE0(XPATH_LOCATIONSET);
9325 obj = valuePop(ctxt);
9326 oldlocset = obj->user;
9327 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009328
Daniel Veillardf06307e2001-07-03 10:35:50 +00009329 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
9330 ctxt->context->contextSize = 0;
9331 ctxt->context->proximityPosition = 0;
9332 if (op->ch2 != -1)
9333 total +=
9334 xmlXPathCompOpEval(ctxt,
9335 &comp->steps[op->ch2]);
9336 res = valuePop(ctxt);
9337 if (res != NULL)
9338 xmlXPathFreeObject(res);
9339 valuePush(ctxt, obj);
9340 CHECK_ERROR0;
9341 return (total);
9342 }
9343 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009344
Daniel Veillardf06307e2001-07-03 10:35:50 +00009345 for (i = 0; i < oldlocset->locNr; i++) {
9346 /*
9347 * Run the evaluation with a node list made of a
9348 * single item in the nodelocset.
9349 */
9350 ctxt->context->node = oldlocset->locTab[i]->user;
9351 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9352 valuePush(ctxt, tmp);
9353 ctxt->context->contextSize = oldlocset->locNr;
9354 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009355
Daniel Veillardf06307e2001-07-03 10:35:50 +00009356 if (op->ch2 != -1)
9357 total +=
9358 xmlXPathCompOpEval(ctxt,
9359 &comp->steps[op->ch2]);
9360 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009361
Daniel Veillardf06307e2001-07-03 10:35:50 +00009362 /*
9363 * The result of the evaluation need to be tested to
9364 * decided whether the filter succeeded or not
9365 */
9366 res = valuePop(ctxt);
9367 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9368 xmlXPtrLocationSetAdd(newlocset,
9369 xmlXPathObjectCopy
9370 (oldlocset->locTab[i]));
9371 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009372
Daniel Veillardf06307e2001-07-03 10:35:50 +00009373 /*
9374 * Cleanup
9375 */
9376 if (res != NULL)
9377 xmlXPathFreeObject(res);
9378 if (ctxt->value == tmp) {
9379 res = valuePop(ctxt);
9380 xmlXPathFreeObject(res);
9381 }
9382
9383 ctxt->context->node = NULL;
9384 }
9385
9386 /*
9387 * The result is used as the new evaluation locset.
9388 */
9389 xmlXPathFreeObject(obj);
9390 ctxt->context->node = NULL;
9391 ctxt->context->contextSize = -1;
9392 ctxt->context->proximityPosition = -1;
9393 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
9394 ctxt->context->node = oldnode;
9395 return (total);
9396 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009397#endif /* LIBXML_XPTR_ENABLED */
9398
Daniel Veillardf06307e2001-07-03 10:35:50 +00009399 /*
9400 * Extract the old set, and then evaluate the result of the
9401 * expression for all the element in the set. use it to grow
9402 * up a new set.
9403 */
9404 CHECK_TYPE0(XPATH_NODESET);
9405 obj = valuePop(ctxt);
9406 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00009407
Daniel Veillardf06307e2001-07-03 10:35:50 +00009408 oldnode = ctxt->context->node;
9409 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009410
Daniel Veillardf06307e2001-07-03 10:35:50 +00009411 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
9412 ctxt->context->contextSize = 0;
9413 ctxt->context->proximityPosition = 0;
9414 if (op->ch2 != -1)
9415 total +=
9416 xmlXPathCompOpEval(ctxt,
9417 &comp->steps[op->ch2]);
9418 res = valuePop(ctxt);
9419 if (res != NULL)
9420 xmlXPathFreeObject(res);
9421 valuePush(ctxt, obj);
9422 ctxt->context->node = oldnode;
9423 CHECK_ERROR0;
9424 } else {
9425 /*
9426 * Initialize the new set.
9427 */
9428 newset = xmlXPathNodeSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009429
Daniel Veillardf06307e2001-07-03 10:35:50 +00009430 for (i = 0; i < oldset->nodeNr; i++) {
9431 /*
9432 * Run the evaluation with a node list made of
9433 * a single item in the nodeset.
9434 */
9435 ctxt->context->node = oldset->nodeTab[i];
9436 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9437 valuePush(ctxt, tmp);
9438 ctxt->context->contextSize = oldset->nodeNr;
9439 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009440
Daniel Veillardf06307e2001-07-03 10:35:50 +00009441 if (op->ch2 != -1)
9442 total +=
9443 xmlXPathCompOpEval(ctxt,
9444 &comp->steps[op->ch2]);
9445 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009446
Daniel Veillardf06307e2001-07-03 10:35:50 +00009447 /*
9448 * The result of the evaluation need to be tested to
9449 * decided whether the filter succeeded or not
9450 */
9451 res = valuePop(ctxt);
9452 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9453 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
9454 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009455
Daniel Veillardf06307e2001-07-03 10:35:50 +00009456 /*
9457 * Cleanup
9458 */
9459 if (res != NULL)
9460 xmlXPathFreeObject(res);
9461 if (ctxt->value == tmp) {
9462 res = valuePop(ctxt);
9463 xmlXPathFreeObject(res);
9464 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009465
Daniel Veillardf06307e2001-07-03 10:35:50 +00009466 ctxt->context->node = NULL;
9467 }
9468
9469 /*
9470 * The result is used as the new evaluation set.
9471 */
9472 xmlXPathFreeObject(obj);
9473 ctxt->context->node = NULL;
9474 ctxt->context->contextSize = -1;
9475 ctxt->context->proximityPosition = -1;
9476 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
9477 }
9478 ctxt->context->node = oldnode;
9479 return (total);
9480 }
9481 case XPATH_OP_SORT:
9482 if (op->ch1 != -1)
9483 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9484 if ((ctxt->value != NULL) &&
9485 (ctxt->value->type == XPATH_NODESET) &&
9486 (ctxt->value->nodesetval != NULL))
9487 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9488 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009489#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009490 case XPATH_OP_RANGETO:{
9491 xmlXPathObjectPtr range;
9492 xmlXPathObjectPtr res, obj;
9493 xmlXPathObjectPtr tmp;
9494 xmlLocationSetPtr newset = NULL;
9495 xmlNodeSetPtr oldset;
9496 int i;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009497
Daniel Veillardf06307e2001-07-03 10:35:50 +00009498 if (op->ch1 != -1)
9499 total +=
9500 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9501 if (op->ch2 == -1)
9502 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009503
Daniel Veillardf06307e2001-07-03 10:35:50 +00009504 CHECK_TYPE0(XPATH_NODESET);
9505 obj = valuePop(ctxt);
9506 oldset = obj->nodesetval;
9507 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009508
Daniel Veillardf06307e2001-07-03 10:35:50 +00009509 newset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009510
Daniel Veillardf06307e2001-07-03 10:35:50 +00009511 if (oldset != NULL) {
9512 for (i = 0; i < oldset->nodeNr; i++) {
9513 /*
9514 * Run the evaluation with a node list made of a single item
9515 * in the nodeset.
9516 */
9517 ctxt->context->node = oldset->nodeTab[i];
9518 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9519 valuePush(ctxt, tmp);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009520
Daniel Veillardf06307e2001-07-03 10:35:50 +00009521 if (op->ch2 != -1)
9522 total +=
9523 xmlXPathCompOpEval(ctxt,
9524 &comp->steps[op->ch2]);
9525 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009526
Daniel Veillardf06307e2001-07-03 10:35:50 +00009527 /*
9528 * The result of the evaluation need to be tested to
9529 * decided whether the filter succeeded or not
9530 */
9531 res = valuePop(ctxt);
9532 range =
9533 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
9534 res);
9535 if (range != NULL) {
9536 xmlXPtrLocationSetAdd(newset, range);
9537 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009538
Daniel Veillardf06307e2001-07-03 10:35:50 +00009539 /*
9540 * Cleanup
9541 */
9542 if (res != NULL)
9543 xmlXPathFreeObject(res);
9544 if (ctxt->value == tmp) {
9545 res = valuePop(ctxt);
9546 xmlXPathFreeObject(res);
9547 }
9548
9549 ctxt->context->node = NULL;
9550 }
9551 }
9552
9553 /*
9554 * The result is used as the new evaluation set.
9555 */
9556 xmlXPathFreeObject(obj);
9557 ctxt->context->node = NULL;
9558 ctxt->context->contextSize = -1;
9559 ctxt->context->proximityPosition = -1;
9560 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
9561 return (total);
9562 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009563#endif /* LIBXML_XPTR_ENABLED */
9564 }
9565 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00009566 "XPath: unknown precompiled operation %d\n", op->op);
9567 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009568}
9569
9570/**
9571 * xmlXPathRunEval:
9572 * @ctxt: the XPath parser context with the compiled expression
9573 *
9574 * Evaluate the Precompiled XPath expression in the given context.
9575 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009576static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009577xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
9578 xmlXPathCompExprPtr comp;
9579
9580 if ((ctxt == NULL) || (ctxt->comp == NULL))
9581 return;
9582
9583 if (ctxt->valueTab == NULL) {
9584 /* Allocate the value stack */
9585 ctxt->valueTab = (xmlXPathObjectPtr *)
9586 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
9587 if (ctxt->valueTab == NULL) {
9588 xmlFree(ctxt);
9589 xmlGenericError(xmlGenericErrorContext,
9590 "xmlXPathRunEval: out of memory\n");
9591 return;
9592 }
9593 ctxt->valueNr = 0;
9594 ctxt->valueMax = 10;
9595 ctxt->value = NULL;
9596 }
9597 comp = ctxt->comp;
9598 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
9599}
9600
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009601/************************************************************************
9602 * *
9603 * Public interfaces *
9604 * *
9605 ************************************************************************/
9606
9607/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009608 * xmlXPathEvalPredicate:
9609 * @ctxt: the XPath context
9610 * @res: the Predicate Expression evaluation result
9611 *
9612 * Evaluate a predicate result for the current node.
9613 * A PredicateExpr is evaluated by evaluating the Expr and converting
9614 * the result to a boolean. If the result is a number, the result will
9615 * be converted to true if the number is equal to the position of the
9616 * context node in the context node list (as returned by the position
9617 * function) and will be converted to false otherwise; if the result
9618 * is not a number, then the result will be converted as if by a call
9619 * to the boolean function.
9620 *
9621 * Return 1 if predicate is true, 0 otherwise
9622 */
9623int
9624xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
9625 if (res == NULL) return(0);
9626 switch (res->type) {
9627 case XPATH_BOOLEAN:
9628 return(res->boolval);
9629 case XPATH_NUMBER:
9630 return(res->floatval == ctxt->proximityPosition);
9631 case XPATH_NODESET:
9632 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009633 if (res->nodesetval == NULL)
9634 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009635 return(res->nodesetval->nodeNr != 0);
9636 case XPATH_STRING:
9637 return((res->stringval != NULL) &&
9638 (xmlStrlen(res->stringval) != 0));
9639 default:
9640 STRANGE
9641 }
9642 return(0);
9643}
9644
9645/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009646 * xmlXPathEvaluatePredicateResult:
9647 * @ctxt: the XPath Parser context
9648 * @res: the Predicate Expression evaluation result
9649 *
9650 * Evaluate a predicate result for the current node.
9651 * A PredicateExpr is evaluated by evaluating the Expr and converting
9652 * the result to a boolean. If the result is a number, the result will
9653 * be converted to true if the number is equal to the position of the
9654 * context node in the context node list (as returned by the position
9655 * function) and will be converted to false otherwise; if the result
9656 * is not a number, then the result will be converted as if by a call
9657 * to the boolean function.
9658 *
9659 * Return 1 if predicate is true, 0 otherwise
9660 */
9661int
9662xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
9663 xmlXPathObjectPtr res) {
9664 if (res == NULL) return(0);
9665 switch (res->type) {
9666 case XPATH_BOOLEAN:
9667 return(res->boolval);
9668 case XPATH_NUMBER:
9669 return(res->floatval == ctxt->context->proximityPosition);
9670 case XPATH_NODESET:
9671 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00009672 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00009673 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009674 return(res->nodesetval->nodeNr != 0);
9675 case XPATH_STRING:
9676 return((res->stringval != NULL) &&
9677 (xmlStrlen(res->stringval) != 0));
9678 default:
9679 STRANGE
9680 }
9681 return(0);
9682}
9683
9684/**
9685 * xmlXPathCompile:
9686 * @str: the XPath expression
9687 *
9688 * Compile an XPath expression
9689 *
9690 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9691 * the caller has to free the object.
9692 */
9693xmlXPathCompExprPtr
9694xmlXPathCompile(const xmlChar *str) {
9695 xmlXPathParserContextPtr ctxt;
9696 xmlXPathCompExprPtr comp;
9697
9698 xmlXPathInit();
9699
9700 ctxt = xmlXPathNewParserContext(str, NULL);
9701 xmlXPathCompileExpr(ctxt);
9702
Daniel Veillard40af6492001-04-22 08:50:55 +00009703 if (*ctxt->cur != 0) {
9704 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9705 comp = NULL;
9706 } else {
9707 comp = ctxt->comp;
9708 ctxt->comp = NULL;
9709 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009710 xmlXPathFreeParserContext(ctxt);
Daniel Veillardf06307e2001-07-03 10:35:50 +00009711#ifdef DEBUG_EVAL_COUNTS
9712 if (comp != NULL) {
9713 comp->string = xmlStrdup(str);
9714 comp->nb = 0;
9715 }
9716#endif
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009717 return(comp);
9718}
9719
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009720/**
9721 * xmlXPathCompiledEval:
9722 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00009723 * @ctx: the XPath context
9724 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009725 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00009726 *
9727 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9728 * the caller has to free the object.
9729 */
9730xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009731xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00009732 xmlXPathParserContextPtr ctxt;
9733 xmlXPathObjectPtr res, tmp, init = NULL;
9734 int stack = 0;
9735
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009736 if ((comp == NULL) || (ctx == NULL))
9737 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00009738 xmlXPathInit();
9739
9740 CHECK_CONTEXT(ctx)
9741
Daniel Veillardf06307e2001-07-03 10:35:50 +00009742#ifdef DEBUG_EVAL_COUNTS
9743 comp->nb++;
9744 if ((comp->string != NULL) && (comp->nb > 100)) {
9745 fprintf(stderr, "100 x %s\n", comp->string);
9746 comp->nb = 0;
9747 }
9748#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009749 ctxt = xmlXPathCompParserContext(comp, ctx);
9750 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009751
9752 if (ctxt->value == NULL) {
9753 xmlGenericError(xmlGenericErrorContext,
9754 "xmlXPathEval: evaluation failed\n");
9755 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00009756 } else {
9757 res = valuePop(ctxt);
9758 }
9759
Daniel Veillardf06307e2001-07-03 10:35:50 +00009760
Owen Taylor3473f882001-02-23 17:55:21 +00009761 do {
9762 tmp = valuePop(ctxt);
9763 if (tmp != NULL) {
9764 if (tmp != init)
9765 stack++;
9766 xmlXPathFreeObject(tmp);
9767 }
9768 } while (tmp != NULL);
9769 if ((stack != 0) && (res != NULL)) {
9770 xmlGenericError(xmlGenericErrorContext,
9771 "xmlXPathEval: %d object left on the stack\n",
9772 stack);
9773 }
9774 if (ctxt->error != XPATH_EXPRESSION_OK) {
9775 xmlXPathFreeObject(res);
9776 res = NULL;
9777 }
9778
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009779
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009780 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009781 xmlXPathFreeParserContext(ctxt);
9782 return(res);
9783}
9784
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009785/**
9786 * xmlXPathEvalExpr:
9787 * @ctxt: the XPath Parser context
9788 *
9789 * Parse and evaluate an XPath expression in the given context,
9790 * then push the result on the context stack
9791 */
9792void
9793xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
9794 xmlXPathCompileExpr(ctxt);
9795 xmlXPathRunEval(ctxt);
9796}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009797
9798/**
9799 * xmlXPathEval:
9800 * @str: the XPath expression
9801 * @ctx: the XPath context
9802 *
9803 * Evaluate the XPath Location Path in the given context.
9804 *
9805 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9806 * the caller has to free the object.
9807 */
9808xmlXPathObjectPtr
9809xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
9810 xmlXPathParserContextPtr ctxt;
9811 xmlXPathObjectPtr res, tmp, init = NULL;
9812 int stack = 0;
9813
9814 xmlXPathInit();
9815
9816 CHECK_CONTEXT(ctx)
9817
9818 ctxt = xmlXPathNewParserContext(str, ctx);
9819 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009820
9821 if (ctxt->value == NULL) {
9822 xmlGenericError(xmlGenericErrorContext,
9823 "xmlXPathEval: evaluation failed\n");
9824 res = NULL;
9825 } else if (*ctxt->cur != 0) {
9826 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9827 res = NULL;
9828 } else {
9829 res = valuePop(ctxt);
9830 }
9831
9832 do {
9833 tmp = valuePop(ctxt);
9834 if (tmp != NULL) {
9835 if (tmp != init)
9836 stack++;
9837 xmlXPathFreeObject(tmp);
9838 }
9839 } while (tmp != NULL);
9840 if ((stack != 0) && (res != NULL)) {
9841 xmlGenericError(xmlGenericErrorContext,
9842 "xmlXPathEval: %d object left on the stack\n",
9843 stack);
9844 }
9845 if (ctxt->error != XPATH_EXPRESSION_OK) {
9846 xmlXPathFreeObject(res);
9847 res = NULL;
9848 }
9849
Owen Taylor3473f882001-02-23 17:55:21 +00009850 xmlXPathFreeParserContext(ctxt);
9851 return(res);
9852}
9853
9854/**
9855 * xmlXPathEvalExpression:
9856 * @str: the XPath expression
9857 * @ctxt: the XPath context
9858 *
9859 * Evaluate the XPath expression in the given context.
9860 *
9861 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
9862 * the caller has to free the object.
9863 */
9864xmlXPathObjectPtr
9865xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
9866 xmlXPathParserContextPtr pctxt;
9867 xmlXPathObjectPtr res, tmp;
9868 int stack = 0;
9869
9870 xmlXPathInit();
9871
9872 CHECK_CONTEXT(ctxt)
9873
9874 pctxt = xmlXPathNewParserContext(str, ctxt);
9875 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009876
9877 if (*pctxt->cur != 0) {
9878 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9879 res = NULL;
9880 } else {
9881 res = valuePop(pctxt);
9882 }
9883 do {
9884 tmp = valuePop(pctxt);
9885 if (tmp != NULL) {
9886 xmlXPathFreeObject(tmp);
9887 stack++;
9888 }
9889 } while (tmp != NULL);
9890 if ((stack != 0) && (res != NULL)) {
9891 xmlGenericError(xmlGenericErrorContext,
9892 "xmlXPathEvalExpression: %d object left on the stack\n",
9893 stack);
9894 }
9895 xmlXPathFreeParserContext(pctxt);
9896 return(res);
9897}
9898
9899/**
9900 * xmlXPathRegisterAllFunctions:
9901 * @ctxt: the XPath context
9902 *
9903 * Registers all default XPath functions in this context
9904 */
9905void
9906xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
9907{
9908 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
9909 xmlXPathBooleanFunction);
9910 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
9911 xmlXPathCeilingFunction);
9912 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
9913 xmlXPathCountFunction);
9914 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
9915 xmlXPathConcatFunction);
9916 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
9917 xmlXPathContainsFunction);
9918 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
9919 xmlXPathIdFunction);
9920 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
9921 xmlXPathFalseFunction);
9922 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
9923 xmlXPathFloorFunction);
9924 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
9925 xmlXPathLastFunction);
9926 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
9927 xmlXPathLangFunction);
9928 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
9929 xmlXPathLocalNameFunction);
9930 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
9931 xmlXPathNotFunction);
9932 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
9933 xmlXPathNameFunction);
9934 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
9935 xmlXPathNamespaceURIFunction);
9936 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
9937 xmlXPathNormalizeFunction);
9938 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
9939 xmlXPathNumberFunction);
9940 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
9941 xmlXPathPositionFunction);
9942 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
9943 xmlXPathRoundFunction);
9944 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
9945 xmlXPathStringFunction);
9946 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
9947 xmlXPathStringLengthFunction);
9948 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
9949 xmlXPathStartsWithFunction);
9950 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
9951 xmlXPathSubstringFunction);
9952 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
9953 xmlXPathSubstringBeforeFunction);
9954 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
9955 xmlXPathSubstringAfterFunction);
9956 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
9957 xmlXPathSumFunction);
9958 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
9959 xmlXPathTrueFunction);
9960 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
9961 xmlXPathTranslateFunction);
9962}
9963
9964#endif /* LIBXML_XPATH_ENABLED */