blob: 062e6c629340bbc3bee0977ccc71c87cb7ed770a [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 Veillardcda96922001-08-21 10:56:31 +000071#define TRIO_PUBLIC static
72#include "trionan.c"
73
Owen Taylor3473f882001-02-23 17:55:21 +000074/*
Owen Taylor3473f882001-02-23 17:55:21 +000075 * The lack of portability of this section of the libc is annoying !
76 */
77double xmlXPathNAN = 0;
78double xmlXPathPINF = 1;
79double xmlXPathNINF = -1;
80
Owen Taylor3473f882001-02-23 17:55:21 +000081/**
82 * xmlXPathInit:
83 *
84 * Initialize the XPath environment
85 */
86void
87xmlXPathInit(void) {
88 static int initialized = 0;
89
90 if (initialized) return;
91
Bjorn Reese45029602001-08-21 09:23:53 +000092 xmlXPathPINF = trio_pinf();
93 xmlXPathNINF = trio_ninf();
94 xmlXPathNAN = trio_nan();
Owen Taylor3473f882001-02-23 17:55:21 +000095
96 initialized = 1;
97}
98
Daniel Veillardcda96922001-08-21 10:56:31 +000099/**
100 * xmlXPathIsNaN:
101 * @val: a double value
102 *
103 * Provides a portable isnan() function to detect whether a double
104 * is a NotaNumber. Based on trio code
105 * http://sourceforge.net/projects/ctrio/
106 *
107 * Returns 1 if the value is a NaN, 0 otherwise
108 */
109int
110xmlXPathIsNaN(double val) {
111 return(trio_isnan(val));
112}
113
114/**
115 * xmlXPathIsInf:
116 * @val: a double value
117 *
118 * Provides a portable isinf() function to detect whether a double
119 * is a +Infinite or -Infinite. Based on trio code
120 * http://sourceforge.net/projects/ctrio/
121 *
122 * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
123 */
124int
125xmlXPathIsInf(double val) {
126 return(trio_isinf(val));
127}
128
Owen Taylor3473f882001-02-23 17:55:21 +0000129/************************************************************************
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000130 * *
131 * Parser Types *
132 * *
133 ************************************************************************/
134
135/*
136 * Types are private:
137 */
138
139typedef enum {
140 XPATH_OP_END=0,
141 XPATH_OP_AND,
142 XPATH_OP_OR,
143 XPATH_OP_EQUAL,
144 XPATH_OP_CMP,
145 XPATH_OP_PLUS,
146 XPATH_OP_MULT,
147 XPATH_OP_UNION,
148 XPATH_OP_ROOT,
149 XPATH_OP_NODE,
150 XPATH_OP_RESET,
151 XPATH_OP_COLLECT,
152 XPATH_OP_VALUE,
153 XPATH_OP_VARIABLE,
154 XPATH_OP_FUNCTION,
155 XPATH_OP_ARG,
156 XPATH_OP_PREDICATE,
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000157 XPATH_OP_FILTER,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000158 XPATH_OP_SORT
159#ifdef LIBXML_XPTR_ENABLED
160 ,XPATH_OP_RANGETO
161#endif
162} xmlXPathOp;
163
164typedef enum {
165 AXIS_ANCESTOR = 1,
166 AXIS_ANCESTOR_OR_SELF,
167 AXIS_ATTRIBUTE,
168 AXIS_CHILD,
169 AXIS_DESCENDANT,
170 AXIS_DESCENDANT_OR_SELF,
171 AXIS_FOLLOWING,
172 AXIS_FOLLOWING_SIBLING,
173 AXIS_NAMESPACE,
174 AXIS_PARENT,
175 AXIS_PRECEDING,
176 AXIS_PRECEDING_SIBLING,
177 AXIS_SELF
178} xmlXPathAxisVal;
179
180typedef enum {
181 NODE_TEST_NONE = 0,
182 NODE_TEST_TYPE = 1,
183 NODE_TEST_PI = 2,
184 NODE_TEST_ALL = 3,
185 NODE_TEST_NS = 4,
186 NODE_TEST_NAME = 5
187} xmlXPathTestVal;
188
189typedef enum {
190 NODE_TYPE_NODE = 0,
191 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
192 NODE_TYPE_TEXT = XML_TEXT_NODE,
193 NODE_TYPE_PI = XML_PI_NODE
194} xmlXPathTypeVal;
195
196
197typedef struct _xmlXPathStepOp xmlXPathStepOp;
198typedef xmlXPathStepOp *xmlXPathStepOpPtr;
199struct _xmlXPathStepOp {
200 xmlXPathOp op;
201 int ch1;
202 int ch2;
203 int value;
204 int value2;
205 int value3;
206 void *value4;
207 void *value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000208 void *cache;
Daniel Veillard42596ad2001-05-22 16:57:14 +0000209 void *cacheURI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000210};
211
212struct _xmlXPathCompExpr {
213 int nbStep;
214 int maxStep;
215 xmlXPathStepOp *steps; /* ops for computation */
216 int last;
Daniel Veillardf06307e2001-07-03 10:35:50 +0000217#ifdef DEBUG_EVAL_COUNTS
218 int nb;
219 xmlChar *string;
220#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000221};
222
223/************************************************************************
224 * *
225 * Parser Type functions *
226 * *
227 ************************************************************************/
228
229/**
230 * xmlXPathNewCompExpr:
231 *
232 * Create a new Xpath component
233 *
234 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
235 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000236static xmlXPathCompExprPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000237xmlXPathNewCompExpr(void) {
238 xmlXPathCompExprPtr cur;
239
240 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
241 if (cur == NULL) {
242 xmlGenericError(xmlGenericErrorContext,
243 "xmlXPathNewCompExpr : malloc failed\n");
244 return(NULL);
245 }
246 memset(cur, 0, sizeof(xmlXPathCompExpr));
247 cur->maxStep = 10;
248 cur->nbStep = 0;
249 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
250 sizeof(xmlXPathStepOp));
251 if (cur->steps == NULL) {
252 xmlGenericError(xmlGenericErrorContext,
253 "xmlXPathNewCompExpr : malloc failed\n");
254 xmlFree(cur);
255 return(NULL);
256 }
257 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
258 cur->last = -1;
Daniel Veillardf06307e2001-07-03 10:35:50 +0000259#ifdef DEBUG_EVAL_COUNTS
260 cur->nb = 0;
261#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000262 return(cur);
263}
264
265/**
266 * xmlXPathFreeCompExpr:
267 * @comp: an XPATH comp
268 *
269 * Free up the memory allocated by @comp
270 */
271void
Daniel Veillardf06307e2001-07-03 10:35:50 +0000272xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
273{
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000274 xmlXPathStepOpPtr op;
275 int i;
276
277 if (comp == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +0000278 return;
279 for (i = 0; i < comp->nbStep; i++) {
280 op = &comp->steps[i];
281 if (op->value4 != NULL) {
282 if (op->op == XPATH_OP_VALUE)
283 xmlXPathFreeObject(op->value4);
284 else
285 xmlFree(op->value4);
286 }
287 if (op->value5 != NULL)
288 xmlFree(op->value5);
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000289 }
290 if (comp->steps != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +0000291 xmlFree(comp->steps);
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000292 }
Daniel Veillardf06307e2001-07-03 10:35:50 +0000293#ifdef DEBUG_EVAL_COUNTS
294 if (comp->string != NULL) {
295 xmlFree(comp->string);
296 }
297#endif
298
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000299 xmlFree(comp);
300}
301
302/**
303 * xmlXPathCompExprAdd:
304 * @comp: the compiled expression
305 * @ch1: first child index
306 * @ch2: second child index
307 * @op: an op
308 * @value: the first int value
309 * @value2: the second int value
310 * @value3: the third int value
311 * @value4: the first string value
312 * @value5: the second string value
313 *
314 * Add an step to an XPath Compiled Expression
315 *
316 * Returns -1 in case of failure, the index otherwise
317 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000318static int
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000319xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
320 xmlXPathOp op, int value,
321 int value2, int value3, void *value4, void *value5) {
322 if (comp->nbStep >= comp->maxStep) {
323 xmlXPathStepOp *real;
324
325 comp->maxStep *= 2;
326 real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
327 comp->maxStep * sizeof(xmlXPathStepOp));
328 if (real == NULL) {
329 comp->maxStep /= 2;
330 xmlGenericError(xmlGenericErrorContext,
331 "xmlXPathCompExprAdd : realloc failed\n");
332 return(-1);
333 }
334 comp->steps = real;
335 }
336 comp->last = comp->nbStep;
337 comp->steps[comp->nbStep].ch1 = ch1;
338 comp->steps[comp->nbStep].ch2 = ch2;
339 comp->steps[comp->nbStep].op = op;
340 comp->steps[comp->nbStep].value = value;
341 comp->steps[comp->nbStep].value2 = value2;
342 comp->steps[comp->nbStep].value3 = value3;
343 comp->steps[comp->nbStep].value4 = value4;
344 comp->steps[comp->nbStep].value5 = value5;
Daniel Veillarde39a93d2001-04-28 14:35:02 +0000345 comp->steps[comp->nbStep].cache = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000346 return(comp->nbStep++);
347}
348
Daniel Veillardf06307e2001-07-03 10:35:50 +0000349/**
350 * xmlXPathCompSwap:
351 * @comp: the compiled expression
352 * @op: operation index
353 *
354 * Swaps 2 operations in the compiled expression
355 * TODO: not thread safe, disable for multi-thread operations
356 *
357 * Returns -1 in case of failure, the index otherwise
358 */
359static void
360xmlXPathCompSwap(xmlXPathStepOpPtr op) {
361 int tmp;
362
363 tmp = op->ch1;
364 op->ch1 = op->ch2;
365 op->ch2 = tmp;
366}
367
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000368#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
369 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
370 (op), (val), (val2), (val3), (val4), (val5))
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000371#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
372 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
373 (op), (val), (val2), (val3), (val4), (val5))
374
375#define PUSH_LEAVE_EXPR(op, val, val2) \
376xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
377
378#define PUSH_UNARY_EXPR(op, ch, val, val2) \
379xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
380
381#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
382xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL)
383
384/************************************************************************
Owen Taylor3473f882001-02-23 17:55:21 +0000385 * *
386 * Debugging related functions *
387 * *
388 ************************************************************************/
389
390#define TODO \
391 xmlGenericError(xmlGenericErrorContext, \
392 "Unimplemented block at %s:%d\n", \
393 __FILE__, __LINE__);
394
395#define STRANGE \
396 xmlGenericError(xmlGenericErrorContext, \
397 "Internal error at %s:%d\n", \
398 __FILE__, __LINE__);
399
400#ifdef LIBXML_DEBUG_ENABLED
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000401static void
402xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000403 int i;
404 char shift[100];
405
406 for (i = 0;((i < depth) && (i < 25));i++)
407 shift[2 * i] = shift[2 * i + 1] = ' ';
408 shift[2 * i] = shift[2 * i + 1] = 0;
409 if (cur == NULL) {
410 fprintf(output, shift);
411 fprintf(output, "Node is NULL !\n");
412 return;
413
414 }
415
416 if ((cur->type == XML_DOCUMENT_NODE) ||
417 (cur->type == XML_HTML_DOCUMENT_NODE)) {
418 fprintf(output, shift);
419 fprintf(output, " /\n");
420 } else if (cur->type == XML_ATTRIBUTE_NODE)
421 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
422 else
423 xmlDebugDumpOneNode(output, cur, depth);
424}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000425static void
426xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000427 xmlNodePtr tmp;
428 int i;
429 char shift[100];
430
431 for (i = 0;((i < depth) && (i < 25));i++)
432 shift[2 * i] = shift[2 * i + 1] = ' ';
433 shift[2 * i] = shift[2 * i + 1] = 0;
434 if (cur == NULL) {
435 fprintf(output, shift);
436 fprintf(output, "Node is NULL !\n");
437 return;
438
439 }
440
441 while (cur != NULL) {
442 tmp = cur;
443 cur = cur->next;
444 xmlDebugDumpOneNode(output, tmp, depth);
445 }
446}
Owen Taylor3473f882001-02-23 17:55:21 +0000447
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000448static void
449xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000450 int i;
451 char shift[100];
452
453 for (i = 0;((i < depth) && (i < 25));i++)
454 shift[2 * i] = shift[2 * i + 1] = ' ';
455 shift[2 * i] = shift[2 * i + 1] = 0;
456
457 if (cur == NULL) {
458 fprintf(output, shift);
459 fprintf(output, "NodeSet is NULL !\n");
460 return;
461
462 }
463
Daniel Veillard911f49a2001-04-07 15:39:35 +0000464 if (cur != NULL) {
465 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
466 for (i = 0;i < cur->nodeNr;i++) {
467 fprintf(output, shift);
468 fprintf(output, "%d", i + 1);
469 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
470 }
Owen Taylor3473f882001-02-23 17:55:21 +0000471 }
472}
473
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000474static void
475xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000476 int i;
477 char shift[100];
478
479 for (i = 0;((i < depth) && (i < 25));i++)
480 shift[2 * i] = shift[2 * i + 1] = ' ';
481 shift[2 * i] = shift[2 * i + 1] = 0;
482
483 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
484 fprintf(output, shift);
485 fprintf(output, "Value Tree is NULL !\n");
486 return;
487
488 }
489
490 fprintf(output, shift);
491 fprintf(output, "%d", i + 1);
492 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
493}
Owen Taylor3473f882001-02-23 17:55:21 +0000494#if defined(LIBXML_XPTR_ENABLED)
495void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000496static void
497xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000498 int i;
499 char shift[100];
500
501 for (i = 0;((i < depth) && (i < 25));i++)
502 shift[2 * i] = shift[2 * i + 1] = ' ';
503 shift[2 * i] = shift[2 * i + 1] = 0;
504
505 if (cur == NULL) {
506 fprintf(output, shift);
507 fprintf(output, "LocationSet is NULL !\n");
508 return;
509
510 }
511
512 for (i = 0;i < cur->locNr;i++) {
513 fprintf(output, shift);
514 fprintf(output, "%d : ", i + 1);
515 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
516 }
517}
Daniel Veillard017b1082001-06-21 11:20:21 +0000518#endif /* LIBXML_XPTR_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000519
Daniel Veillardafcbe1c2001-03-19 10:57:13 +0000520/**
521 * xmlXPathDebugDumpObject:
522 * @output: the FILE * to dump the output
523 * @cur: the object to inspect
524 * @depth: indentation level
525 *
526 * Dump the content of the object for debugging purposes
527 */
528void
529xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
Owen Taylor3473f882001-02-23 17:55:21 +0000530 int i;
531 char shift[100];
532
533 for (i = 0;((i < depth) && (i < 25));i++)
534 shift[2 * i] = shift[2 * i + 1] = ' ';
535 shift[2 * i] = shift[2 * i + 1] = 0;
536
537 fprintf(output, shift);
538
539 if (cur == NULL) {
540 fprintf(output, "Object is empty (NULL)\n");
541 return;
542 }
543 switch(cur->type) {
544 case XPATH_UNDEFINED:
545 fprintf(output, "Object is uninitialized\n");
546 break;
547 case XPATH_NODESET:
548 fprintf(output, "Object is a Node Set :\n");
549 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
550 break;
551 case XPATH_XSLT_TREE:
552 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000553 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000554 break;
555 case XPATH_BOOLEAN:
556 fprintf(output, "Object is a Boolean : ");
557 if (cur->boolval) fprintf(output, "true\n");
558 else fprintf(output, "false\n");
559 break;
560 case XPATH_NUMBER:
Daniel Veillardcda96922001-08-21 10:56:31 +0000561 switch (xmlXPathIsInf(cur->floatval)) {
Daniel Veillard357c9602001-05-03 10:49:20 +0000562 case 1:
563 fprintf(output, "Object is a number : +Infinity\n");
564 break;
565 case -1:
566 fprintf(output, "Object is a number : -Infinity\n");
567 break;
568 default:
Daniel Veillardcda96922001-08-21 10:56:31 +0000569 if (xmlXPathIsNaN(cur->floatval)) {
Daniel Veillard357c9602001-05-03 10:49:20 +0000570 fprintf(output, "Object is a number : NaN\n");
571 } else {
572 fprintf(output, "Object is a number : %0g\n", cur->floatval);
573 }
574 }
Owen Taylor3473f882001-02-23 17:55:21 +0000575 break;
576 case XPATH_STRING:
577 fprintf(output, "Object is a string : ");
578 xmlDebugDumpString(output, cur->stringval);
579 fprintf(output, "\n");
580 break;
581 case XPATH_POINT:
582 fprintf(output, "Object is a point : index %d in node", cur->index);
583 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
584 fprintf(output, "\n");
585 break;
586 case XPATH_RANGE:
587 if ((cur->user2 == NULL) ||
588 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
589 fprintf(output, "Object is a collapsed range :\n");
590 fprintf(output, shift);
591 if (cur->index >= 0)
592 fprintf(output, "index %d in ", cur->index);
593 fprintf(output, "node\n");
594 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
595 depth + 1);
596 } else {
597 fprintf(output, "Object is a range :\n");
598 fprintf(output, shift);
599 fprintf(output, "From ");
600 if (cur->index >= 0)
601 fprintf(output, "index %d in ", cur->index);
602 fprintf(output, "node\n");
603 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
604 depth + 1);
605 fprintf(output, shift);
606 fprintf(output, "To ");
607 if (cur->index2 >= 0)
608 fprintf(output, "index %d in ", cur->index2);
609 fprintf(output, "node\n");
610 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
611 depth + 1);
612 fprintf(output, "\n");
613 }
614 break;
615 case XPATH_LOCATIONSET:
616#if defined(LIBXML_XPTR_ENABLED)
617 fprintf(output, "Object is a Location Set:\n");
618 xmlXPathDebugDumpLocationSet(output,
619 (xmlLocationSetPtr) cur->user, depth);
620#endif
621 break;
622 case XPATH_USERS:
623 fprintf(output, "Object is user defined\n");
624 break;
625 }
626}
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000627
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000628static void
629xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp,
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000630 xmlXPathStepOpPtr op, int depth) {
631 int i;
632 char shift[100];
633
634 for (i = 0;((i < depth) && (i < 25));i++)
635 shift[2 * i] = shift[2 * i + 1] = ' ';
636 shift[2 * i] = shift[2 * i + 1] = 0;
637
638 fprintf(output, shift);
639 if (op == NULL) {
640 fprintf(output, "Step is NULL\n");
641 return;
642 }
643 switch (op->op) {
644 case XPATH_OP_END:
645 fprintf(output, "END"); break;
646 case XPATH_OP_AND:
647 fprintf(output, "AND"); break;
648 case XPATH_OP_OR:
649 fprintf(output, "OR"); break;
650 case XPATH_OP_EQUAL:
651 if (op->value)
652 fprintf(output, "EQUAL =");
653 else
654 fprintf(output, "EQUAL !=");
655 break;
656 case XPATH_OP_CMP:
657 if (op->value)
658 fprintf(output, "CMP <");
659 else
660 fprintf(output, "CMP >");
661 if (!op->value2)
662 fprintf(output, "=");
663 break;
664 case XPATH_OP_PLUS:
665 if (op->value == 0)
666 fprintf(output, "PLUS -");
667 else if (op->value == 1)
668 fprintf(output, "PLUS +");
669 else if (op->value == 2)
670 fprintf(output, "PLUS unary -");
671 else if (op->value == 3)
672 fprintf(output, "PLUS unary - -");
673 break;
674 case XPATH_OP_MULT:
675 if (op->value == 0)
676 fprintf(output, "MULT *");
677 else if (op->value == 1)
678 fprintf(output, "MULT div");
679 else
680 fprintf(output, "MULT mod");
681 break;
682 case XPATH_OP_UNION:
683 fprintf(output, "UNION"); break;
684 case XPATH_OP_ROOT:
685 fprintf(output, "ROOT"); break;
686 case XPATH_OP_NODE:
687 fprintf(output, "NODE"); break;
688 case XPATH_OP_RESET:
689 fprintf(output, "RESET"); break;
690 case XPATH_OP_SORT:
691 fprintf(output, "SORT"); break;
692 case XPATH_OP_COLLECT: {
693 xmlXPathAxisVal axis = op->value;
694 xmlXPathTestVal test = op->value2;
695 xmlXPathTypeVal type = op->value3;
696 const xmlChar *prefix = op->value4;
697 const xmlChar *name = op->value5;
698
699 fprintf(output, "COLLECT ");
700 switch (axis) {
701 case AXIS_ANCESTOR:
702 fprintf(output, " 'ancestors' "); break;
703 case AXIS_ANCESTOR_OR_SELF:
704 fprintf(output, " 'ancestors-or-self' "); break;
705 case AXIS_ATTRIBUTE:
706 fprintf(output, " 'attributes' "); break;
707 case AXIS_CHILD:
708 fprintf(output, " 'child' "); break;
709 case AXIS_DESCENDANT:
710 fprintf(output, " 'descendant' "); break;
711 case AXIS_DESCENDANT_OR_SELF:
712 fprintf(output, " 'descendant-or-self' "); break;
713 case AXIS_FOLLOWING:
714 fprintf(output, " 'following' "); break;
715 case AXIS_FOLLOWING_SIBLING:
716 fprintf(output, " 'following-siblings' "); break;
717 case AXIS_NAMESPACE:
718 fprintf(output, " 'namespace' "); break;
719 case AXIS_PARENT:
720 fprintf(output, " 'parent' "); break;
721 case AXIS_PRECEDING:
722 fprintf(output, " 'preceding' "); break;
723 case AXIS_PRECEDING_SIBLING:
724 fprintf(output, " 'preceding-sibling' "); break;
725 case AXIS_SELF:
726 fprintf(output, " 'self' "); break;
727 }
728 switch (test) {
729 case NODE_TEST_NONE:
730 fprintf(output, "'none' "); break;
731 case NODE_TEST_TYPE:
732 fprintf(output, "'type' "); break;
733 case NODE_TEST_PI:
734 fprintf(output, "'PI' "); break;
735 case NODE_TEST_ALL:
736 fprintf(output, "'all' "); break;
737 case NODE_TEST_NS:
738 fprintf(output, "'namespace' "); break;
739 case NODE_TEST_NAME:
740 fprintf(output, "'name' "); break;
741 }
742 switch (type) {
743 case NODE_TYPE_NODE:
744 fprintf(output, "'node' "); break;
745 case NODE_TYPE_COMMENT:
746 fprintf(output, "'comment' "); break;
747 case NODE_TYPE_TEXT:
748 fprintf(output, "'text' "); break;
749 case NODE_TYPE_PI:
750 fprintf(output, "'PI' "); break;
751 }
752 if (prefix != NULL)
753 fprintf(output, "%s:", prefix);
754 if (name != NULL)
755 fprintf(output, "%s", name);
756 break;
757
758 }
759 case XPATH_OP_VALUE: {
760 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4;
761
762 fprintf(output, "ELEM ");
763 xmlXPathDebugDumpObject(output, object, 0);
764 goto finish;
765 }
766 case XPATH_OP_VARIABLE: {
767 const xmlChar *prefix = op->value5;
768 const xmlChar *name = op->value4;
769
770 if (prefix != NULL)
771 fprintf(output, "VARIABLE %s:%s", prefix, name);
772 else
773 fprintf(output, "VARIABLE %s", name);
774 break;
775 }
776 case XPATH_OP_FUNCTION: {
777 int nbargs = op->value;
778 const xmlChar *prefix = op->value5;
779 const xmlChar *name = op->value4;
780
781 if (prefix != NULL)
782 fprintf(output, "FUNCTION %s:%s(%d args)",
783 prefix, name, nbargs);
784 else
785 fprintf(output, "FUNCTION %s(%d args)", name, nbargs);
786 break;
787 }
788 case XPATH_OP_ARG: fprintf(output, "ARG"); break;
789 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +0000790 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +0000791#ifdef LIBXML_XPTR_ENABLED
792 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break;
793#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000794 default:
795 fprintf(output, "UNKNOWN %d\n", op->op); return;
796 }
797 fprintf(output, "\n");
798finish:
799 if (op->ch1 >= 0)
800 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1);
801 if (op->ch2 >= 0)
802 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1);
803}
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000804
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000805/**
806 * xmlXPathDebugDumpCompExpr:
807 * @output: the FILE * for the output
808 * @comp: the precompiled XPath expression
809 * @depth: the indentation level.
810 *
811 * Dumps the tree of the compiled XPath expression.
812 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000813void
814xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
815 int depth) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000816 int i;
817 char shift[100];
818
819 for (i = 0;((i < depth) && (i < 25));i++)
820 shift[2 * i] = shift[2 * i + 1] = ' ';
821 shift[2 * i] = shift[2 * i + 1] = 0;
822
823 fprintf(output, shift);
824
825 if (comp == NULL) {
826 fprintf(output, "Compiled Expression is NULL\n");
827 return;
828 }
829 fprintf(output, "Compiled Expression : %d elements\n",
830 comp->nbStep);
831 i = comp->last;
832 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
833}
Daniel Veillard017b1082001-06-21 11:20:21 +0000834#endif /* LIBXML_DEBUG_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +0000835
836/************************************************************************
837 * *
838 * Parser stacks related functions and macros *
839 * *
840 ************************************************************************/
841
842/*
843 * Generic function for accessing stacks in the Parser Context
844 */
845
846#define PUSH_AND_POP(type, name) \
847extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
848 if (ctxt->name##Nr >= ctxt->name##Max) { \
849 ctxt->name##Max *= 2; \
850 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
851 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
852 if (ctxt->name##Tab == NULL) { \
853 xmlGenericError(xmlGenericErrorContext, \
854 "realloc failed !\n"); \
855 return(0); \
856 } \
857 } \
858 ctxt->name##Tab[ctxt->name##Nr] = value; \
859 ctxt->name = value; \
860 return(ctxt->name##Nr++); \
861} \
862extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
863 type ret; \
864 if (ctxt->name##Nr <= 0) return(0); \
865 ctxt->name##Nr--; \
866 if (ctxt->name##Nr > 0) \
867 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
868 else \
869 ctxt->name = NULL; \
870 ret = ctxt->name##Tab[ctxt->name##Nr]; \
871 ctxt->name##Tab[ctxt->name##Nr] = 0; \
872 return(ret); \
873} \
874
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000875/**
876 * valuePop:
877 * @ctxt: an XPath evaluation context
878 *
879 * Pops the top XPath object from the value stack
880 *
881 * Returns the XPath object just removed
882 */
883/**
884 * valuePush:
885 * @ctxt: an XPath evaluation context
886 * @value: the XPath object
887 *
888 * Pushes a new XPath object on top of the value stack
889 */
Owen Taylor3473f882001-02-23 17:55:21 +0000890PUSH_AND_POP(xmlXPathObjectPtr, value)
891
Thomas Broyerf06a3d82001-07-16 04:52:57 +0000892/**
893 * xmlXPathPopBoolean:
894 * @ctxt: an XPath parser context
895 *
896 * Pops a boolean from the stack, handling conversion if needed.
897 * Check error with #xmlXPathCheckError.
898 *
899 * Returns the boolean
900 */
901int
902xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
903 xmlXPathObjectPtr obj;
904 int ret;
905
906 obj = valuePop(ctxt);
907 if (obj == NULL) {
908 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
909 return(0);
910 }
911 ret = xmlXPathCastToBoolean(obj);
912 xmlXPathFreeObject(obj);
913 return(ret);
914}
915
916/**
917 * xmlXPathPopNumber:
918 * @ctxt: an XPath parser context
919 *
920 * Pops a number from the stack, handling conversion if needed.
921 * Check error with #xmlXPathCheckError.
922 *
923 * Returns the number
924 */
925double
926xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
927 xmlXPathObjectPtr obj;
928 double ret;
929
930 obj = valuePop(ctxt);
931 if (obj == NULL) {
932 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
933 return(0);
934 }
935 ret = xmlXPathCastToNumber(obj);
936 xmlXPathFreeObject(obj);
937 return(ret);
938}
939
940/**
941 * xmlXPathPopString:
942 * @ctxt: an XPath parser context
943 *
944 * Pops a string from the stack, handling conversion if needed.
945 * Check error with #xmlXPathCheckError.
946 *
947 * Returns the string
948 */
949xmlChar *
950xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
951 xmlXPathObjectPtr obj;
952 xmlChar * ret;
953
954 obj = valuePop(ctxt);
955 if (obj == NULL) {
956 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
957 return(NULL);
958 }
959 ret = xmlXPathCastToString(obj);
960 /* TODO: needs refactoring somewhere else */
961 if (obj->stringval == ret)
962 obj->stringval = NULL;
963 xmlXPathFreeObject(obj);
964 return(ret);
965}
966
967/**
968 * xmlXPathPopNodeSet:
969 * @ctxt: an XPath parser context
970 *
971 * Pops a node-set from the stack, handling conversion if needed.
972 * Check error with #xmlXPathCheckError.
973 *
974 * Returns the node-set
975 */
976xmlNodeSetPtr
977xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
978 xmlXPathObjectPtr obj;
979 xmlNodeSetPtr ret;
980
981 if (ctxt->value == NULL) {
982 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
983 return(NULL);
984 }
985 if (!xmlXPathStackIsNodeSet(ctxt)) {
986 xmlXPathSetTypeError(ctxt);
987 return(NULL);
988 }
989 obj = valuePop(ctxt);
990 ret = obj->nodesetval;
991 xmlXPathFreeNodeSetList(obj);
992 return(ret);
993}
994
995/**
996 * xmlXPathPopExternal:
997 * @ctxt: an XPath parser context
998 *
999 * Pops an external oject from the stack, handling conversion if needed.
1000 * Check error with #xmlXPathCheckError.
1001 *
1002 * Returns the object
1003 */
1004void *
1005xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
1006 xmlXPathObjectPtr obj;
1007 void * ret;
1008
1009 if (ctxt->value == NULL) {
1010 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
1011 return(NULL);
1012 }
1013 if (ctxt->value->type != XPATH_USERS) {
1014 xmlXPathSetTypeError(ctxt);
1015 return(NULL);
1016 }
1017 obj = valuePop(ctxt);
1018 ret = obj->user;
1019 xmlXPathFreeObject(obj);
1020 return(ret);
1021}
1022
Owen Taylor3473f882001-02-23 17:55:21 +00001023/*
1024 * Macros for accessing the content. Those should be used only by the parser,
1025 * and not exported.
1026 *
1027 * Dirty macros, i.e. one need to make assumption on the context to use them
1028 *
1029 * CUR_PTR return the current pointer to the xmlChar to be parsed.
1030 * CUR returns the current xmlChar value, i.e. a 8 bit value
1031 * in ISO-Latin or UTF-8.
1032 * This should be used internally by the parser
1033 * only to compare to ASCII values otherwise it would break when
1034 * running with UTF-8 encoding.
1035 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
1036 * to compare on ASCII based substring.
1037 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
1038 * strings within the parser.
1039 * CURRENT Returns the current char value, with the full decoding of
1040 * UTF-8 if we are using this mode. It returns an int.
1041 * NEXT Skip to the next character, this does the proper decoding
1042 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
1043 * It returns the pointer to the current xmlChar.
1044 */
1045
1046#define CUR (*ctxt->cur)
1047#define SKIP(val) ctxt->cur += (val)
1048#define NXT(val) ctxt->cur[(val)]
1049#define CUR_PTR ctxt->cur
Daniel Veillard61d80a22001-04-27 17:13:01 +00001050#define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l)
1051
1052#define COPY_BUF(l,b,i,v) \
1053 if (l == 1) b[i++] = (xmlChar) v; \
1054 else i += xmlCopyChar(l,&b[i],v)
1055
1056#define NEXTL(l) ctxt->cur += l
Owen Taylor3473f882001-02-23 17:55:21 +00001057
1058#define SKIP_BLANKS \
1059 while (IS_BLANK(*(ctxt->cur))) NEXT
1060
1061#define CURRENT (*ctxt->cur)
1062#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1063
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001064
1065#ifndef DBL_DIG
1066#define DBL_DIG 16
1067#endif
1068#ifndef DBL_EPSILON
1069#define DBL_EPSILON 1E-9
1070#endif
1071
1072#define UPPER_DOUBLE 1E9
1073#define LOWER_DOUBLE 1E-5
1074
1075#define INTEGER_DIGITS DBL_DIG
1076#define FRACTION_DIGITS (DBL_DIG + 1)
1077#define EXPONENT_DIGITS (3 + 2)
1078
1079/**
1080 * xmlXPathFormatNumber:
1081 * @number: number to format
1082 * @buffer: output buffer
1083 * @buffersize: size of output buffer
1084 *
1085 * Convert the number into a string representation.
1086 */
1087static void
1088xmlXPathFormatNumber(double number, char buffer[], int buffersize)
1089{
Daniel Veillardcda96922001-08-21 10:56:31 +00001090 switch (xmlXPathIsInf(number)) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001091 case 1:
1092 if (buffersize > (int)sizeof("+Infinity"))
1093 sprintf(buffer, "+Infinity");
1094 break;
1095 case -1:
1096 if (buffersize > (int)sizeof("-Infinity"))
1097 sprintf(buffer, "-Infinity");
1098 break;
1099 default:
Daniel Veillardcda96922001-08-21 10:56:31 +00001100 if (xmlXPathIsNaN(number)) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001101 if (buffersize > (int)sizeof("NaN"))
1102 sprintf(buffer, "NaN");
1103 } else {
Bjorn Reese70a9da52001-04-21 16:57:29 +00001104 /* 3 is sign, decimal point, and terminating zero */
1105 char work[DBL_DIG + EXPONENT_DIGITS + 3];
1106 int integer_place, fraction_place;
1107 char *ptr;
1108 char *after_fraction;
1109 double absolute_value;
1110 int size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001111
Bjorn Reese70a9da52001-04-21 16:57:29 +00001112 absolute_value = fabs(number);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001113
Bjorn Reese70a9da52001-04-21 16:57:29 +00001114 /*
1115 * First choose format - scientific or regular floating point.
1116 * In either case, result is in work, and after_fraction points
1117 * just past the fractional part.
1118 */
1119 if ( ((absolute_value > UPPER_DOUBLE) ||
1120 (absolute_value < LOWER_DOUBLE)) &&
1121 (absolute_value != 0.0) ) {
1122 /* Use scientific notation */
1123 integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
1124 fraction_place = DBL_DIG - 1;
1125 snprintf(work, sizeof(work),"%*.*e",
1126 integer_place, fraction_place, number);
1127 after_fraction = strchr(work + DBL_DIG, 'e');
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001128 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00001129 else {
1130 /* Use regular notation */
Daniel Veillard56f06462001-06-24 21:34:03 +00001131 if (absolute_value > 0.0)
1132 integer_place = 1 + (int)log10(absolute_value);
1133 else
Daniel Veillarda3067d12001-06-24 21:39:39 +00001134 integer_place = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00001135 fraction_place = (integer_place > 0)
1136 ? DBL_DIG - integer_place
1137 : DBL_DIG;
1138 size = snprintf(work, sizeof(work), "%0.*f",
1139 fraction_place, number);
1140 after_fraction = work + size;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001141 }
1142
Bjorn Reese70a9da52001-04-21 16:57:29 +00001143 /* Remove fractional trailing zeroes */
1144 ptr = after_fraction;
1145 while (*(--ptr) == '0')
1146 ;
1147 if (*ptr != '.')
1148 ptr++;
1149 strcpy(ptr, after_fraction);
1150
1151 /* Finally copy result back to caller */
1152 size = strlen(work) + 1;
1153 if (size > buffersize) {
1154 work[buffersize - 1] = 0;
1155 size = buffersize;
1156 }
1157 memcpy(buffer, work, size);
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001158 }
1159 break;
1160 }
1161}
1162
Owen Taylor3473f882001-02-23 17:55:21 +00001163/************************************************************************
1164 * *
1165 * Error handling routines *
1166 * *
1167 ************************************************************************/
1168
1169
1170const char *xmlXPathErrorMessages[] = {
1171 "Ok",
1172 "Number encoding",
1173 "Unfinished litteral",
1174 "Start of litteral",
1175 "Expected $ for variable reference",
1176 "Undefined variable",
1177 "Invalid predicate",
1178 "Invalid expression",
1179 "Missing closing curly brace",
1180 "Unregistered function",
1181 "Invalid operand",
1182 "Invalid type",
1183 "Invalid number of arguments",
1184 "Invalid context size",
1185 "Invalid context position",
1186 "Memory allocation error",
1187 "Syntax error",
1188 "Resource error",
1189 "Sub resource error",
Daniel Veillard61d80a22001-04-27 17:13:01 +00001190 "Undefined namespace prefix",
1191 "Encoding error",
1192 "Char out of XML range"
Owen Taylor3473f882001-02-23 17:55:21 +00001193};
1194
1195/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001196 * xmlXPatherror:
Owen Taylor3473f882001-02-23 17:55:21 +00001197 * @ctxt: the XPath Parser context
1198 * @file: the file name
1199 * @line: the line number
1200 * @no: the error number
1201 *
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001202 * Formats an error message.
Owen Taylor3473f882001-02-23 17:55:21 +00001203 */
1204void
1205xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
1206 int line, int no) {
1207 int n;
1208 const xmlChar *cur;
1209 const xmlChar *base;
1210
1211 xmlGenericError(xmlGenericErrorContext,
1212 "Error %s:%d: %s\n", file, line,
1213 xmlXPathErrorMessages[no]);
1214
1215 cur = ctxt->cur;
1216 base = ctxt->base;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001217 if ((cur == NULL) || (base == NULL))
1218 return;
1219
Owen Taylor3473f882001-02-23 17:55:21 +00001220 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
1221 cur--;
1222 }
1223 n = 0;
1224 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
1225 cur--;
1226 if ((*cur == '\n') || (*cur == '\r')) cur++;
1227 base = cur;
1228 n = 0;
1229 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
1230 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
1231 n++;
1232 }
1233 xmlGenericError(xmlGenericErrorContext, "\n");
1234 cur = ctxt->cur;
1235 while ((*cur == '\n') || (*cur == '\r'))
1236 cur--;
1237 n = 0;
1238 while ((cur != base) && (n++ < 80)) {
1239 xmlGenericError(xmlGenericErrorContext, " ");
1240 base++;
1241 }
1242 xmlGenericError(xmlGenericErrorContext,"^\n");
1243}
1244
1245
1246/************************************************************************
1247 * *
1248 * Routines to handle NodeSets *
1249 * *
1250 ************************************************************************/
1251
1252/**
1253 * xmlXPathCmpNodes:
1254 * @node1: the first node
1255 * @node2: the second node
1256 *
1257 * Compare two nodes w.r.t document order
1258 *
1259 * Returns -2 in case of error 1 if first point < second point, 0 if
1260 * that's the same node, -1 otherwise
1261 */
1262int
1263xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
1264 int depth1, depth2;
1265 xmlNodePtr cur, root;
1266
1267 if ((node1 == NULL) || (node2 == NULL))
1268 return(-2);
1269 /*
1270 * a couple of optimizations which will avoid computations in most cases
1271 */
1272 if (node1 == node2)
1273 return(0);
Daniel Veillardb33c2012001-04-25 12:59:04 +00001274 if ((node1->type == XML_NAMESPACE_DECL) ||
1275 (node2->type == XML_NAMESPACE_DECL))
1276 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00001277 if (node1 == node2->prev)
1278 return(1);
1279 if (node1 == node2->next)
1280 return(-1);
1281
1282 /*
1283 * compute depth to root
1284 */
1285 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
1286 if (cur == node1)
1287 return(1);
1288 depth2++;
1289 }
1290 root = cur;
1291 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
1292 if (cur == node2)
1293 return(-1);
1294 depth1++;
1295 }
1296 /*
1297 * Distinct document (or distinct entities :-( ) case.
1298 */
1299 if (root != cur) {
1300 return(-2);
1301 }
1302 /*
1303 * get the nearest common ancestor.
1304 */
1305 while (depth1 > depth2) {
1306 depth1--;
1307 node1 = node1->parent;
1308 }
1309 while (depth2 > depth1) {
1310 depth2--;
1311 node2 = node2->parent;
1312 }
1313 while (node1->parent != node2->parent) {
1314 node1 = node1->parent;
1315 node2 = node2->parent;
1316 /* should not happen but just in case ... */
1317 if ((node1 == NULL) || (node2 == NULL))
1318 return(-2);
1319 }
1320 /*
1321 * Find who's first.
1322 */
1323 if (node1 == node2->next)
1324 return(-1);
1325 for (cur = node1->next;cur != NULL;cur = cur->next)
1326 if (cur == node2)
1327 return(1);
1328 return(-1); /* assume there is no sibling list corruption */
1329}
1330
1331/**
1332 * xmlXPathNodeSetSort:
1333 * @set: the node set
1334 *
1335 * Sort the node set in document order
1336 */
1337void
1338xmlXPathNodeSetSort(xmlNodeSetPtr set) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001339 int i, j, incr, len;
Owen Taylor3473f882001-02-23 17:55:21 +00001340 xmlNodePtr tmp;
1341
1342 if (set == NULL)
1343 return;
1344
1345 /* Use Shell's sort to sort the node-set */
1346 len = set->nodeNr;
1347 for (incr = len / 2; incr > 0; incr /= 2) {
1348 for (i = incr; i < len; i++) {
1349 j = i - incr;
1350 while (j >= 0) {
Bjorn Reesee1dc0112001-03-03 12:09:03 +00001351 if (xmlXPathCmpNodes(set->nodeTab[j],
1352 set->nodeTab[j + incr]) == -1) {
Owen Taylor3473f882001-02-23 17:55:21 +00001353 tmp = set->nodeTab[j];
1354 set->nodeTab[j] = set->nodeTab[j + incr];
1355 set->nodeTab[j + incr] = tmp;
1356 j -= incr;
1357 } else
1358 break;
1359 }
1360 }
1361 }
1362}
1363
1364#define XML_NODESET_DEFAULT 10
1365/**
1366 * xmlXPathNodeSetCreate:
1367 * @val: an initial xmlNodePtr, or NULL
1368 *
1369 * Create a new xmlNodeSetPtr of type double and of value @val
1370 *
1371 * Returns the newly created object.
1372 */
1373xmlNodeSetPtr
1374xmlXPathNodeSetCreate(xmlNodePtr val) {
1375 xmlNodeSetPtr ret;
1376
1377 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
1378 if (ret == NULL) {
1379 xmlGenericError(xmlGenericErrorContext,
1380 "xmlXPathNewNodeSet: out of memory\n");
1381 return(NULL);
1382 }
1383 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
1384 if (val != NULL) {
1385 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1386 sizeof(xmlNodePtr));
1387 if (ret->nodeTab == NULL) {
1388 xmlGenericError(xmlGenericErrorContext,
1389 "xmlXPathNewNodeSet: out of memory\n");
1390 return(NULL);
1391 }
1392 memset(ret->nodeTab, 0 ,
1393 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1394 ret->nodeMax = XML_NODESET_DEFAULT;
1395 ret->nodeTab[ret->nodeNr++] = val;
1396 }
1397 return(ret);
1398}
1399
1400/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001401 * xmlXPathNodeSetContains:
1402 * @cur: the node-set
1403 * @val: the node
1404 *
1405 * checks whether @cur contains @val
1406 *
1407 * Returns true (1) if @cur contains @val, false (0) otherwise
1408 */
1409int
1410xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
1411 int i;
1412
1413 for (i = 0; i < cur->nodeNr; i++) {
1414 if (cur->nodeTab[i] == val)
1415 return(1);
1416 }
1417 return(0);
1418}
1419
1420/**
Owen Taylor3473f882001-02-23 17:55:21 +00001421 * xmlXPathNodeSetAdd:
1422 * @cur: the initial node set
1423 * @val: a new xmlNodePtr
1424 *
1425 * add a new xmlNodePtr ot an existing NodeSet
1426 */
1427void
1428xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
1429 int i;
1430
1431 if (val == NULL) return;
1432
1433 /*
1434 * check against doublons
1435 */
1436 for (i = 0;i < cur->nodeNr;i++)
1437 if (cur->nodeTab[i] == val) return;
1438
1439 /*
1440 * grow the nodeTab if needed
1441 */
1442 if (cur->nodeMax == 0) {
1443 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1444 sizeof(xmlNodePtr));
1445 if (cur->nodeTab == NULL) {
1446 xmlGenericError(xmlGenericErrorContext,
1447 "xmlXPathNodeSetAdd: out of memory\n");
1448 return;
1449 }
1450 memset(cur->nodeTab, 0 ,
1451 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1452 cur->nodeMax = XML_NODESET_DEFAULT;
1453 } else if (cur->nodeNr == cur->nodeMax) {
1454 xmlNodePtr *temp;
1455
1456 cur->nodeMax *= 2;
1457 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1458 sizeof(xmlNodePtr));
1459 if (temp == NULL) {
1460 xmlGenericError(xmlGenericErrorContext,
1461 "xmlXPathNodeSetAdd: out of memory\n");
1462 return;
1463 }
1464 cur->nodeTab = temp;
1465 }
1466 cur->nodeTab[cur->nodeNr++] = val;
1467}
1468
1469/**
1470 * xmlXPathNodeSetAddUnique:
1471 * @cur: the initial node set
1472 * @val: a new xmlNodePtr
1473 *
1474 * add a new xmlNodePtr ot an existing NodeSet, optimized version
1475 * when we are sure the node is not already in the set.
1476 */
1477void
1478xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
1479 if (val == NULL) return;
1480
1481 /*
1482 * grow the nodeTab if needed
1483 */
1484 if (cur->nodeMax == 0) {
1485 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1486 sizeof(xmlNodePtr));
1487 if (cur->nodeTab == NULL) {
1488 xmlGenericError(xmlGenericErrorContext,
1489 "xmlXPathNodeSetAddUnique: out of memory\n");
1490 return;
1491 }
1492 memset(cur->nodeTab, 0 ,
1493 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1494 cur->nodeMax = XML_NODESET_DEFAULT;
1495 } else if (cur->nodeNr == cur->nodeMax) {
1496 xmlNodePtr *temp;
1497
1498 cur->nodeMax *= 2;
1499 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
1500 sizeof(xmlNodePtr));
1501 if (temp == NULL) {
1502 xmlGenericError(xmlGenericErrorContext,
1503 "xmlXPathNodeSetAddUnique: out of memory\n");
1504 return;
1505 }
1506 cur->nodeTab = temp;
1507 }
1508 cur->nodeTab[cur->nodeNr++] = val;
1509}
1510
1511/**
1512 * xmlXPathNodeSetMerge:
1513 * @val1: the first NodeSet or NULL
1514 * @val2: the second NodeSet
1515 *
1516 * Merges two nodesets, all nodes from @val2 are added to @val1
1517 * if @val1 is NULL, a new set is created and copied from @val2
1518 *
1519 * Returns val1 once extended or NULL in case of error.
1520 */
1521xmlNodeSetPtr
1522xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001523 int i, j, initNr, skip;
Owen Taylor3473f882001-02-23 17:55:21 +00001524
1525 if (val2 == NULL) return(val1);
1526 if (val1 == NULL) {
1527 val1 = xmlXPathNodeSetCreate(NULL);
1528 }
1529
1530 initNr = val1->nodeNr;
1531
1532 for (i = 0;i < val2->nodeNr;i++) {
1533 /*
1534 * check against doublons
1535 */
Daniel Veillardd8df6c02001-04-05 16:54:14 +00001536 skip = 0;
1537 for (j = 0; j < initNr; j++) {
1538 if (val1->nodeTab[j] == val2->nodeTab[i]) {
1539 skip = 1;
1540 break;
1541 }
1542 }
1543 if (skip)
1544 continue;
Owen Taylor3473f882001-02-23 17:55:21 +00001545
1546 /*
1547 * grow the nodeTab if needed
1548 */
1549 if (val1->nodeMax == 0) {
1550 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
1551 sizeof(xmlNodePtr));
1552 if (val1->nodeTab == NULL) {
1553 xmlGenericError(xmlGenericErrorContext,
1554 "xmlXPathNodeSetMerge: out of memory\n");
1555 return(NULL);
1556 }
1557 memset(val1->nodeTab, 0 ,
1558 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
1559 val1->nodeMax = XML_NODESET_DEFAULT;
1560 } else if (val1->nodeNr == val1->nodeMax) {
1561 xmlNodePtr *temp;
1562
1563 val1->nodeMax *= 2;
1564 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
1565 sizeof(xmlNodePtr));
1566 if (temp == NULL) {
1567 xmlGenericError(xmlGenericErrorContext,
1568 "xmlXPathNodeSetMerge: out of memory\n");
1569 return(NULL);
1570 }
1571 val1->nodeTab = temp;
1572 }
1573 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
1574 }
1575
1576 return(val1);
1577}
1578
1579/**
1580 * xmlXPathNodeSetDel:
1581 * @cur: the initial node set
1582 * @val: an xmlNodePtr
1583 *
1584 * Removes an xmlNodePtr from an existing NodeSet
1585 */
1586void
1587xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
1588 int i;
1589
1590 if (cur == NULL) return;
1591 if (val == NULL) return;
1592
1593 /*
1594 * check against doublons
1595 */
1596 for (i = 0;i < cur->nodeNr;i++)
1597 if (cur->nodeTab[i] == val) break;
1598
1599 if (i >= cur->nodeNr) {
1600#ifdef DEBUG
1601 xmlGenericError(xmlGenericErrorContext,
1602 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
1603 val->name);
1604#endif
1605 return;
1606 }
1607 cur->nodeNr--;
1608 for (;i < cur->nodeNr;i++)
1609 cur->nodeTab[i] = cur->nodeTab[i + 1];
1610 cur->nodeTab[cur->nodeNr] = NULL;
1611}
1612
1613/**
1614 * xmlXPathNodeSetRemove:
1615 * @cur: the initial node set
1616 * @val: the index to remove
1617 *
1618 * Removes an entry from an existing NodeSet list.
1619 */
1620void
1621xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
1622 if (cur == NULL) return;
1623 if (val >= cur->nodeNr) return;
1624 cur->nodeNr--;
1625 for (;val < cur->nodeNr;val++)
1626 cur->nodeTab[val] = cur->nodeTab[val + 1];
1627 cur->nodeTab[cur->nodeNr] = NULL;
1628}
1629
1630/**
1631 * xmlXPathFreeNodeSet:
1632 * @obj: the xmlNodeSetPtr to free
1633 *
1634 * Free the NodeSet compound (not the actual nodes !).
1635 */
1636void
1637xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
1638 if (obj == NULL) return;
1639 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001640 xmlFree(obj->nodeTab);
1641 }
Owen Taylor3473f882001-02-23 17:55:21 +00001642 xmlFree(obj);
1643}
1644
1645/**
1646 * xmlXPathFreeValueTree:
1647 * @obj: the xmlNodeSetPtr to free
1648 *
1649 * Free the NodeSet compound and the actual tree, this is different
1650 * from xmlXPathFreeNodeSet()
1651 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001652static void
Owen Taylor3473f882001-02-23 17:55:21 +00001653xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
1654 int i;
1655
1656 if (obj == NULL) return;
1657 for (i = 0;i < obj->nodeNr;i++)
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00001658 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +00001659 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00001660
1661 if (obj->nodeTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001662 xmlFree(obj->nodeTab);
1663 }
Owen Taylor3473f882001-02-23 17:55:21 +00001664 xmlFree(obj);
1665}
1666
1667#if defined(DEBUG) || defined(DEBUG_STEP)
1668/**
1669 * xmlGenericErrorContextNodeSet:
1670 * @output: a FILE * for the output
1671 * @obj: the xmlNodeSetPtr to free
1672 *
1673 * Quick display of a NodeSet
1674 */
1675void
1676xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
1677 int i;
1678
1679 if (output == NULL) output = xmlGenericErrorContext;
1680 if (obj == NULL) {
1681 fprintf(output, "NodeSet == NULL !\n");
1682 return;
1683 }
1684 if (obj->nodeNr == 0) {
1685 fprintf(output, "NodeSet is empty\n");
1686 return;
1687 }
1688 if (obj->nodeTab == NULL) {
1689 fprintf(output, " nodeTab == NULL !\n");
1690 return;
1691 }
1692 for (i = 0; i < obj->nodeNr; i++) {
1693 if (obj->nodeTab[i] == NULL) {
1694 fprintf(output, " NULL !\n");
1695 return;
1696 }
1697 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
1698 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
1699 fprintf(output, " /");
1700 else if (obj->nodeTab[i]->name == NULL)
1701 fprintf(output, " noname!");
1702 else fprintf(output, " %s", obj->nodeTab[i]->name);
1703 }
1704 fprintf(output, "\n");
1705}
1706#endif
1707
1708/**
1709 * xmlXPathNewNodeSet:
1710 * @val: the NodePtr value
1711 *
1712 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1713 * it with the single Node @val
1714 *
1715 * Returns the newly created object.
1716 */
1717xmlXPathObjectPtr
1718xmlXPathNewNodeSet(xmlNodePtr val) {
1719 xmlXPathObjectPtr ret;
1720
1721 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1722 if (ret == NULL) {
1723 xmlGenericError(xmlGenericErrorContext,
1724 "xmlXPathNewNodeSet: out of memory\n");
1725 return(NULL);
1726 }
1727 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1728 ret->type = XPATH_NODESET;
Daniel Veillard77851712001-02-27 21:54:07 +00001729 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001730 ret->nodesetval = xmlXPathNodeSetCreate(val);
1731 return(ret);
1732}
1733
1734/**
1735 * xmlXPathNewValueTree:
1736 * @val: the NodePtr value
1737 *
1738 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1739 * it with the tree root @val
1740 *
1741 * Returns the newly created object.
1742 */
1743xmlXPathObjectPtr
1744xmlXPathNewValueTree(xmlNodePtr val) {
1745 xmlXPathObjectPtr ret;
1746
1747 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1748 if (ret == NULL) {
1749 xmlGenericError(xmlGenericErrorContext,
1750 "xmlXPathNewNodeSet: out of memory\n");
1751 return(NULL);
1752 }
1753 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1754 ret->type = XPATH_XSLT_TREE;
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00001755 ret->boolval = 1;
1756 ret->user = (void *) val;
Owen Taylor3473f882001-02-23 17:55:21 +00001757 ret->nodesetval = xmlXPathNodeSetCreate(val);
1758 return(ret);
1759}
1760
1761/**
1762 * xmlXPathNewNodeSetList:
1763 * @val: an existing NodeSet
1764 *
1765 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1766 * it with the Nodeset @val
1767 *
1768 * Returns the newly created object.
1769 */
1770xmlXPathObjectPtr
1771xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1772 xmlXPathObjectPtr ret;
1773 int i;
1774
1775 if (val == NULL)
1776 ret = NULL;
1777 else if (val->nodeTab == NULL)
1778 ret = xmlXPathNewNodeSet(NULL);
1779 else
1780 {
1781 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1782 for (i = 1; i < val->nodeNr; ++i)
1783 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1784 }
1785
1786 return(ret);
1787}
1788
1789/**
1790 * xmlXPathWrapNodeSet:
1791 * @val: the NodePtr value
1792 *
1793 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1794 *
1795 * Returns the newly created object.
1796 */
1797xmlXPathObjectPtr
1798xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1799 xmlXPathObjectPtr ret;
1800
1801 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1802 if (ret == NULL) {
1803 xmlGenericError(xmlGenericErrorContext,
1804 "xmlXPathWrapNodeSet: out of memory\n");
1805 return(NULL);
1806 }
1807 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1808 ret->type = XPATH_NODESET;
1809 ret->nodesetval = val;
1810 return(ret);
1811}
1812
1813/**
1814 * xmlXPathFreeNodeSetList:
1815 * @obj: an existing NodeSetList object
1816 *
1817 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1818 * the list contrary to xmlXPathFreeObject().
1819 */
1820void
1821xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1822 if (obj == NULL) return;
Owen Taylor3473f882001-02-23 17:55:21 +00001823 xmlFree(obj);
1824}
1825
Thomas Broyerf06a3d82001-07-16 04:52:57 +00001826/**
1827 * xmlXPathDifference:
1828 * @nodes1: a node-set
1829 * @nodes2: a node-set
1830 *
1831 * Implements the EXSLT - Sets difference() function:
1832 * node-set set:difference (node-set, node-set)
1833 *
1834 * Returns the difference between the two node sets, or nodes1 if
1835 * nodes2 is empty
1836 */
1837xmlNodeSetPtr
1838xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1839 xmlNodeSetPtr ret;
1840 int i, l1;
1841 xmlNodePtr cur;
1842
1843 if (xmlXPathNodeSetIsEmpty(nodes2))
1844 return(nodes1);
1845
1846 ret = xmlXPathNodeSetCreate(NULL);
1847 if (xmlXPathNodeSetIsEmpty(nodes1))
1848 return(ret);
1849
1850 l1 = xmlXPathNodeSetGetLength(nodes1);
1851
1852 for (i = 0; i < l1; i++) {
1853 cur = xmlXPathNodeSetItem(nodes1, i);
1854 if (!xmlXPathNodeSetContains(nodes2, cur))
1855 xmlXPathNodeSetAddUnique(ret, cur);
1856 }
1857 return(ret);
1858}
1859
1860/**
1861 * xmlXPathIntersection:
1862 * @nodes1: a node-set
1863 * @nodes2: a node-set
1864 *
1865 * Implements the EXSLT - Sets intersection() function:
1866 * node-set set:intersection (node-set, node-set)
1867 *
1868 * Returns a node set comprising the nodes that are within both the
1869 * node sets passed as arguments
1870 */
1871xmlNodeSetPtr
1872xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1873 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
1874 int i, l1;
1875 xmlNodePtr cur;
1876
1877 if (xmlXPathNodeSetIsEmpty(nodes1))
1878 return(ret);
1879 if (xmlXPathNodeSetIsEmpty(nodes2))
1880 return(ret);
1881
1882 l1 = xmlXPathNodeSetGetLength(nodes1);
1883
1884 for (i = 0; i < l1; i++) {
1885 cur = xmlXPathNodeSetItem(nodes1, i);
1886 if (xmlXPathNodeSetContains(nodes2, cur))
1887 xmlXPathNodeSetAddUnique(ret, cur);
1888 }
1889 return(ret);
1890}
1891
1892/**
1893 * xmlXPathDistinctSorted:
1894 * @nodes: a node-set, sorted by document order
1895 *
1896 * Implements the EXSLT - Sets distinct() function:
1897 * node-set set:distinct (node-set)
1898 *
1899 * Returns a subset of the nodes contained in @nodes, or @nodes if
1900 * it is empty
1901 */
1902xmlNodeSetPtr
1903xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
1904 xmlNodeSetPtr ret;
1905 xmlHashTablePtr hash;
1906 int i, l;
1907 xmlChar * strval;
1908 xmlNodePtr cur;
1909
1910 if (xmlXPathNodeSetIsEmpty(nodes))
1911 return(nodes);
1912
1913 ret = xmlXPathNodeSetCreate(NULL);
1914 l = xmlXPathNodeSetGetLength(nodes);
1915 hash = xmlHashCreate (l);
1916 for (i = 0; i < l; i++) {
1917 cur = xmlXPathNodeSetItem(nodes, i);
1918 strval = xmlXPathCastNodeToString(cur);
1919 if (xmlHashLookup(hash, strval) == NULL) {
1920 xmlHashAddEntry(hash, strval, strval);
1921 xmlXPathNodeSetAddUnique(ret, cur);
1922 } else {
1923 xmlFree(strval);
1924 }
1925 }
1926 xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
1927 return(ret);
1928}
1929
1930/**
1931 * xmlXPathDistinct:
1932 * @nodes: a node-set
1933 *
1934 * Implements the EXSLT - Sets distinct() function:
1935 * node-set set:distinct (node-set)
1936 * @nodes is sorted by document order, then #exslSetsDistinctSorted
1937 * is called with the sorted node-set
1938 *
1939 * Returns a subset of the nodes contained in @nodes, or @nodes if
1940 * it is empty
1941 */
1942xmlNodeSetPtr
1943xmlXPathDistinct (xmlNodeSetPtr nodes) {
1944 if (xmlXPathNodeSetIsEmpty(nodes))
1945 return(nodes);
1946
1947 xmlXPathNodeSetSort(nodes);
1948 return(xmlXPathDistinctSorted(nodes));
1949}
1950
1951/**
1952 * xmlXPathHasSameNodes:
1953 * @nodes1: a node-set
1954 * @nodes2: a node-set
1955 *
1956 * Implements the EXSLT - Sets has-same-nodes function:
1957 * boolean set:has-same-node(node-set, node-set)
1958 *
1959 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
1960 * otherwise
1961 */
1962int
1963xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
1964 int i, l;
1965 xmlNodePtr cur;
1966
1967 if (xmlXPathNodeSetIsEmpty(nodes1) ||
1968 xmlXPathNodeSetIsEmpty(nodes2))
1969 return(0);
1970
1971 l = xmlXPathNodeSetGetLength(nodes1);
1972 for (i = 0; i < l; i++) {
1973 cur = xmlXPathNodeSetItem(nodes1, i);
1974 if (xmlXPathNodeSetContains(nodes2, cur))
1975 return(1);
1976 }
1977 return(0);
1978}
1979
1980/**
1981 * xmlXPathNodeLeadingSorted:
1982 * @nodes: a node-set, sorted by document order
1983 * @node: a node
1984 *
1985 * Implements the EXSLT - Sets leading() function:
1986 * node-set set:leading (node-set, node-set)
1987 *
1988 * Returns the nodes in @nodes that precede @node in document order,
1989 * @nodes if @node is NULL or an empty node-set if @nodes
1990 * doesn't contain @node
1991 */
1992xmlNodeSetPtr
1993xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
1994 int i, l;
1995 xmlNodePtr cur;
1996 xmlNodeSetPtr ret;
1997
1998 if (node == NULL)
1999 return(nodes);
2000
2001 ret = xmlXPathNodeSetCreate(NULL);
2002 if (xmlXPathNodeSetIsEmpty(nodes) ||
2003 (!xmlXPathNodeSetContains(nodes, node)))
2004 return(ret);
2005
2006 l = xmlXPathNodeSetGetLength(nodes);
2007 for (i = 0; i < l; i++) {
2008 cur = xmlXPathNodeSetItem(nodes, i);
2009 if (cur == node)
2010 break;
2011 xmlXPathNodeSetAddUnique(ret, cur);
2012 }
2013 return(ret);
2014}
2015
2016/**
2017 * xmlXPathNodeLeading:
2018 * @nodes: a node-set
2019 * @node: a node
2020 *
2021 * Implements the EXSLT - Sets leading() function:
2022 * node-set set:leading (node-set, node-set)
2023 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
2024 * is called.
2025 *
2026 * Returns the nodes in @nodes that precede @node in document order,
2027 * @nodes if @node is NULL or an empty node-set if @nodes
2028 * doesn't contain @node
2029 */
2030xmlNodeSetPtr
2031xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
2032 xmlXPathNodeSetSort(nodes);
2033 return(xmlXPathNodeLeadingSorted(nodes, node));
2034}
2035
2036/**
2037 * xmlXPathLeadingSorted:
2038 * @nodes1: a node-set, sorted by document order
2039 * @nodes2: a node-set, sorted by document order
2040 *
2041 * Implements the EXSLT - Sets leading() function:
2042 * node-set set:leading (node-set, node-set)
2043 *
2044 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2045 * in document order, @nodes1 if @nodes2 is NULL or empty or
2046 * an empty node-set if @nodes1 doesn't contain @nodes2
2047 */
2048xmlNodeSetPtr
2049xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2050 if (xmlXPathNodeSetIsEmpty(nodes2))
2051 return(nodes1);
2052 return(xmlXPathNodeLeadingSorted(nodes1,
2053 xmlXPathNodeSetItem(nodes2, 1)));
2054}
2055
2056/**
2057 * xmlXPathLeading:
2058 * @nodes1: a node-set
2059 * @nodes2: a node-set
2060 *
2061 * Implements the EXSLT - Sets leading() function:
2062 * node-set set:leading (node-set, node-set)
2063 * @nodes1 and @nodes2 are sorted by document order, then
2064 * #exslSetsLeadingSorted is called.
2065 *
2066 * Returns the nodes in @nodes1 that precede the first node in @nodes2
2067 * in document order, @nodes1 if @nodes2 is NULL or empty or
2068 * an empty node-set if @nodes1 doesn't contain @nodes2
2069 */
2070xmlNodeSetPtr
2071xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2072 if (xmlXPathNodeSetIsEmpty(nodes2))
2073 return(nodes1);
2074 if (xmlXPathNodeSetIsEmpty(nodes1))
2075 return(xmlXPathNodeSetCreate(NULL));
2076 xmlXPathNodeSetSort(nodes1);
2077 xmlXPathNodeSetSort(nodes2);
2078 return(xmlXPathNodeLeadingSorted(nodes1,
2079 xmlXPathNodeSetItem(nodes2, 1)));
2080}
2081
2082/**
2083 * xmlXPathNodeTrailingSorted:
2084 * @nodes: a node-set, sorted by document order
2085 * @node: a node
2086 *
2087 * Implements the EXSLT - Sets trailing() function:
2088 * node-set set:trailing (node-set, node-set)
2089 *
2090 * Returns the nodes in @nodes that follow @node in document order,
2091 * @nodes if @node is NULL or an empty node-set if @nodes
2092 * doesn't contain @node
2093 */
2094xmlNodeSetPtr
2095xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
2096 int i, l;
2097 xmlNodePtr cur;
2098 xmlNodeSetPtr ret;
2099
2100 if (node == NULL)
2101 return(nodes);
2102
2103 ret = xmlXPathNodeSetCreate(NULL);
2104 if (xmlXPathNodeSetIsEmpty(nodes) ||
2105 (!xmlXPathNodeSetContains(nodes, node)))
2106 return(ret);
2107
2108 l = xmlXPathNodeSetGetLength(nodes);
Thomas Broyerf186c822001-07-31 23:30:37 +00002109 for (i = l; i > 0; i--) {
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002110 cur = xmlXPathNodeSetItem(nodes, i);
2111 if (cur == node)
2112 break;
2113 xmlXPathNodeSetAddUnique(ret, cur);
2114 }
2115 return(ret);
2116}
2117
2118/**
2119 * xmlXPathNodeTrailing:
2120 * @nodes: a node-set
2121 * @node: a node
2122 *
2123 * Implements the EXSLT - Sets trailing() function:
2124 * node-set set:trailing (node-set, node-set)
2125 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
2126 * is called.
2127 *
2128 * Returns the nodes in @nodes that follow @node in document order,
2129 * @nodes if @node is NULL or an empty node-set if @nodes
2130 * doesn't contain @node
2131 */
2132xmlNodeSetPtr
2133xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
2134 xmlXPathNodeSetSort(nodes);
2135 return(xmlXPathNodeTrailingSorted(nodes, node));
2136}
2137
2138/**
2139 * xmlXPathTrailingSorted:
2140 * @nodes1: a node-set, sorted by document order
2141 * @nodes2: a node-set, sorted by document order
2142 *
2143 * Implements the EXSLT - Sets trailing() function:
2144 * node-set set:trailing (node-set, node-set)
2145 *
2146 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2147 * in document order, @nodes1 if @nodes2 is NULL or empty or
2148 * an empty node-set if @nodes1 doesn't contain @nodes2
2149 */
2150xmlNodeSetPtr
2151xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2152 if (xmlXPathNodeSetIsEmpty(nodes2))
2153 return(nodes1);
2154 return(xmlXPathNodeTrailingSorted(nodes1,
2155 xmlXPathNodeSetItem(nodes2, 0)));
2156}
2157
2158/**
2159 * xmlXPathTrailing:
2160 * @nodes1: a node-set
2161 * @nodes2: a node-set
2162 *
2163 * Implements the EXSLT - Sets trailing() function:
2164 * node-set set:trailing (node-set, node-set)
2165 * @nodes1 and @nodes2 are sorted by document order, then
2166 * #xmlXPathTrailingSorted is called.
2167 *
2168 * Returns the nodes in @nodes1 that follow the first node in @nodes2
2169 * in document order, @nodes1 if @nodes2 is NULL or empty or
2170 * an empty node-set if @nodes1 doesn't contain @nodes2
2171 */
2172xmlNodeSetPtr
2173xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
2174 if (xmlXPathNodeSetIsEmpty(nodes2))
2175 return(nodes1);
2176 if (xmlXPathNodeSetIsEmpty(nodes1))
2177 return(xmlXPathNodeSetCreate(NULL));
2178 xmlXPathNodeSetSort(nodes1);
2179 xmlXPathNodeSetSort(nodes2);
2180 return(xmlXPathNodeTrailingSorted(nodes1,
2181 xmlXPathNodeSetItem(nodes2, 0)));
2182}
2183
Owen Taylor3473f882001-02-23 17:55:21 +00002184/************************************************************************
2185 * *
2186 * Routines to handle extra functions *
2187 * *
2188 ************************************************************************/
2189
2190/**
2191 * xmlXPathRegisterFunc:
2192 * @ctxt: the XPath context
2193 * @name: the function name
2194 * @f: the function implementation or NULL
2195 *
2196 * Register a new function. If @f is NULL it unregisters the function
2197 *
2198 * Returns 0 in case of success, -1 in case of error
2199 */
2200int
2201xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
2202 xmlXPathFunction f) {
2203 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
2204}
2205
2206/**
2207 * xmlXPathRegisterFuncNS:
2208 * @ctxt: the XPath context
2209 * @name: the function name
2210 * @ns_uri: the function namespace URI
2211 * @f: the function implementation or NULL
2212 *
2213 * Register a new function. If @f is NULL it unregisters the function
2214 *
2215 * Returns 0 in case of success, -1 in case of error
2216 */
2217int
2218xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2219 const xmlChar *ns_uri, xmlXPathFunction f) {
2220 if (ctxt == NULL)
2221 return(-1);
2222 if (name == NULL)
2223 return(-1);
2224
2225 if (ctxt->funcHash == NULL)
2226 ctxt->funcHash = xmlHashCreate(0);
2227 if (ctxt->funcHash == NULL)
2228 return(-1);
2229 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
2230}
2231
2232/**
Thomas Broyerba4ad322001-07-26 16:55:21 +00002233 * xmlXPathRegisterFuncLookup:
2234 * @ctxt: the XPath context
2235 * @f: the lookup function
2236 * @data: the lookup data
2237 *
2238 * Registers an external mecanism to do function lookup.
2239 */
2240void
2241xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
2242 xmlXPathFuncLookupFunc f,
2243 void *funcCtxt) {
2244 if (ctxt == NULL)
2245 return;
2246 ctxt->funcLookupFunc = (void *) f;
2247 ctxt->funcLookupData = funcCtxt;
2248}
2249
2250/**
Owen Taylor3473f882001-02-23 17:55:21 +00002251 * xmlXPathFunctionLookup:
2252 * @ctxt: the XPath context
2253 * @name: the function name
2254 *
2255 * Search in the Function array of the context for the given
2256 * function.
2257 *
2258 * Returns the xmlXPathFunction or NULL if not found
2259 */
2260xmlXPathFunction
2261xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
Thomas Broyerba4ad322001-07-26 16:55:21 +00002262 if (ctxt == NULL)
2263 return (NULL);
2264
2265 if (ctxt->funcLookupFunc != NULL) {
2266 xmlXPathFunction ret;
2267
2268 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2269 (ctxt->funcLookupData, name, NULL);
2270 if (ret != NULL)
2271 return(ret);
2272 }
Owen Taylor3473f882001-02-23 17:55:21 +00002273 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
2274}
2275
2276/**
2277 * xmlXPathFunctionLookupNS:
2278 * @ctxt: the XPath context
2279 * @name: the function name
2280 * @ns_uri: the function namespace URI
2281 *
2282 * Search in the Function array of the context for the given
2283 * function.
2284 *
2285 * Returns the xmlXPathFunction or NULL if not found
2286 */
2287xmlXPathFunction
2288xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2289 const xmlChar *ns_uri) {
2290 if (ctxt == NULL)
2291 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002292 if (name == NULL)
2293 return(NULL);
2294
Thomas Broyerba4ad322001-07-26 16:55:21 +00002295 if (ctxt->funcLookupFunc != NULL) {
2296 xmlXPathFunction ret;
2297
2298 ret = ((xmlXPathFuncLookupFunc) ctxt->funcLookupFunc)
2299 (ctxt->funcLookupData, name, ns_uri);
2300 if (ret != NULL)
2301 return(ret);
2302 }
2303
2304 if (ctxt->funcHash == NULL)
2305 return(NULL);
2306
Owen Taylor3473f882001-02-23 17:55:21 +00002307 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
2308}
2309
2310/**
2311 * xmlXPathRegisteredFuncsCleanup:
2312 * @ctxt: the XPath context
2313 *
2314 * Cleanup the XPath context data associated to registered functions
2315 */
2316void
2317xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
2318 if (ctxt == NULL)
2319 return;
2320
2321 xmlHashFree(ctxt->funcHash, NULL);
2322 ctxt->funcHash = NULL;
2323}
2324
2325/************************************************************************
2326 * *
2327 * Routines to handle Variable *
2328 * *
2329 ************************************************************************/
2330
2331/**
2332 * xmlXPathRegisterVariable:
2333 * @ctxt: the XPath context
2334 * @name: the variable name
2335 * @value: the variable value or NULL
2336 *
2337 * Register a new variable value. If @value is NULL it unregisters
2338 * the variable
2339 *
2340 * Returns 0 in case of success, -1 in case of error
2341 */
2342int
2343xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
2344 xmlXPathObjectPtr value) {
2345 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
2346}
2347
2348/**
2349 * xmlXPathRegisterVariableNS:
2350 * @ctxt: the XPath context
2351 * @name: the variable name
2352 * @ns_uri: the variable namespace URI
2353 * @value: the variable value or NULL
2354 *
2355 * Register a new variable value. If @value is NULL it unregisters
2356 * the variable
2357 *
2358 * Returns 0 in case of success, -1 in case of error
2359 */
2360int
2361xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2362 const xmlChar *ns_uri,
2363 xmlXPathObjectPtr value) {
2364 if (ctxt == NULL)
2365 return(-1);
2366 if (name == NULL)
2367 return(-1);
2368
2369 if (ctxt->varHash == NULL)
2370 ctxt->varHash = xmlHashCreate(0);
2371 if (ctxt->varHash == NULL)
2372 return(-1);
2373 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
2374 (void *) value,
2375 (xmlHashDeallocator)xmlXPathFreeObject));
2376}
2377
2378/**
2379 * xmlXPathRegisterVariableLookup:
2380 * @ctxt: the XPath context
2381 * @f: the lookup function
2382 * @data: the lookup data
2383 *
2384 * register an external mechanism to do variable lookup
2385 */
2386void
2387xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
2388 xmlXPathVariableLookupFunc f, void *data) {
2389 if (ctxt == NULL)
2390 return;
2391 ctxt->varLookupFunc = (void *) f;
2392 ctxt->varLookupData = data;
2393}
2394
2395/**
2396 * xmlXPathVariableLookup:
2397 * @ctxt: the XPath context
2398 * @name: the variable name
2399 *
2400 * Search in the Variable array of the context for the given
2401 * variable value.
2402 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002403 * Returns a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002404 */
2405xmlXPathObjectPtr
2406xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
2407 if (ctxt == NULL)
2408 return(NULL);
2409
2410 if (ctxt->varLookupFunc != NULL) {
2411 xmlXPathObjectPtr ret;
2412
2413 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2414 (ctxt->varLookupData, name, NULL);
2415 if (ret != NULL) return(ret);
2416 }
2417 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
2418}
2419
2420/**
2421 * xmlXPathVariableLookupNS:
2422 * @ctxt: the XPath context
2423 * @name: the variable name
2424 * @ns_uri: the variable namespace URI
2425 *
2426 * Search in the Variable array of the context for the given
Daniel Veillard73c9c042001-07-05 20:02:54 +00002427 * variable value.
Owen Taylor3473f882001-02-23 17:55:21 +00002428 *
Daniel Veillard73c9c042001-07-05 20:02:54 +00002429 * Returns the a copy of the value or NULL if not found
Owen Taylor3473f882001-02-23 17:55:21 +00002430 */
2431xmlXPathObjectPtr
2432xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
2433 const xmlChar *ns_uri) {
2434 if (ctxt == NULL)
2435 return(NULL);
2436
2437 if (ctxt->varLookupFunc != NULL) {
2438 xmlXPathObjectPtr ret;
2439
2440 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
2441 (ctxt->varLookupData, name, ns_uri);
2442 if (ret != NULL) return(ret);
2443 }
2444
2445 if (ctxt->varHash == NULL)
2446 return(NULL);
2447 if (name == NULL)
2448 return(NULL);
2449
Daniel Veillard8c357d52001-07-03 23:43:33 +00002450 return(xmlXPathObjectCopy((xmlXPathObjectPtr)
2451 xmlHashLookup2(ctxt->varHash, name, ns_uri)));
Owen Taylor3473f882001-02-23 17:55:21 +00002452}
2453
2454/**
2455 * xmlXPathRegisteredVariablesCleanup:
2456 * @ctxt: the XPath context
2457 *
2458 * Cleanup the XPath context data associated to registered variables
2459 */
2460void
2461xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
2462 if (ctxt == NULL)
2463 return;
2464
Daniel Veillard76d66f42001-05-16 21:05:17 +00002465 xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject);
Owen Taylor3473f882001-02-23 17:55:21 +00002466 ctxt->varHash = NULL;
2467}
2468
2469/**
2470 * xmlXPathRegisterNs:
2471 * @ctxt: the XPath context
2472 * @prefix: the namespace prefix
2473 * @ns_uri: the namespace name
2474 *
2475 * Register a new namespace. If @ns_uri is NULL it unregisters
2476 * the namespace
2477 *
2478 * Returns 0 in case of success, -1 in case of error
2479 */
2480int
2481xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
2482 const xmlChar *ns_uri) {
2483 if (ctxt == NULL)
2484 return(-1);
2485 if (prefix == NULL)
2486 return(-1);
2487
2488 if (ctxt->nsHash == NULL)
2489 ctxt->nsHash = xmlHashCreate(10);
2490 if (ctxt->nsHash == NULL)
2491 return(-1);
2492 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
2493 (xmlHashDeallocator)xmlFree));
2494}
2495
2496/**
2497 * xmlXPathNsLookup:
2498 * @ctxt: the XPath context
2499 * @prefix: the namespace prefix value
2500 *
2501 * Search in the namespace declaration array of the context for the given
2502 * namespace name associated to the given prefix
2503 *
2504 * Returns the value or NULL if not found
2505 */
2506const xmlChar *
2507xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
2508 if (ctxt == NULL)
2509 return(NULL);
2510 if (prefix == NULL)
2511 return(NULL);
2512
2513#ifdef XML_XML_NAMESPACE
2514 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
2515 return(XML_XML_NAMESPACE);
2516#endif
2517
Daniel Veillardc8f620b2001-04-30 20:31:33 +00002518 if (ctxt->namespaces != NULL) {
2519 int i;
2520
2521 for (i = 0;i < ctxt->nsNr;i++) {
2522 if ((ctxt->namespaces[i] != NULL) &&
2523 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
2524 return(ctxt->namespaces[i]->href);
2525 }
2526 }
Owen Taylor3473f882001-02-23 17:55:21 +00002527
2528 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
2529}
2530
2531/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00002532 * xmlXPathRegisteredNsCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +00002533 * @ctxt: the XPath context
2534 *
2535 * Cleanup the XPath context data associated to registered variables
2536 */
2537void
2538xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
2539 if (ctxt == NULL)
2540 return;
2541
2542 xmlHashFree(ctxt->nsHash, NULL);
2543 ctxt->nsHash = NULL;
2544}
2545
2546/************************************************************************
2547 * *
2548 * Routines to handle Values *
2549 * *
2550 ************************************************************************/
2551
2552/* Allocations are terrible, one need to optimize all this !!! */
2553
2554/**
2555 * xmlXPathNewFloat:
2556 * @val: the double value
2557 *
2558 * Create a new xmlXPathObjectPtr of type double and of value @val
2559 *
2560 * Returns the newly created object.
2561 */
2562xmlXPathObjectPtr
2563xmlXPathNewFloat(double val) {
2564 xmlXPathObjectPtr ret;
2565
2566 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2567 if (ret == NULL) {
2568 xmlGenericError(xmlGenericErrorContext,
2569 "xmlXPathNewFloat: out of memory\n");
2570 return(NULL);
2571 }
2572 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2573 ret->type = XPATH_NUMBER;
2574 ret->floatval = val;
2575 return(ret);
2576}
2577
2578/**
2579 * xmlXPathNewBoolean:
2580 * @val: the boolean value
2581 *
2582 * Create a new xmlXPathObjectPtr of type boolean and of value @val
2583 *
2584 * Returns the newly created object.
2585 */
2586xmlXPathObjectPtr
2587xmlXPathNewBoolean(int val) {
2588 xmlXPathObjectPtr ret;
2589
2590 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2591 if (ret == NULL) {
2592 xmlGenericError(xmlGenericErrorContext,
2593 "xmlXPathNewBoolean: out of memory\n");
2594 return(NULL);
2595 }
2596 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2597 ret->type = XPATH_BOOLEAN;
2598 ret->boolval = (val != 0);
2599 return(ret);
2600}
2601
2602/**
2603 * xmlXPathNewString:
2604 * @val: the xmlChar * value
2605 *
2606 * Create a new xmlXPathObjectPtr of type string and of value @val
2607 *
2608 * Returns the newly created object.
2609 */
2610xmlXPathObjectPtr
2611xmlXPathNewString(const xmlChar *val) {
2612 xmlXPathObjectPtr ret;
2613
2614 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2615 if (ret == NULL) {
2616 xmlGenericError(xmlGenericErrorContext,
2617 "xmlXPathNewString: out of memory\n");
2618 return(NULL);
2619 }
2620 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2621 ret->type = XPATH_STRING;
2622 if (val != NULL)
2623 ret->stringval = xmlStrdup(val);
2624 else
2625 ret->stringval = xmlStrdup((const xmlChar *)"");
2626 return(ret);
2627}
2628
2629/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002630 * xmlXPathWrapString:
2631 * @val: the xmlChar * value
2632 *
2633 * Wraps the @val string into an XPath object.
2634 *
2635 * Returns the newly created object.
2636 */
2637xmlXPathObjectPtr
2638xmlXPathWrapString (xmlChar *val) {
2639 xmlXPathObjectPtr ret;
2640
2641 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2642 if (ret == NULL) {
2643 xmlGenericError(xmlGenericErrorContext,
2644 "xmlXPathWrapString: out of memory\n");
2645 return(NULL);
2646 }
2647 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2648 ret->type = XPATH_STRING;
2649 ret->stringval = val;
2650 return(ret);
2651}
2652
2653/**
Owen Taylor3473f882001-02-23 17:55:21 +00002654 * xmlXPathNewCString:
2655 * @val: the char * value
2656 *
2657 * Create a new xmlXPathObjectPtr of type string and of value @val
2658 *
2659 * Returns the newly created object.
2660 */
2661xmlXPathObjectPtr
2662xmlXPathNewCString(const char *val) {
2663 xmlXPathObjectPtr ret;
2664
2665 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2666 if (ret == NULL) {
2667 xmlGenericError(xmlGenericErrorContext,
2668 "xmlXPathNewCString: out of memory\n");
2669 return(NULL);
2670 }
2671 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2672 ret->type = XPATH_STRING;
2673 ret->stringval = xmlStrdup(BAD_CAST val);
2674 return(ret);
2675}
2676
2677/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002678 * xmlXPathWrapCString:
2679 * @val: the char * value
2680 *
2681 * Wraps a string into an XPath object.
2682 *
2683 * Returns the newly created object.
2684 */
2685xmlXPathObjectPtr
2686xmlXPathWrapCString (char * val) {
2687 return(xmlXPathWrapString((xmlChar *)(val)));
2688}
2689
2690/**
Thomas Broyerf06a3d82001-07-16 04:52:57 +00002691 * xmlXPathWrapExternal:
2692 * @val: the user data
2693 *
2694 * Wraps the @val data into an XPath object.
2695 *
2696 * Returns the newly created object.
2697 */
2698xmlXPathObjectPtr
2699xmlXPathWrapExternal (void *val) {
2700 xmlXPathObjectPtr ret;
2701
2702 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2703 if (ret == NULL) {
2704 xmlGenericError(xmlGenericErrorContext,
2705 "xmlXPathWrapString: out of memory\n");
2706 return(NULL);
2707 }
2708 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
2709 ret->type = XPATH_USERS;
2710 ret->user = val;
2711 return(ret);
2712}
2713
2714/**
Owen Taylor3473f882001-02-23 17:55:21 +00002715 * xmlXPathObjectCopy:
2716 * @val: the original object
2717 *
2718 * allocate a new copy of a given object
2719 *
2720 * Returns the newly created object.
2721 */
2722xmlXPathObjectPtr
2723xmlXPathObjectCopy(xmlXPathObjectPtr val) {
2724 xmlXPathObjectPtr ret;
2725
2726 if (val == NULL)
2727 return(NULL);
2728
2729 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
2730 if (ret == NULL) {
2731 xmlGenericError(xmlGenericErrorContext,
2732 "xmlXPathObjectCopy: out of memory\n");
2733 return(NULL);
2734 }
2735 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
2736 switch (val->type) {
2737 case XPATH_BOOLEAN:
2738 case XPATH_NUMBER:
2739 case XPATH_POINT:
2740 case XPATH_RANGE:
2741 break;
2742 case XPATH_STRING:
2743 ret->stringval = xmlStrdup(val->stringval);
2744 break;
2745 case XPATH_XSLT_TREE:
2746 if ((val->nodesetval != NULL) &&
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002747 (val->nodesetval->nodeTab != NULL)) {
2748 ret->boolval = 1;
2749 ret->user = xmlCopyNode(val->nodesetval->nodeTab[0], 1);
Owen Taylor3473f882001-02-23 17:55:21 +00002750 ret->nodesetval = xmlXPathNodeSetCreate(
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002751 (xmlNodePtr) ret->user);
2752 } else
Owen Taylor3473f882001-02-23 17:55:21 +00002753 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002754 /* Deallocate the copied tree value */
Owen Taylor3473f882001-02-23 17:55:21 +00002755 break;
2756 case XPATH_NODESET:
2757 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002758 /* Do not deallocate the copied tree value */
2759 ret->boolval = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002760 break;
2761 case XPATH_LOCATIONSET:
2762#ifdef LIBXML_XPTR_ENABLED
2763 {
2764 xmlLocationSetPtr loc = val->user;
2765 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
2766 break;
2767 }
2768#endif
2769 case XPATH_UNDEFINED:
2770 case XPATH_USERS:
2771 xmlGenericError(xmlGenericErrorContext,
2772 "xmlXPathObjectCopy: unsupported type %d\n",
2773 val->type);
2774 break;
2775 }
2776 return(ret);
2777}
2778
2779/**
2780 * xmlXPathFreeObject:
2781 * @obj: the object to free
2782 *
2783 * Free up an xmlXPathObjectPtr object.
2784 */
2785void
2786xmlXPathFreeObject(xmlXPathObjectPtr obj) {
2787 if (obj == NULL) return;
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002788 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
Daniel Veillard77851712001-02-27 21:54:07 +00002789 if (obj->boolval) {
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00002790 if (obj->user != NULL) {
2791 xmlFreeNodeList((xmlNodePtr) obj->user);
2792 xmlXPathFreeNodeSet(obj->nodesetval);
2793 } else if (obj->nodesetval != NULL)
Daniel Veillard77851712001-02-27 21:54:07 +00002794 xmlXPathFreeValueTree(obj->nodesetval);
2795 } else {
2796 if (obj->nodesetval != NULL)
2797 xmlXPathFreeNodeSet(obj->nodesetval);
2798 }
Owen Taylor3473f882001-02-23 17:55:21 +00002799#ifdef LIBXML_XPTR_ENABLED
2800 } else if (obj->type == XPATH_LOCATIONSET) {
2801 if (obj->user != NULL)
2802 xmlXPtrFreeLocationSet(obj->user);
2803#endif
2804 } else if (obj->type == XPATH_STRING) {
2805 if (obj->stringval != NULL)
2806 xmlFree(obj->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00002807 }
2808
Owen Taylor3473f882001-02-23 17:55:21 +00002809 xmlFree(obj);
2810}
2811
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002812
2813/************************************************************************
2814 * *
2815 * Type Casting Routines *
2816 * *
2817 ************************************************************************/
2818
2819/**
2820 * xmlXPathCastBooleanToString:
2821 * @val: a boolean
2822 *
2823 * Converts a boolean to its string value.
2824 *
2825 * Returns a newly allocated string.
2826 */
2827xmlChar *
2828xmlXPathCastBooleanToString (int val) {
2829 xmlChar *ret;
2830 if (val)
2831 ret = xmlStrdup((const xmlChar *) "true");
2832 else
2833 ret = xmlStrdup((const xmlChar *) "false");
2834 return(ret);
2835}
2836
2837/**
2838 * xmlXPathCastNumberToString:
2839 * @val: a number
2840 *
2841 * Converts a number to its string value.
2842 *
2843 * Returns a newly allocated string.
2844 */
2845xmlChar *
2846xmlXPathCastNumberToString (double val) {
2847 xmlChar *ret;
Daniel Veillardcda96922001-08-21 10:56:31 +00002848 switch (xmlXPathIsInf(val)) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002849 case 1:
2850 ret = xmlStrdup((const xmlChar *) "+Infinity");
2851 break;
2852 case -1:
2853 ret = xmlStrdup((const xmlChar *) "-Infinity");
2854 break;
2855 default:
Daniel Veillardcda96922001-08-21 10:56:31 +00002856 if (xmlXPathIsNaN(val)) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00002857 ret = xmlStrdup((const xmlChar *) "NaN");
2858 } else {
2859 /* could be improved */
2860 char buf[100];
2861 xmlXPathFormatNumber(val, buf, 100);
2862 ret = xmlStrdup((const xmlChar *) buf);
2863 }
2864 }
2865 return(ret);
2866}
2867
2868/**
2869 * xmlXPathCastNodeToString:
2870 * @node: a node
2871 *
2872 * Converts a node to its string value.
2873 *
2874 * Returns a newly allocated string.
2875 */
2876xmlChar *
2877xmlXPathCastNodeToString (xmlNodePtr node) {
2878 return(xmlNodeGetContent(node));
2879}
2880
2881/**
2882 * xmlXPathCastNodeSetToString:
2883 * @ns: a node-set
2884 *
2885 * Converts a node-set to its string value.
2886 *
2887 * Returns a newly allocated string.
2888 */
2889xmlChar *
2890xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
2891 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
2892 return(xmlStrdup((const xmlChar *) ""));
2893
2894 xmlXPathNodeSetSort(ns);
2895 return(xmlXPathCastNodeToString(ns->nodeTab[0]));
2896}
2897
2898/**
2899 * xmlXPathCastToString:
2900 * @val: an XPath object
2901 *
2902 * Converts an existing object to its string() equivalent
2903 *
2904 * Returns the string value of the object, NULL in case of error.
2905 * A new string is allocated only if needed (val isn't a
2906 * string object).
2907 */
2908xmlChar *
2909xmlXPathCastToString(xmlXPathObjectPtr val) {
2910 xmlChar *ret = NULL;
2911
2912 if (val == NULL)
2913 return(xmlStrdup((const xmlChar *) ""));
2914 switch (val->type) {
2915 case XPATH_UNDEFINED:
2916#ifdef DEBUG_EXPR
2917 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
2918#endif
2919 ret = xmlStrdup((const xmlChar *) "");
2920 break;
2921 case XPATH_XSLT_TREE:
2922 case XPATH_NODESET:
2923 ret = xmlXPathCastNodeSetToString(val->nodesetval);
2924 break;
2925 case XPATH_STRING:
2926 return(val->stringval);
2927 case XPATH_BOOLEAN:
2928 ret = xmlXPathCastBooleanToString(val->boolval);
2929 break;
2930 case XPATH_NUMBER: {
2931 ret = xmlXPathCastNumberToString(val->floatval);
2932 break;
2933 }
2934 case XPATH_USERS:
2935 case XPATH_POINT:
2936 case XPATH_RANGE:
2937 case XPATH_LOCATIONSET:
2938 TODO
2939 ret = xmlStrdup((const xmlChar *) "");
2940 break;
2941 }
2942 return(ret);
2943}
2944
2945/**
2946 * xmlXPathConvertString:
2947 * @val: an XPath object
2948 *
2949 * Converts an existing object to its string() equivalent
2950 *
2951 * Returns the new object, the old one is freed (or the operation
2952 * is done directly on @val)
2953 */
2954xmlXPathObjectPtr
2955xmlXPathConvertString(xmlXPathObjectPtr val) {
2956 xmlChar *res = NULL;
2957
2958 if (val == NULL)
2959 return(xmlXPathNewCString(""));
2960
2961 switch (val->type) {
2962 case XPATH_UNDEFINED:
2963#ifdef DEBUG_EXPR
2964 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
2965#endif
2966 break;
2967 case XPATH_XSLT_TREE:
2968 case XPATH_NODESET:
2969 res = xmlXPathCastNodeSetToString(val->nodesetval);
2970 break;
2971 case XPATH_STRING:
2972 return(val);
2973 case XPATH_BOOLEAN:
2974 res = xmlXPathCastBooleanToString(val->boolval);
2975 break;
2976 case XPATH_NUMBER:
2977 res = xmlXPathCastNumberToString(val->floatval);
2978 break;
2979 case XPATH_USERS:
2980 case XPATH_POINT:
2981 case XPATH_RANGE:
2982 case XPATH_LOCATIONSET:
2983 TODO;
2984 break;
2985 }
2986 xmlXPathFreeObject(val);
2987 if (res == NULL)
2988 return(xmlXPathNewCString(""));
2989 return(xmlXPathWrapString(res));
2990}
2991
2992/**
2993 * xmlXPathCastBooleanToNumber:
2994 * @val: a boolean
2995 *
2996 * Converts a boolean to its number value
2997 *
2998 * Returns the number value
2999 */
3000double
3001xmlXPathCastBooleanToNumber(int val) {
3002 if (val)
3003 return(1.0);
3004 return(0.0);
3005}
3006
3007/**
3008 * xmlXPathCastStringToNumber:
3009 * @val: a string
3010 *
3011 * Converts a string to its number value
3012 *
3013 * Returns the number value
3014 */
3015double
3016xmlXPathCastStringToNumber(const xmlChar * val) {
3017 return(xmlXPathStringEvalNumber(val));
3018}
3019
3020/**
3021 * xmlXPathCastNodeToNumber:
3022 * @node: a node
3023 *
3024 * Converts a node to its number value
3025 *
3026 * Returns the number value
3027 */
3028double
3029xmlXPathCastNodeToNumber (xmlNodePtr node) {
3030 xmlChar *strval;
3031 double ret;
3032
3033 if (node == NULL)
3034 return(xmlXPathNAN);
3035 strval = xmlXPathCastNodeToString(node);
3036 if (strval == NULL)
3037 return(xmlXPathNAN);
3038 ret = xmlXPathCastStringToNumber(strval);
3039 xmlFree(strval);
3040
3041 return(ret);
3042}
3043
3044/**
3045 * xmlXPathCastNodeSetToNumber:
3046 * @ns: a node-set
3047 *
3048 * Converts a node-set to its number value
3049 *
3050 * Returns the number value
3051 */
3052double
3053xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
3054 xmlChar *str;
3055 double ret;
3056
3057 if (ns == NULL)
3058 return(xmlXPathNAN);
3059 str = xmlXPathCastNodeSetToString(ns);
3060 ret = xmlXPathCastStringToNumber(str);
3061 xmlFree(str);
3062 return(ret);
3063}
3064
3065/**
3066 * xmlXPathCastToNumber:
3067 * @val: an XPath object
3068 *
3069 * Converts an XPath object to its number value
3070 *
3071 * Returns the number value
3072 */
3073double
3074xmlXPathCastToNumber(xmlXPathObjectPtr val) {
3075 double ret = 0.0;
3076
3077 if (val == NULL)
3078 return(xmlXPathNAN);
3079 switch (val->type) {
3080 case XPATH_UNDEFINED:
3081#ifdef DEGUB_EXPR
3082 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
3083#endif
3084 ret = xmlXPathNAN;
3085 break;
3086 case XPATH_XSLT_TREE:
3087 case XPATH_NODESET:
3088 ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
3089 break;
3090 case XPATH_STRING:
3091 ret = xmlXPathCastStringToNumber(val->stringval);
3092 break;
3093 case XPATH_NUMBER:
3094 ret = val->floatval;
3095 break;
3096 case XPATH_BOOLEAN:
3097 ret = xmlXPathCastBooleanToNumber(val->boolval);
3098 break;
3099 case XPATH_USERS:
3100 case XPATH_POINT:
3101 case XPATH_RANGE:
3102 case XPATH_LOCATIONSET:
3103 TODO;
3104 ret = xmlXPathNAN;
3105 break;
3106 }
3107 return(ret);
3108}
3109
3110/**
3111 * xmlXPathConvertNumber:
3112 * @val: an XPath object
3113 *
3114 * Converts an existing object to its number() equivalent
3115 *
3116 * Returns the new object, the old one is freed (or the operation
3117 * is done directly on @val)
3118 */
3119xmlXPathObjectPtr
3120xmlXPathConvertNumber(xmlXPathObjectPtr val) {
3121 xmlXPathObjectPtr ret;
3122
3123 if (val == NULL)
3124 return(xmlXPathNewFloat(0.0));
3125 if (val->type == XPATH_NUMBER)
3126 return(val);
3127 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
3128 xmlXPathFreeObject(val);
3129 return(ret);
3130}
3131
3132/**
3133 * xmlXPathCastNumberToBoolean:
3134 * @val: a number
3135 *
3136 * Converts a number to its boolean value
3137 *
3138 * Returns the boolean value
3139 */
3140int
3141xmlXPathCastNumberToBoolean (double val) {
Daniel Veillardcda96922001-08-21 10:56:31 +00003142 if (xmlXPathIsNaN(val) || (val == 0.0))
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003143 return(0);
3144 return(1);
3145}
3146
3147/**
3148 * xmlXPathCastStringToBoolean:
3149 * @val: a string
3150 *
3151 * Converts a string to its boolean value
3152 *
3153 * Returns the boolean value
3154 */
3155int
3156xmlXPathCastStringToBoolean (const xmlChar *val) {
3157 if ((val == NULL) || (xmlStrlen(val) == 0))
3158 return(0);
3159 return(1);
3160}
3161
3162/**
3163 * xmlXPathCastNodeSetToBoolean:
3164 * @ns: a node-set
3165 *
3166 * Converts a node-set to its boolean value
3167 *
3168 * Returns the boolean value
3169 */
3170int
3171xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
3172 if ((ns == NULL) || (ns->nodeNr == 0))
3173 return(0);
3174 return(1);
3175}
3176
3177/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +00003178 * xmlXPathCastToBoolean:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003179 * @val: an XPath object
3180 *
3181 * Converts an XPath object to its boolean value
3182 *
3183 * Returns the boolean value
3184 */
3185int
3186xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
3187 int ret = 0;
3188
3189 if (val == NULL)
3190 return(0);
3191 switch (val->type) {
3192 case XPATH_UNDEFINED:
3193#ifdef DEBUG_EXPR
3194 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
3195#endif
3196 ret = 0;
3197 break;
3198 case XPATH_XSLT_TREE:
3199 case XPATH_NODESET:
3200 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
3201 break;
3202 case XPATH_STRING:
3203 ret = xmlXPathCastStringToBoolean(val->stringval);
3204 break;
3205 case XPATH_NUMBER:
3206 ret = xmlXPathCastNumberToBoolean(val->floatval);
3207 break;
3208 case XPATH_BOOLEAN:
3209 ret = val->boolval;
3210 break;
3211 case XPATH_USERS:
3212 case XPATH_POINT:
3213 case XPATH_RANGE:
3214 case XPATH_LOCATIONSET:
3215 TODO;
3216 ret = 0;
3217 break;
3218 }
3219 return(ret);
3220}
3221
3222
3223/**
3224 * xmlXPathConvertBoolean:
3225 * @val: an XPath object
3226 *
3227 * Converts an existing object to its boolean() equivalent
3228 *
3229 * Returns the new object, the old one is freed (or the operation
3230 * is done directly on @val)
3231 */
3232xmlXPathObjectPtr
3233xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
3234 xmlXPathObjectPtr ret;
3235
3236 if (val == NULL)
3237 return(xmlXPathNewBoolean(0));
3238 if (val->type == XPATH_BOOLEAN)
3239 return(val);
3240 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
3241 xmlXPathFreeObject(val);
3242 return(ret);
3243}
3244
Owen Taylor3473f882001-02-23 17:55:21 +00003245/************************************************************************
3246 * *
3247 * Routines to handle XPath contexts *
3248 * *
3249 ************************************************************************/
3250
3251/**
3252 * xmlXPathNewContext:
3253 * @doc: the XML document
3254 *
3255 * Create a new xmlXPathContext
3256 *
3257 * Returns the xmlXPathContext just allocated.
3258 */
3259xmlXPathContextPtr
3260xmlXPathNewContext(xmlDocPtr doc) {
3261 xmlXPathContextPtr ret;
3262
3263 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
3264 if (ret == NULL) {
3265 xmlGenericError(xmlGenericErrorContext,
3266 "xmlXPathNewContext: out of memory\n");
3267 return(NULL);
3268 }
3269 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
3270 ret->doc = doc;
3271 ret->node = NULL;
3272
3273 ret->varHash = NULL;
3274
3275 ret->nb_types = 0;
3276 ret->max_types = 0;
3277 ret->types = NULL;
3278
3279 ret->funcHash = xmlHashCreate(0);
3280
3281 ret->nb_axis = 0;
3282 ret->max_axis = 0;
3283 ret->axis = NULL;
3284
3285 ret->nsHash = NULL;
3286 ret->user = NULL;
3287
3288 ret->contextSize = -1;
3289 ret->proximityPosition = -1;
3290
3291 xmlXPathRegisterAllFunctions(ret);
3292
3293 return(ret);
3294}
3295
3296/**
3297 * xmlXPathFreeContext:
3298 * @ctxt: the context to free
3299 *
3300 * Free up an xmlXPathContext
3301 */
3302void
3303xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
3304 xmlXPathRegisteredNsCleanup(ctxt);
3305 xmlXPathRegisteredFuncsCleanup(ctxt);
3306 xmlXPathRegisteredVariablesCleanup(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00003307 xmlFree(ctxt);
3308}
3309
3310/************************************************************************
3311 * *
3312 * Routines to handle XPath parser contexts *
3313 * *
3314 ************************************************************************/
3315
3316#define CHECK_CTXT(ctxt) \
3317 if (ctxt == NULL) { \
3318 xmlGenericError(xmlGenericErrorContext, \
3319 "%s:%d Internal error: ctxt == NULL\n", \
3320 __FILE__, __LINE__); \
3321 } \
3322
3323
3324#define CHECK_CONTEXT(ctxt) \
3325 if (ctxt == NULL) { \
3326 xmlGenericError(xmlGenericErrorContext, \
3327 "%s:%d Internal error: no context\n", \
3328 __FILE__, __LINE__); \
3329 } \
3330 else if (ctxt->doc == NULL) { \
3331 xmlGenericError(xmlGenericErrorContext, \
3332 "%s:%d Internal error: no document\n", \
3333 __FILE__, __LINE__); \
3334 } \
3335 else if (ctxt->doc->children == NULL) { \
3336 xmlGenericError(xmlGenericErrorContext, \
3337 "%s:%d Internal error: document without root\n", \
3338 __FILE__, __LINE__); \
3339 } \
3340
3341
3342/**
3343 * xmlXPathNewParserContext:
3344 * @str: the XPath expression
3345 * @ctxt: the XPath context
3346 *
3347 * Create a new xmlXPathParserContext
3348 *
3349 * Returns the xmlXPathParserContext just allocated.
3350 */
3351xmlXPathParserContextPtr
3352xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
3353 xmlXPathParserContextPtr ret;
3354
3355 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3356 if (ret == NULL) {
3357 xmlGenericError(xmlGenericErrorContext,
3358 "xmlXPathNewParserContext: out of memory\n");
3359 return(NULL);
3360 }
3361 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3362 ret->cur = ret->base = str;
3363 ret->context = ctxt;
3364
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003365 ret->comp = xmlXPathNewCompExpr();
3366 if (ret->comp == NULL) {
3367 xmlFree(ret->valueTab);
3368 xmlFree(ret);
3369 return(NULL);
3370 }
3371
3372 return(ret);
3373}
3374
3375/**
3376 * xmlXPathCompParserContext:
3377 * @comp: the XPath compiled expression
3378 * @ctxt: the XPath context
3379 *
3380 * Create a new xmlXPathParserContext when processing a compiled expression
3381 *
3382 * Returns the xmlXPathParserContext just allocated.
3383 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003384static xmlXPathParserContextPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003385xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
3386 xmlXPathParserContextPtr ret;
3387
3388 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
3389 if (ret == NULL) {
3390 xmlGenericError(xmlGenericErrorContext,
3391 "xmlXPathNewParserContext: out of memory\n");
3392 return(NULL);
3393 }
3394 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
3395
Owen Taylor3473f882001-02-23 17:55:21 +00003396 /* Allocate the value stack */
3397 ret->valueTab = (xmlXPathObjectPtr *)
3398 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003399 if (ret->valueTab == NULL) {
3400 xmlFree(ret);
3401 xmlGenericError(xmlGenericErrorContext,
3402 "xmlXPathNewParserContext: out of memory\n");
3403 return(NULL);
3404 }
Owen Taylor3473f882001-02-23 17:55:21 +00003405 ret->valueNr = 0;
3406 ret->valueMax = 10;
3407 ret->value = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003408
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00003409 ret->context = ctxt;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003410 ret->comp = comp;
3411
Owen Taylor3473f882001-02-23 17:55:21 +00003412 return(ret);
3413}
3414
3415/**
3416 * xmlXPathFreeParserContext:
3417 * @ctxt: the context to free
3418 *
3419 * Free up an xmlXPathParserContext
3420 */
3421void
3422xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
3423 if (ctxt->valueTab != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00003424 xmlFree(ctxt->valueTab);
3425 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00003426 if (ctxt->comp)
3427 xmlXPathFreeCompExpr(ctxt->comp);
Owen Taylor3473f882001-02-23 17:55:21 +00003428 xmlFree(ctxt);
3429}
3430
3431/************************************************************************
3432 * *
3433 * The implicit core function library *
3434 * *
3435 ************************************************************************/
3436
Owen Taylor3473f882001-02-23 17:55:21 +00003437/**
Daniel Veillardf06307e2001-07-03 10:35:50 +00003438 * xmlXPathNodeStringHash:
3439 * @node: a node pointer
3440 *
3441 * Function computing the beginning of the string value of the node,
3442 * used to speed up comparisons
3443 *
3444 * Returns an int usable as a hash
3445 */
3446static unsigned int
3447xmlXPathNodeValHash(xmlNodePtr node) {
3448 int len = 2;
3449 const xmlChar * string = NULL;
3450 xmlNodePtr tmp = NULL;
3451 unsigned int ret = 0;
3452
3453 if (node == NULL)
3454 return(0);
3455
3456
3457 switch (node->type) {
3458 case XML_COMMENT_NODE:
3459 case XML_PI_NODE:
3460 case XML_CDATA_SECTION_NODE:
3461 case XML_TEXT_NODE:
3462 string = node->content;
3463 if (string == NULL)
3464 return(0);
3465 if (string[0] == 0)
3466 return(0);
3467 return(((unsigned int) string[0]) +
3468 (((unsigned int) string[1]) << 8));
3469 case XML_NAMESPACE_DECL:
3470 string = ((xmlNsPtr)node)->href;
3471 if (string == NULL)
3472 return(0);
3473 if (string[0] == 0)
3474 return(0);
3475 return(((unsigned int) string[0]) +
3476 (((unsigned int) string[1]) << 8));
3477 case XML_ATTRIBUTE_NODE:
3478 tmp = ((xmlAttrPtr) node)->children;
3479 break;
3480 case XML_ELEMENT_NODE:
3481 tmp = node->children;
3482 break;
3483 default:
3484 return(0);
3485 }
3486 while (tmp != NULL) {
3487 switch (tmp->type) {
3488 case XML_COMMENT_NODE:
3489 case XML_PI_NODE:
3490 case XML_CDATA_SECTION_NODE:
3491 case XML_TEXT_NODE:
3492 string = tmp->content;
3493 break;
3494 case XML_NAMESPACE_DECL:
3495 string = ((xmlNsPtr)tmp)->href;
3496 break;
3497 default:
3498 break;
3499 }
3500 if ((string != NULL) && (string[0] != 0)) {
3501 if (string[0] == 0)
3502 return(0);
3503 if (len == 1) {
3504 return(ret + (((unsigned int) string[0]) << 8));
3505 }
3506 if (string[1] == 0) {
3507 len = 1;
3508 ret = (unsigned int) string[0];
3509 } else {
3510 return(((unsigned int) string[0]) +
3511 (((unsigned int) string[1]) << 8));
3512 }
3513 }
3514 /*
3515 * Skip to next node
3516 */
3517 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
3518 if (tmp->children->type != XML_ENTITY_DECL) {
3519 tmp = tmp->children;
3520 continue;
3521 }
3522 }
3523 if (tmp == node)
3524 break;
3525
3526 if (tmp->next != NULL) {
3527 tmp = tmp->next;
3528 continue;
3529 }
3530
3531 do {
3532 tmp = tmp->parent;
3533 if (tmp == NULL)
3534 break;
3535 if (tmp == node) {
3536 tmp = NULL;
3537 break;
3538 }
3539 if (tmp->next != NULL) {
3540 tmp = tmp->next;
3541 break;
3542 }
3543 } while (tmp != NULL);
3544 }
3545 return(ret);
3546}
3547
3548/**
3549 * xmlXPathStringHash:
3550 * @string: a string
3551 *
3552 * Function computing the beginning of the string value of the node,
3553 * used to speed up comparisons
3554 *
3555 * Returns an int usable as a hash
3556 */
3557static unsigned int
3558xmlXPathStringHash(const xmlChar * string) {
3559 if (string == NULL)
3560 return((unsigned int) 0);
3561 if (string[0] == 0)
3562 return(0);
3563 return(((unsigned int) string[0]) +
3564 (((unsigned int) string[1]) << 8));
3565}
3566
3567/**
Owen Taylor3473f882001-02-23 17:55:21 +00003568 * xmlXPathCompareNodeSetFloat:
3569 * @ctxt: the XPath Parser context
3570 * @inf: less than (1) or greater than (0)
3571 * @strict: is the comparison strict
3572 * @arg: the node set
3573 * @f: the value
3574 *
3575 * Implement the compare operation between a nodeset and a number
3576 * @ns < @val (1, 1, ...
3577 * @ns <= @val (1, 0, ...
3578 * @ns > @val (0, 1, ...
3579 * @ns >= @val (0, 0, ...
3580 *
3581 * If one object to be compared is a node-set and the other is a number,
3582 * then the comparison will be true if and only if there is a node in the
3583 * node-set such that the result of performing the comparison on the number
3584 * to be compared and on the result of converting the string-value of that
3585 * node to a number using the number function is true.
3586 *
3587 * Returns 0 or 1 depending on the results of the test.
3588 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003589static int
Owen Taylor3473f882001-02-23 17:55:21 +00003590xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
3591 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
3592 int i, ret = 0;
3593 xmlNodeSetPtr ns;
3594 xmlChar *str2;
3595
3596 if ((f == NULL) || (arg == NULL) ||
3597 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3598 xmlXPathFreeObject(arg);
3599 xmlXPathFreeObject(f);
3600 return(0);
3601 }
3602 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003603 if (ns != NULL) {
3604 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003605 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003606 if (str2 != NULL) {
3607 valuePush(ctxt,
3608 xmlXPathNewString(str2));
3609 xmlFree(str2);
3610 xmlXPathNumberFunction(ctxt, 1);
3611 valuePush(ctxt, xmlXPathObjectCopy(f));
3612 ret = xmlXPathCompareValues(ctxt, inf, strict);
3613 if (ret)
3614 break;
3615 }
3616 }
Owen Taylor3473f882001-02-23 17:55:21 +00003617 }
3618 xmlXPathFreeObject(arg);
3619 xmlXPathFreeObject(f);
3620 return(ret);
3621}
3622
3623/**
3624 * xmlXPathCompareNodeSetString:
3625 * @ctxt: the XPath Parser context
3626 * @inf: less than (1) or greater than (0)
3627 * @strict: is the comparison strict
3628 * @arg: the node set
3629 * @s: the value
3630 *
3631 * Implement the compare operation between a nodeset and a string
3632 * @ns < @val (1, 1, ...
3633 * @ns <= @val (1, 0, ...
3634 * @ns > @val (0, 1, ...
3635 * @ns >= @val (0, 0, ...
3636 *
3637 * If one object to be compared is a node-set and the other is a string,
3638 * then the comparison will be true if and only if there is a node in
3639 * the node-set such that the result of performing the comparison on the
3640 * string-value of the node and the other string is true.
3641 *
3642 * Returns 0 or 1 depending on the results of the test.
3643 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003644static int
Owen Taylor3473f882001-02-23 17:55:21 +00003645xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
3646 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
3647 int i, ret = 0;
3648 xmlNodeSetPtr ns;
3649 xmlChar *str2;
3650
3651 if ((s == NULL) || (arg == NULL) ||
3652 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
3653 xmlXPathFreeObject(arg);
3654 xmlXPathFreeObject(s);
3655 return(0);
3656 }
3657 ns = arg->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00003658 if (ns != NULL) {
3659 for (i = 0;i < ns->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003660 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003661 if (str2 != NULL) {
3662 valuePush(ctxt,
3663 xmlXPathNewString(str2));
3664 xmlFree(str2);
3665 valuePush(ctxt, xmlXPathObjectCopy(s));
3666 ret = xmlXPathCompareValues(ctxt, inf, strict);
3667 if (ret)
3668 break;
3669 }
3670 }
Owen Taylor3473f882001-02-23 17:55:21 +00003671 }
3672 xmlXPathFreeObject(arg);
3673 xmlXPathFreeObject(s);
3674 return(ret);
3675}
3676
3677/**
3678 * xmlXPathCompareNodeSets:
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003679 * @inf: less than (1) or greater than (0)
Owen Taylor3473f882001-02-23 17:55:21 +00003680 * @strict: is the comparison strict
3681 * @arg1: the fist node set object
3682 * @arg2: the second node set object
3683 *
3684 * Implement the compare operation on nodesets:
3685 *
3686 * If both objects to be compared are node-sets, then the comparison
3687 * will be true if and only if there is a node in the first node-set
3688 * and a node in the second node-set such that the result of performing
3689 * the comparison on the string-values of the two nodes is true.
3690 * ....
3691 * When neither object to be compared is a node-set and the operator
3692 * is <=, <, >= or >, then the objects are compared by converting both
3693 * objects to numbers and comparing the numbers according to IEEE 754.
3694 * ....
3695 * The number function converts its argument to a number as follows:
3696 * - a string that consists of optional whitespace followed by an
3697 * optional minus sign followed by a Number followed by whitespace
3698 * is converted to the IEEE 754 number that is nearest (according
3699 * to the IEEE 754 round-to-nearest rule) to the mathematical value
3700 * represented by the string; any other string is converted to NaN
3701 *
3702 * Conclusion all nodes need to be converted first to their string value
3703 * and then the comparison must be done when possible
3704 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003705static int
3706xmlXPathCompareNodeSets(int inf, int strict,
Owen Taylor3473f882001-02-23 17:55:21 +00003707 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3708 int i, j, init = 0;
3709 double val1;
3710 double *values2;
3711 int ret = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00003712 xmlNodeSetPtr ns1;
3713 xmlNodeSetPtr ns2;
3714
3715 if ((arg1 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003716 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
3717 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003718 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003719 }
Owen Taylor3473f882001-02-23 17:55:21 +00003720 if ((arg2 == NULL) ||
Daniel Veillard4dd93462001-04-02 15:16:19 +00003721 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
3722 xmlXPathFreeObject(arg1);
3723 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003724 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003725 }
Owen Taylor3473f882001-02-23 17:55:21 +00003726
3727 ns1 = arg1->nodesetval;
3728 ns2 = arg2->nodesetval;
3729
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003730 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003731 xmlXPathFreeObject(arg1);
3732 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003733 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003734 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003735 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003736 xmlXPathFreeObject(arg1);
3737 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003738 return(0);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003739 }
Owen Taylor3473f882001-02-23 17:55:21 +00003740
3741 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
3742 if (values2 == NULL) {
Daniel Veillard4dd93462001-04-02 15:16:19 +00003743 xmlXPathFreeObject(arg1);
3744 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003745 return(0);
3746 }
3747 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003748 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
Daniel Veillardcda96922001-08-21 10:56:31 +00003749 if (xmlXPathIsNaN(val1))
Owen Taylor3473f882001-02-23 17:55:21 +00003750 continue;
3751 for (j = 0;j < ns2->nodeNr;j++) {
3752 if (init == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00003753 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
Owen Taylor3473f882001-02-23 17:55:21 +00003754 }
Daniel Veillardcda96922001-08-21 10:56:31 +00003755 if (xmlXPathIsNaN(values2[j]))
Owen Taylor3473f882001-02-23 17:55:21 +00003756 continue;
3757 if (inf && strict)
3758 ret = (val1 < values2[j]);
3759 else 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 if (ret)
3766 break;
3767 }
3768 if (ret)
3769 break;
3770 init = 1;
3771 }
3772 xmlFree(values2);
Daniel Veillard4dd93462001-04-02 15:16:19 +00003773 xmlXPathFreeObject(arg1);
3774 xmlXPathFreeObject(arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00003775 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00003776}
3777
3778/**
3779 * xmlXPathCompareNodeSetValue:
3780 * @ctxt: the XPath Parser context
3781 * @inf: less than (1) or greater than (0)
3782 * @strict: is the comparison strict
3783 * @arg: the node set
3784 * @val: the value
3785 *
3786 * Implement the compare operation between a nodeset and a value
3787 * @ns < @val (1, 1, ...
3788 * @ns <= @val (1, 0, ...
3789 * @ns > @val (0, 1, ...
3790 * @ns >= @val (0, 0, ...
3791 *
3792 * If one object to be compared is a node-set and the other is a boolean,
3793 * then the comparison will be true if and only if the result of performing
3794 * the comparison on the boolean and on the result of converting
3795 * the node-set to a boolean using the boolean function is true.
3796 *
3797 * Returns 0 or 1 depending on the results of the test.
3798 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003799static int
Owen Taylor3473f882001-02-23 17:55:21 +00003800xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
3801 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
3802 if ((val == NULL) || (arg == NULL) ||
3803 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3804 return(0);
3805
3806 switch(val->type) {
3807 case XPATH_NUMBER:
3808 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
3809 case XPATH_NODESET:
3810 case XPATH_XSLT_TREE:
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003811 return(xmlXPathCompareNodeSets(inf, strict, arg, val));
Owen Taylor3473f882001-02-23 17:55:21 +00003812 case XPATH_STRING:
3813 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
3814 case XPATH_BOOLEAN:
3815 valuePush(ctxt, arg);
3816 xmlXPathBooleanFunction(ctxt, 1);
3817 valuePush(ctxt, val);
3818 return(xmlXPathCompareValues(ctxt, inf, strict));
3819 default:
3820 TODO
3821 return(0);
3822 }
3823 return(0);
3824}
3825
3826/**
3827 * xmlXPathEqualNodeSetString
3828 * @arg: the nodeset object argument
3829 * @str: the string to compare to.
3830 *
3831 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3832 * If one object to be compared is a node-set and the other is a string,
3833 * then the comparison will be true if and only if there is a node in
3834 * the node-set such that the result of performing the comparison on the
3835 * string-value of the node and the other string is true.
3836 *
3837 * Returns 0 or 1 depending on the results of the test.
3838 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003839static int
Daniel Veillardf06307e2001-07-03 10:35:50 +00003840xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str)
3841{
Owen Taylor3473f882001-02-23 17:55:21 +00003842 int i;
3843 xmlNodeSetPtr ns;
3844 xmlChar *str2;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003845 unsigned int hash;
Owen Taylor3473f882001-02-23 17:55:21 +00003846
3847 if ((str == NULL) || (arg == NULL) ||
Daniel Veillardf06307e2001-07-03 10:35:50 +00003848 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3849 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003850 ns = arg->nodesetval;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003851 hash = xmlXPathStringHash(str);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00003852 if (ns == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00003853 return (0);
Daniel Veillard73c9c042001-07-05 20:02:54 +00003854 if (ns->nodeNr <= 0) {
3855 if (hash == 0)
3856 return(1);
3857 return(0);
3858 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003859 for (i = 0; i < ns->nodeNr; i++) {
3860 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
3861 str2 = xmlNodeGetContent(ns->nodeTab[i]);
3862 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
3863 xmlFree(str2);
3864 return (1);
3865 }
3866 if (str2 != NULL)
3867 xmlFree(str2);
3868 }
Owen Taylor3473f882001-02-23 17:55:21 +00003869 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003870 return (0);
Owen Taylor3473f882001-02-23 17:55:21 +00003871}
3872
3873/**
3874 * xmlXPathEqualNodeSetFloat
3875 * @arg: the nodeset object argument
3876 * @f: the float to compare to
3877 *
3878 * Implement the equal operation on XPath objects content: @arg1 == @arg2
3879 * If one object to be compared is a node-set and the other is a number,
3880 * then the comparison will be true if and only if there is a node in
3881 * the node-set such that the result of performing the comparison on the
3882 * number to be compared and on the result of converting the string-value
3883 * of that node to a number using the number function is true.
3884 *
3885 * Returns 0 or 1 depending on the results of the test.
3886 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003887static int
Owen Taylor3473f882001-02-23 17:55:21 +00003888xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
3889 char buf[100] = "";
3890
3891 if ((arg == NULL) ||
3892 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
3893 return(0);
3894
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003895 xmlXPathFormatNumber(f, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00003896 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
3897}
3898
3899
3900/**
3901 * xmlXPathEqualNodeSets
3902 * @arg1: first nodeset object argument
3903 * @arg2: second nodeset object argument
3904 *
3905 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
3906 * If both objects to be compared are node-sets, then the comparison
3907 * will be true if and only if there is a node in the first node-set and
3908 * a node in the second node-set such that the result of performing the
3909 * comparison on the string-values of the two nodes is true.
3910 *
3911 * (needless to say, this is a costly operation)
3912 *
3913 * Returns 0 or 1 depending on the results of the test.
3914 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003915static int
Owen Taylor3473f882001-02-23 17:55:21 +00003916xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
3917 int i, j;
Daniel Veillardf06307e2001-07-03 10:35:50 +00003918 unsigned int *hashs1;
3919 unsigned int *hashs2;
Owen Taylor3473f882001-02-23 17:55:21 +00003920 xmlChar **values1;
3921 xmlChar **values2;
3922 int ret = 0;
3923 xmlNodeSetPtr ns1;
3924 xmlNodeSetPtr ns2;
3925
3926 if ((arg1 == NULL) ||
3927 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
3928 return(0);
3929 if ((arg2 == NULL) ||
3930 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
3931 return(0);
3932
3933 ns1 = arg1->nodesetval;
3934 ns2 = arg2->nodesetval;
3935
Daniel Veillard911f49a2001-04-07 15:39:35 +00003936 if ((ns1 == NULL) || (ns1->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00003937 return(0);
Daniel Veillard911f49a2001-04-07 15:39:35 +00003938 if ((ns2 == NULL) || (ns2->nodeNr <= 0))
Owen Taylor3473f882001-02-23 17:55:21 +00003939 return(0);
3940
3941 /*
3942 * check if there is a node pertaining to both sets
3943 */
3944 for (i = 0;i < ns1->nodeNr;i++)
3945 for (j = 0;j < ns2->nodeNr;j++)
3946 if (ns1->nodeTab[i] == ns2->nodeTab[j])
3947 return(1);
3948
3949 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
3950 if (values1 == NULL)
3951 return(0);
Daniel Veillardf06307e2001-07-03 10:35:50 +00003952 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
3953 if (hashs1 == NULL) {
3954 xmlFree(values1);
3955 return(0);
3956 }
Owen Taylor3473f882001-02-23 17:55:21 +00003957 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
3958 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
3959 if (values2 == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00003960 xmlFree(hashs1);
Owen Taylor3473f882001-02-23 17:55:21 +00003961 xmlFree(values1);
3962 return(0);
3963 }
Daniel Veillardf06307e2001-07-03 10:35:50 +00003964 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
3965 if (hashs2 == NULL) {
3966 xmlFree(hashs1);
3967 xmlFree(values1);
3968 xmlFree(values2);
3969 return(0);
3970 }
Owen Taylor3473f882001-02-23 17:55:21 +00003971 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
3972 for (i = 0;i < ns1->nodeNr;i++) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00003973 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00003974 for (j = 0;j < ns2->nodeNr;j++) {
3975 if (i == 0)
Daniel Veillardf06307e2001-07-03 10:35:50 +00003976 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
3977 if (hashs1[i] == hashs2[j]) {
3978 if (values1[i] == NULL)
3979 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
3980 if (values2[j] == NULL)
3981 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
3982 ret = xmlStrEqual(values1[i], values2[j]);
3983 if (ret)
3984 break;
3985 }
Owen Taylor3473f882001-02-23 17:55:21 +00003986 }
3987 if (ret)
3988 break;
3989 }
3990 for (i = 0;i < ns1->nodeNr;i++)
3991 if (values1[i] != NULL)
3992 xmlFree(values1[i]);
3993 for (j = 0;j < ns2->nodeNr;j++)
3994 if (values2[j] != NULL)
3995 xmlFree(values2[j]);
3996 xmlFree(values1);
3997 xmlFree(values2);
Daniel Veillardf06307e2001-07-03 10:35:50 +00003998 xmlFree(hashs1);
3999 xmlFree(hashs2);
Owen Taylor3473f882001-02-23 17:55:21 +00004000 return(ret);
4001}
4002
4003/**
4004 * xmlXPathEqualValues:
4005 * @ctxt: the XPath Parser context
4006 *
4007 * Implement the equal operation on XPath objects content: @arg1 == @arg2
4008 *
4009 * Returns 0 or 1 depending on the results of the test.
4010 */
4011int
4012xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
4013 xmlXPathObjectPtr arg1, arg2;
4014 int ret = 0;
4015
4016 arg1 = valuePop(ctxt);
4017 if (arg1 == NULL)
4018 XP_ERROR0(XPATH_INVALID_OPERAND);
4019
4020 arg2 = valuePop(ctxt);
4021 if (arg2 == NULL) {
4022 xmlXPathFreeObject(arg1);
4023 XP_ERROR0(XPATH_INVALID_OPERAND);
4024 }
4025
4026 if (arg1 == arg2) {
4027#ifdef DEBUG_EXPR
4028 xmlGenericError(xmlGenericErrorContext,
4029 "Equal: by pointer\n");
4030#endif
4031 return(1);
4032 }
4033
4034 switch (arg1->type) {
4035 case XPATH_UNDEFINED:
4036#ifdef DEBUG_EXPR
4037 xmlGenericError(xmlGenericErrorContext,
4038 "Equal: undefined\n");
4039#endif
4040 break;
4041 case XPATH_XSLT_TREE:
4042 case XPATH_NODESET:
4043 switch (arg2->type) {
4044 case XPATH_UNDEFINED:
4045#ifdef DEBUG_EXPR
4046 xmlGenericError(xmlGenericErrorContext,
4047 "Equal: undefined\n");
4048#endif
4049 break;
4050 case XPATH_XSLT_TREE:
4051 case XPATH_NODESET:
4052 ret = xmlXPathEqualNodeSets(arg1, arg2);
4053 break;
4054 case XPATH_BOOLEAN:
4055 if ((arg1->nodesetval == NULL) ||
4056 (arg1->nodesetval->nodeNr == 0)) ret = 0;
4057 else
4058 ret = 1;
4059 ret = (ret == arg2->boolval);
4060 break;
4061 case XPATH_NUMBER:
4062 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
4063 break;
4064 case XPATH_STRING:
4065 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
4066 break;
4067 case XPATH_USERS:
4068 case XPATH_POINT:
4069 case XPATH_RANGE:
4070 case XPATH_LOCATIONSET:
4071 TODO
4072 break;
4073 }
4074 break;
4075 case XPATH_BOOLEAN:
4076 switch (arg2->type) {
4077 case XPATH_UNDEFINED:
4078#ifdef DEBUG_EXPR
4079 xmlGenericError(xmlGenericErrorContext,
4080 "Equal: undefined\n");
4081#endif
4082 break;
4083 case XPATH_NODESET:
4084 case XPATH_XSLT_TREE:
4085 if ((arg2->nodesetval == NULL) ||
4086 (arg2->nodesetval->nodeNr == 0)) ret = 0;
4087 else
4088 ret = 1;
4089 break;
4090 case XPATH_BOOLEAN:
4091#ifdef DEBUG_EXPR
4092 xmlGenericError(xmlGenericErrorContext,
4093 "Equal: %d boolean %d \n",
4094 arg1->boolval, arg2->boolval);
4095#endif
4096 ret = (arg1->boolval == arg2->boolval);
4097 break;
4098 case XPATH_NUMBER:
4099 if (arg2->floatval) ret = 1;
4100 else ret = 0;
4101 ret = (arg1->boolval == ret);
4102 break;
4103 case XPATH_STRING:
4104 if ((arg2->stringval == NULL) ||
4105 (arg2->stringval[0] == 0)) ret = 0;
4106 else
4107 ret = 1;
4108 ret = (arg1->boolval == ret);
4109 break;
4110 case XPATH_USERS:
4111 case XPATH_POINT:
4112 case XPATH_RANGE:
4113 case XPATH_LOCATIONSET:
4114 TODO
4115 break;
4116 }
4117 break;
4118 case XPATH_NUMBER:
4119 switch (arg2->type) {
4120 case XPATH_UNDEFINED:
4121#ifdef DEBUG_EXPR
4122 xmlGenericError(xmlGenericErrorContext,
4123 "Equal: undefined\n");
4124#endif
4125 break;
4126 case XPATH_NODESET:
4127 case XPATH_XSLT_TREE:
4128 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
4129 break;
4130 case XPATH_BOOLEAN:
4131 if (arg1->floatval) ret = 1;
4132 else ret = 0;
4133 ret = (arg2->boolval == ret);
4134 break;
4135 case XPATH_STRING:
4136 valuePush(ctxt, arg2);
4137 xmlXPathNumberFunction(ctxt, 1);
4138 arg2 = valuePop(ctxt);
4139 /* no break on purpose */
4140 case XPATH_NUMBER:
4141 ret = (arg1->floatval == arg2->floatval);
4142 break;
4143 case XPATH_USERS:
4144 case XPATH_POINT:
4145 case XPATH_RANGE:
4146 case XPATH_LOCATIONSET:
4147 TODO
4148 break;
4149 }
4150 break;
4151 case XPATH_STRING:
4152 switch (arg2->type) {
4153 case XPATH_UNDEFINED:
4154#ifdef DEBUG_EXPR
4155 xmlGenericError(xmlGenericErrorContext,
4156 "Equal: undefined\n");
4157#endif
4158 break;
4159 case XPATH_NODESET:
4160 case XPATH_XSLT_TREE:
4161 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
4162 break;
4163 case XPATH_BOOLEAN:
4164 if ((arg1->stringval == NULL) ||
4165 (arg1->stringval[0] == 0)) ret = 0;
4166 else
4167 ret = 1;
4168 ret = (arg2->boolval == ret);
4169 break;
4170 case XPATH_STRING:
4171 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
4172 break;
4173 case XPATH_NUMBER:
4174 valuePush(ctxt, arg1);
4175 xmlXPathNumberFunction(ctxt, 1);
4176 arg1 = valuePop(ctxt);
4177 ret = (arg1->floatval == arg2->floatval);
4178 break;
4179 case XPATH_USERS:
4180 case XPATH_POINT:
4181 case XPATH_RANGE:
4182 case XPATH_LOCATIONSET:
4183 TODO
4184 break;
4185 }
4186 break;
4187 case XPATH_USERS:
4188 case XPATH_POINT:
4189 case XPATH_RANGE:
4190 case XPATH_LOCATIONSET:
4191 TODO
4192 break;
4193 }
4194 xmlXPathFreeObject(arg1);
4195 xmlXPathFreeObject(arg2);
4196 return(ret);
4197}
4198
4199
4200/**
4201 * xmlXPathCompareValues:
4202 * @ctxt: the XPath Parser context
4203 * @inf: less than (1) or greater than (0)
4204 * @strict: is the comparison strict
4205 *
4206 * Implement the compare operation on XPath objects:
4207 * @arg1 < @arg2 (1, 1, ...
4208 * @arg1 <= @arg2 (1, 0, ...
4209 * @arg1 > @arg2 (0, 1, ...
4210 * @arg1 >= @arg2 (0, 0, ...
4211 *
4212 * When neither object to be compared is a node-set and the operator is
4213 * <=, <, >=, >, then the objects are compared by converted both objects
4214 * to numbers and comparing the numbers according to IEEE 754. The <
4215 * comparison will be true if and only if the first number is less than the
4216 * second number. The <= comparison will be true if and only if the first
4217 * number is less than or equal to the second number. The > comparison
4218 * will be true if and only if the first number is greater than the second
4219 * number. The >= comparison will be true if and only if the first number
4220 * is greater than or equal to the second number.
4221 *
4222 * Returns 1 if the comparaison succeeded, 0 if it failed
4223 */
4224int
4225xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
4226 int ret = 0;
4227 xmlXPathObjectPtr arg1, arg2;
4228
4229 arg2 = valuePop(ctxt);
4230 if (arg2 == NULL) {
4231 XP_ERROR0(XPATH_INVALID_OPERAND);
4232 }
4233
4234 arg1 = valuePop(ctxt);
4235 if (arg1 == NULL) {
4236 xmlXPathFreeObject(arg2);
4237 XP_ERROR0(XPATH_INVALID_OPERAND);
4238 }
4239
4240 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
4241 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004242 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004243 } else {
4244 if (arg1->type == XPATH_NODESET) {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004245 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
4246 arg1, arg2);
Owen Taylor3473f882001-02-23 17:55:21 +00004247 } else {
Daniel Veillard4af6b6e2001-03-06 08:33:38 +00004248 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
4249 arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00004250 }
4251 }
4252 return(ret);
4253 }
4254
4255 if (arg1->type != XPATH_NUMBER) {
4256 valuePush(ctxt, arg1);
4257 xmlXPathNumberFunction(ctxt, 1);
4258 arg1 = valuePop(ctxt);
4259 }
4260 if (arg1->type != XPATH_NUMBER) {
4261 xmlXPathFreeObject(arg1);
4262 xmlXPathFreeObject(arg2);
4263 XP_ERROR0(XPATH_INVALID_OPERAND);
4264 }
4265 if (arg2->type != XPATH_NUMBER) {
4266 valuePush(ctxt, arg2);
4267 xmlXPathNumberFunction(ctxt, 1);
4268 arg2 = valuePop(ctxt);
4269 }
4270 if (arg2->type != XPATH_NUMBER) {
4271 xmlXPathFreeObject(arg1);
4272 xmlXPathFreeObject(arg2);
4273 XP_ERROR0(XPATH_INVALID_OPERAND);
4274 }
4275 /*
4276 * Add tests for infinity and nan
4277 * => feedback on 3.4 for Inf and NaN
4278 */
4279 if (inf && strict)
4280 ret = (arg1->floatval < arg2->floatval);
4281 else 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 xmlXPathFreeObject(arg1);
4288 xmlXPathFreeObject(arg2);
4289 return(ret);
4290}
4291
4292/**
4293 * xmlXPathValueFlipSign:
4294 * @ctxt: the XPath Parser context
4295 *
4296 * Implement the unary - operation on an XPath object
4297 * The numeric operators convert their operands to numbers as if
4298 * by calling the number function.
4299 */
4300void
4301xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004302 CAST_TO_NUMBER;
4303 CHECK_TYPE(XPATH_NUMBER);
4304 ctxt->value->floatval = - ctxt->value->floatval;
Owen Taylor3473f882001-02-23 17:55:21 +00004305}
4306
4307/**
4308 * xmlXPathAddValues:
4309 * @ctxt: the XPath Parser context
4310 *
4311 * Implement the add operation on XPath objects:
4312 * The numeric operators convert their operands to numbers as if
4313 * by calling the number function.
4314 */
4315void
4316xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
4317 xmlXPathObjectPtr arg;
4318 double val;
4319
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004320 arg = valuePop(ctxt);
4321 if (arg == NULL)
4322 XP_ERROR(XPATH_INVALID_OPERAND);
4323 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004324 xmlXPathFreeObject(arg);
4325
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004326 CAST_TO_NUMBER;
4327 CHECK_TYPE(XPATH_NUMBER);
4328 ctxt->value->floatval += val;
Owen Taylor3473f882001-02-23 17:55:21 +00004329}
4330
4331/**
4332 * xmlXPathSubValues:
4333 * @ctxt: the XPath Parser context
4334 *
4335 * Implement the substraction operation on XPath objects:
4336 * The numeric operators convert their operands to numbers as if
4337 * by calling the number function.
4338 */
4339void
4340xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
4341 xmlXPathObjectPtr arg;
4342 double val;
4343
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004344 arg = valuePop(ctxt);
4345 if (arg == NULL)
4346 XP_ERROR(XPATH_INVALID_OPERAND);
4347 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004348 xmlXPathFreeObject(arg);
4349
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004350 CAST_TO_NUMBER;
4351 CHECK_TYPE(XPATH_NUMBER);
4352 ctxt->value->floatval -= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004353}
4354
4355/**
4356 * xmlXPathMultValues:
4357 * @ctxt: the XPath Parser context
4358 *
4359 * Implement the multiply operation on XPath objects:
4360 * The numeric operators convert their operands to numbers as if
4361 * by calling the number function.
4362 */
4363void
4364xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
4365 xmlXPathObjectPtr arg;
4366 double val;
4367
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004368 arg = valuePop(ctxt);
4369 if (arg == NULL)
4370 XP_ERROR(XPATH_INVALID_OPERAND);
4371 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004372 xmlXPathFreeObject(arg);
4373
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004374 CAST_TO_NUMBER;
4375 CHECK_TYPE(XPATH_NUMBER);
4376 ctxt->value->floatval *= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004377}
4378
4379/**
4380 * xmlXPathDivValues:
4381 * @ctxt: the XPath Parser context
4382 *
4383 * Implement the div operation on XPath objects @arg1 / @arg2:
4384 * The numeric operators convert their operands to numbers as if
4385 * by calling the number function.
4386 */
4387void
4388xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
4389 xmlXPathObjectPtr arg;
4390 double val;
4391
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004392 arg = valuePop(ctxt);
4393 if (arg == NULL)
4394 XP_ERROR(XPATH_INVALID_OPERAND);
4395 val = xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004396 xmlXPathFreeObject(arg);
4397
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004398 CAST_TO_NUMBER;
4399 CHECK_TYPE(XPATH_NUMBER);
4400 ctxt->value->floatval /= val;
Owen Taylor3473f882001-02-23 17:55:21 +00004401}
4402
4403/**
4404 * xmlXPathModValues:
4405 * @ctxt: the XPath Parser context
4406 *
4407 * Implement the mod operation on XPath objects: @arg1 / @arg2
4408 * The numeric operators convert their operands to numbers as if
4409 * by calling the number function.
4410 */
4411void
4412xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
4413 xmlXPathObjectPtr arg;
4414 int arg1, arg2;
4415
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004416 arg = valuePop(ctxt);
4417 if (arg == NULL)
4418 XP_ERROR(XPATH_INVALID_OPERAND);
4419 arg2 = (int) xmlXPathCastToNumber(arg);
Owen Taylor3473f882001-02-23 17:55:21 +00004420 xmlXPathFreeObject(arg);
4421
Daniel Veillardba0b8c92001-05-15 09:43:47 +00004422 CAST_TO_NUMBER;
4423 CHECK_TYPE(XPATH_NUMBER);
4424 arg1 = (int) ctxt->value->floatval;
Daniel Veillard268fd1b2001-08-26 18:46:36 +00004425 if (arg2 == 0)
4426 ctxt->value->floatval = xmlXPathNAN;
4427 else
4428 ctxt->value->floatval = arg1 % arg2;
Owen Taylor3473f882001-02-23 17:55:21 +00004429}
4430
4431/************************************************************************
4432 * *
4433 * The traversal functions *
4434 * *
4435 ************************************************************************/
4436
Owen Taylor3473f882001-02-23 17:55:21 +00004437/*
4438 * A traversal function enumerates nodes along an axis.
4439 * Initially it must be called with NULL, and it indicates
4440 * termination on the axis by returning NULL.
4441 */
4442typedef xmlNodePtr (*xmlXPathTraversalFunction)
4443 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
4444
4445/**
4446 * xmlXPathNextSelf:
4447 * @ctxt: the XPath Parser context
4448 * @cur: the current node in the traversal
4449 *
4450 * Traversal function for the "self" direction
4451 * The self axis contains just the context node itself
4452 *
4453 * Returns the next element following that axis
4454 */
4455xmlNodePtr
4456xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4457 if (cur == NULL)
4458 return(ctxt->context->node);
4459 return(NULL);
4460}
4461
4462/**
4463 * xmlXPathNextChild:
4464 * @ctxt: the XPath Parser context
4465 * @cur: the current node in the traversal
4466 *
4467 * Traversal function for the "child" direction
4468 * The child axis contains the children of the context node in document order.
4469 *
4470 * Returns the next element following that axis
4471 */
4472xmlNodePtr
4473xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4474 if (cur == NULL) {
4475 if (ctxt->context->node == NULL) return(NULL);
4476 switch (ctxt->context->node->type) {
4477 case XML_ELEMENT_NODE:
4478 case XML_TEXT_NODE:
4479 case XML_CDATA_SECTION_NODE:
4480 case XML_ENTITY_REF_NODE:
4481 case XML_ENTITY_NODE:
4482 case XML_PI_NODE:
4483 case XML_COMMENT_NODE:
4484 case XML_NOTATION_NODE:
4485 case XML_DTD_NODE:
4486 return(ctxt->context->node->children);
4487 case XML_DOCUMENT_NODE:
4488 case XML_DOCUMENT_TYPE_NODE:
4489 case XML_DOCUMENT_FRAG_NODE:
4490 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004491#ifdef LIBXML_DOCB_ENABLED
4492 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004493#endif
4494 return(((xmlDocPtr) ctxt->context->node)->children);
4495 case XML_ELEMENT_DECL:
4496 case XML_ATTRIBUTE_DECL:
4497 case XML_ENTITY_DECL:
4498 case XML_ATTRIBUTE_NODE:
4499 case XML_NAMESPACE_DECL:
4500 case XML_XINCLUDE_START:
4501 case XML_XINCLUDE_END:
4502 return(NULL);
4503 }
4504 return(NULL);
4505 }
4506 if ((cur->type == XML_DOCUMENT_NODE) ||
4507 (cur->type == XML_HTML_DOCUMENT_NODE))
4508 return(NULL);
4509 return(cur->next);
4510}
4511
4512/**
4513 * xmlXPathNextDescendant:
4514 * @ctxt: the XPath Parser context
4515 * @cur: the current node in the traversal
4516 *
4517 * Traversal function for the "descendant" direction
4518 * the descendant axis contains the descendants of the context node in document
4519 * order; a descendant is a child or a child of a child and so on.
4520 *
4521 * Returns the next element following that axis
4522 */
4523xmlNodePtr
4524xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4525 if (cur == NULL) {
4526 if (ctxt->context->node == NULL)
4527 return(NULL);
4528 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4529 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4530 return(NULL);
4531
4532 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
4533 return(ctxt->context->doc->children);
4534 return(ctxt->context->node->children);
4535 }
4536
Daniel Veillard567e1b42001-08-01 15:53:47 +00004537 if (cur->children != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004538 if (cur->children->type != XML_ENTITY_DECL)
Daniel Veillard567e1b42001-08-01 15:53:47 +00004539 return(cur->children);
4540 }
4541
4542 if (cur == ctxt->context->node) return(NULL);
4543
Owen Taylor3473f882001-02-23 17:55:21 +00004544 if (cur->next != NULL) return(cur->next);
4545
4546 do {
4547 cur = cur->parent;
4548 if (cur == NULL) return(NULL);
4549 if (cur == ctxt->context->node) return(NULL);
4550 if (cur->next != NULL) {
4551 cur = cur->next;
4552 return(cur);
4553 }
4554 } while (cur != NULL);
4555 return(cur);
4556}
4557
4558/**
4559 * xmlXPathNextDescendantOrSelf:
4560 * @ctxt: the XPath Parser context
4561 * @cur: the current node in the traversal
4562 *
4563 * Traversal function for the "descendant-or-self" direction
4564 * the descendant-or-self axis contains the context node and the descendants
4565 * of the context node in document order; thus the context node is the first
4566 * node on the axis, and the first child of the context node is the second node
4567 * on the axis
4568 *
4569 * Returns the next element following that axis
4570 */
4571xmlNodePtr
4572xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4573 if (cur == NULL) {
4574 if (ctxt->context->node == NULL)
4575 return(NULL);
4576 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4577 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4578 return(NULL);
4579 return(ctxt->context->node);
4580 }
4581
4582 return(xmlXPathNextDescendant(ctxt, cur));
4583}
4584
4585/**
4586 * xmlXPathNextParent:
4587 * @ctxt: the XPath Parser context
4588 * @cur: the current node in the traversal
4589 *
4590 * Traversal function for the "parent" direction
4591 * The parent axis contains the parent of the context node, if there is one.
4592 *
4593 * Returns the next element following that axis
4594 */
4595xmlNodePtr
4596xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4597 /*
4598 * the parent of an attribute or namespace node is the element
4599 * to which the attribute or namespace node is attached
4600 * Namespace handling !!!
4601 */
4602 if (cur == NULL) {
4603 if (ctxt->context->node == NULL) return(NULL);
4604 switch (ctxt->context->node->type) {
4605 case XML_ELEMENT_NODE:
4606 case XML_TEXT_NODE:
4607 case XML_CDATA_SECTION_NODE:
4608 case XML_ENTITY_REF_NODE:
4609 case XML_ENTITY_NODE:
4610 case XML_PI_NODE:
4611 case XML_COMMENT_NODE:
4612 case XML_NOTATION_NODE:
4613 case XML_DTD_NODE:
4614 case XML_ELEMENT_DECL:
4615 case XML_ATTRIBUTE_DECL:
4616 case XML_XINCLUDE_START:
4617 case XML_XINCLUDE_END:
4618 case XML_ENTITY_DECL:
4619 if (ctxt->context->node->parent == NULL)
4620 return((xmlNodePtr) ctxt->context->doc);
4621 return(ctxt->context->node->parent);
4622 case XML_ATTRIBUTE_NODE: {
4623 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4624
4625 return(att->parent);
4626 }
4627 case XML_DOCUMENT_NODE:
4628 case XML_DOCUMENT_TYPE_NODE:
4629 case XML_DOCUMENT_FRAG_NODE:
4630 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004631#ifdef LIBXML_DOCB_ENABLED
4632 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004633#endif
4634 return(NULL);
4635 case XML_NAMESPACE_DECL:
4636 /*
4637 * TODO !!! may require extending struct _xmlNs with
4638 * parent field
4639 * C.f. Infoset case...
4640 */
4641 return(NULL);
4642 }
4643 }
4644 return(NULL);
4645}
4646
4647/**
4648 * xmlXPathNextAncestor:
4649 * @ctxt: the XPath Parser context
4650 * @cur: the current node in the traversal
4651 *
4652 * Traversal function for the "ancestor" direction
4653 * the ancestor axis contains the ancestors of the context node; the ancestors
4654 * of the context node consist of the parent of context node and the parent's
4655 * parent and so on; the nodes are ordered in reverse document order; thus the
4656 * parent is the first node on the axis, and the parent's parent is the second
4657 * node on the axis
4658 *
4659 * Returns the next element following that axis
4660 */
4661xmlNodePtr
4662xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4663 /*
4664 * the parent of an attribute or namespace node is the element
4665 * to which the attribute or namespace node is attached
4666 * !!!!!!!!!!!!!
4667 */
4668 if (cur == NULL) {
4669 if (ctxt->context->node == NULL) return(NULL);
4670 switch (ctxt->context->node->type) {
4671 case XML_ELEMENT_NODE:
4672 case XML_TEXT_NODE:
4673 case XML_CDATA_SECTION_NODE:
4674 case XML_ENTITY_REF_NODE:
4675 case XML_ENTITY_NODE:
4676 case XML_PI_NODE:
4677 case XML_COMMENT_NODE:
4678 case XML_DTD_NODE:
4679 case XML_ELEMENT_DECL:
4680 case XML_ATTRIBUTE_DECL:
4681 case XML_ENTITY_DECL:
4682 case XML_NOTATION_NODE:
4683 case XML_XINCLUDE_START:
4684 case XML_XINCLUDE_END:
4685 if (ctxt->context->node->parent == NULL)
4686 return((xmlNodePtr) ctxt->context->doc);
4687 return(ctxt->context->node->parent);
4688 case XML_ATTRIBUTE_NODE: {
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004689 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00004690
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004691 return(tmp->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00004692 }
4693 case XML_DOCUMENT_NODE:
4694 case XML_DOCUMENT_TYPE_NODE:
4695 case XML_DOCUMENT_FRAG_NODE:
4696 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004697#ifdef LIBXML_DOCB_ENABLED
4698 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004699#endif
4700 return(NULL);
4701 case XML_NAMESPACE_DECL:
4702 /*
4703 * TODO !!! may require extending struct _xmlNs with
4704 * parent field
4705 * C.f. Infoset case...
4706 */
4707 return(NULL);
4708 }
4709 return(NULL);
4710 }
4711 if (cur == ctxt->context->doc->children)
4712 return((xmlNodePtr) ctxt->context->doc);
4713 if (cur == (xmlNodePtr) ctxt->context->doc)
4714 return(NULL);
4715 switch (cur->type) {
4716 case XML_ELEMENT_NODE:
4717 case XML_TEXT_NODE:
4718 case XML_CDATA_SECTION_NODE:
4719 case XML_ENTITY_REF_NODE:
4720 case XML_ENTITY_NODE:
4721 case XML_PI_NODE:
4722 case XML_COMMENT_NODE:
4723 case XML_NOTATION_NODE:
4724 case XML_DTD_NODE:
4725 case XML_ELEMENT_DECL:
4726 case XML_ATTRIBUTE_DECL:
4727 case XML_ENTITY_DECL:
4728 case XML_XINCLUDE_START:
4729 case XML_XINCLUDE_END:
4730 return(cur->parent);
4731 case XML_ATTRIBUTE_NODE: {
4732 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
4733
4734 return(att->parent);
4735 }
4736 case XML_DOCUMENT_NODE:
4737 case XML_DOCUMENT_TYPE_NODE:
4738 case XML_DOCUMENT_FRAG_NODE:
4739 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004740#ifdef LIBXML_DOCB_ENABLED
4741 case XML_DOCB_DOCUMENT_NODE:
Owen Taylor3473f882001-02-23 17:55:21 +00004742#endif
4743 return(NULL);
4744 case XML_NAMESPACE_DECL:
4745 /*
4746 * TODO !!! may require extending struct _xmlNs with
4747 * parent field
4748 * C.f. Infoset case...
4749 */
4750 return(NULL);
4751 }
4752 return(NULL);
4753}
4754
4755/**
4756 * xmlXPathNextAncestorOrSelf:
4757 * @ctxt: the XPath Parser context
4758 * @cur: the current node in the traversal
4759 *
4760 * Traversal function for the "ancestor-or-self" direction
4761 * he ancestor-or-self axis contains the context node and ancestors of
4762 * the context node in reverse document order; thus the context node is
4763 * the first node on the axis, and the context node's parent the second;
4764 * parent here is defined the same as with the parent axis.
4765 *
4766 * Returns the next element following that axis
4767 */
4768xmlNodePtr
4769xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4770 if (cur == NULL)
4771 return(ctxt->context->node);
4772 return(xmlXPathNextAncestor(ctxt, cur));
4773}
4774
4775/**
4776 * xmlXPathNextFollowingSibling:
4777 * @ctxt: the XPath Parser context
4778 * @cur: the current node in the traversal
4779 *
4780 * Traversal function for the "following-sibling" direction
4781 * The following-sibling axis contains the following siblings of the context
4782 * node in document order.
4783 *
4784 * Returns the next element following that axis
4785 */
4786xmlNodePtr
4787xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4788 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4789 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4790 return(NULL);
4791 if (cur == (xmlNodePtr) ctxt->context->doc)
4792 return(NULL);
4793 if (cur == NULL)
4794 return(ctxt->context->node->next);
4795 return(cur->next);
4796}
4797
4798/**
4799 * xmlXPathNextPrecedingSibling:
4800 * @ctxt: the XPath Parser context
4801 * @cur: the current node in the traversal
4802 *
4803 * Traversal function for the "preceding-sibling" direction
4804 * The preceding-sibling axis contains the preceding siblings of the context
4805 * node in reverse document order; the first preceding sibling is first on the
4806 * axis; the sibling preceding that node is the second on the axis and so on.
4807 *
4808 * Returns the next element following that axis
4809 */
4810xmlNodePtr
4811xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4812 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
4813 (ctxt->context->node->type == XML_NAMESPACE_DECL))
4814 return(NULL);
4815 if (cur == (xmlNodePtr) ctxt->context->doc)
4816 return(NULL);
4817 if (cur == NULL)
4818 return(ctxt->context->node->prev);
Daniel Veillardf06307e2001-07-03 10:35:50 +00004819 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
4820 cur = cur->prev;
4821 if (cur == NULL)
4822 return(ctxt->context->node->prev);
4823 }
Owen Taylor3473f882001-02-23 17:55:21 +00004824 return(cur->prev);
4825}
4826
4827/**
4828 * xmlXPathNextFollowing:
4829 * @ctxt: the XPath Parser context
4830 * @cur: the current node in the traversal
4831 *
4832 * Traversal function for the "following" direction
4833 * The following axis contains all nodes in the same document as the context
4834 * node that are after the context node in document order, excluding any
4835 * descendants and excluding attribute nodes and namespace nodes; the nodes
4836 * are ordered in document order
4837 *
4838 * Returns the next element following that axis
4839 */
4840xmlNodePtr
4841xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
4842 if (cur != NULL && cur->children != NULL)
4843 return cur->children ;
4844 if (cur == NULL) cur = ctxt->context->node;
4845 if (cur == NULL) return(NULL) ; /* ERROR */
4846 if (cur->next != NULL) return(cur->next) ;
4847 do {
4848 cur = cur->parent;
4849 if (cur == NULL) return(NULL);
4850 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
4851 if (cur->next != NULL) return(cur->next);
4852 } while (cur != NULL);
4853 return(cur);
4854}
4855
4856/*
4857 * xmlXPathIsAncestor:
4858 * @ancestor: the ancestor node
4859 * @node: the current node
4860 *
4861 * Check that @ancestor is a @node's ancestor
4862 *
4863 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
4864 */
4865static int
4866xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
4867 if ((ancestor == NULL) || (node == NULL)) return(0);
4868 /* nodes need to be in the same document */
4869 if (ancestor->doc != node->doc) return(0);
4870 /* avoid searching if ancestor or node is the root node */
4871 if (ancestor == (xmlNodePtr) node->doc) return(1);
4872 if (node == (xmlNodePtr) ancestor->doc) return(0);
4873 while (node->parent != NULL) {
4874 if (node->parent == ancestor)
4875 return(1);
4876 node = node->parent;
4877 }
4878 return(0);
4879}
4880
4881/**
4882 * xmlXPathNextPreceding:
4883 * @ctxt: the XPath Parser context
4884 * @cur: the current node in the traversal
4885 *
4886 * Traversal function for the "preceding" direction
4887 * the preceding axis contains all nodes in the same document as the context
4888 * node that are before the context node in document order, excluding any
4889 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4890 * ordered in reverse document order
4891 *
4892 * Returns the next element following that axis
4893 */
4894xmlNodePtr
Daniel Veillardf06307e2001-07-03 10:35:50 +00004895xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
4896{
Owen Taylor3473f882001-02-23 17:55:21 +00004897 if (cur == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00004898 cur = ctxt->context->node;
4899 if (cur == NULL)
4900 return (NULL);
4901 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4902 cur = cur->prev;
Owen Taylor3473f882001-02-23 17:55:21 +00004903 do {
4904 if (cur->prev != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00004905 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
4906 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004907 }
4908
4909 cur = cur->parent;
Daniel Veillardf06307e2001-07-03 10:35:50 +00004910 if (cur == NULL)
4911 return (NULL);
4912 if (cur == ctxt->context->doc->children)
4913 return (NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004914 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
Daniel Veillardf06307e2001-07-03 10:35:50 +00004915 return (cur);
4916}
4917
4918/**
4919 * xmlXPathNextPrecedingInternal:
4920 * @ctxt: the XPath Parser context
4921 * @cur: the current node in the traversal
4922 *
4923 * Traversal function for the "preceding" direction
4924 * the preceding axis contains all nodes in the same document as the context
4925 * node that are before the context node in document order, excluding any
4926 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
4927 * ordered in reverse document order
4928 * This is a faster implementation but internal only since it requires a
4929 * state kept in the parser context: ctxt->ancestor.
4930 *
4931 * Returns the next element following that axis
4932 */
4933static xmlNodePtr
4934xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
4935 xmlNodePtr cur)
4936{
4937 if (cur == NULL) {
4938 cur = ctxt->context->node;
4939 if (cur == NULL)
4940 return (NULL);
4941 ctxt->ancestor = cur->parent;
4942 }
4943 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
4944 cur = cur->prev;
4945 while (cur->prev == NULL) {
4946 cur = cur->parent;
4947 if (cur == NULL)
4948 return (NULL);
4949 if (cur == ctxt->context->doc->children)
4950 return (NULL);
4951 if (cur != ctxt->ancestor)
4952 return (cur);
4953 ctxt->ancestor = cur->parent;
4954 }
4955 cur = cur->prev;
4956 while (cur->last != NULL)
4957 cur = cur->last;
4958 return (cur);
Owen Taylor3473f882001-02-23 17:55:21 +00004959}
4960
4961/**
4962 * xmlXPathNextNamespace:
4963 * @ctxt: the XPath Parser context
4964 * @cur: the current attribute in the traversal
4965 *
4966 * Traversal function for the "namespace" direction
4967 * the namespace axis contains the namespace nodes of the context node;
4968 * the order of nodes on this axis is implementation-defined; the axis will
4969 * be empty unless the context node is an element
4970 *
4971 * Returns the next element following that axis
4972 */
4973xmlNodePtr
4974xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004975 xmlNodePtr ret;
4976
Owen Taylor3473f882001-02-23 17:55:21 +00004977 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004978 if ((cur == NULL) || (ctxt->context->tmpNsList == NULL)) {
4979 if (ctxt->context->tmpNsList != NULL)
4980 xmlFree(ctxt->context->tmpNsList);
4981 ctxt->context->tmpNsList =
Owen Taylor3473f882001-02-23 17:55:21 +00004982 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004983 if (ctxt->context->tmpNsList == NULL) return(NULL);
4984 ctxt->context->tmpNsNr = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004985 }
Daniel Veillard7d7e3792001-07-30 13:42:13 +00004986 ret = (xmlNodePtr)ctxt->context->tmpNsList[ctxt->context->tmpNsNr++];
4987 if (ret == NULL) {
4988 xmlFree(ctxt->context->tmpNsList);
4989 ctxt->context->tmpNsList = NULL;
4990 }
4991 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00004992}
4993
4994/**
4995 * xmlXPathNextAttribute:
4996 * @ctxt: the XPath Parser context
4997 * @cur: the current attribute in the traversal
4998 *
4999 * Traversal function for the "attribute" direction
5000 * TODO: support DTD inherited default attributes
5001 *
5002 * Returns the next element following that axis
5003 */
5004xmlNodePtr
5005xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillarde470df72001-04-18 21:41:07 +00005006 if (ctxt->context->node == NULL)
5007 return(NULL);
5008 if (ctxt->context->node->type != XML_ELEMENT_NODE)
5009 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005010 if (cur == NULL) {
5011 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
5012 return(NULL);
5013 return((xmlNodePtr)ctxt->context->node->properties);
5014 }
5015 return((xmlNodePtr)cur->next);
5016}
5017
5018/************************************************************************
5019 * *
5020 * NodeTest Functions *
5021 * *
5022 ************************************************************************/
5023
Owen Taylor3473f882001-02-23 17:55:21 +00005024#define IS_FUNCTION 200
5025
Owen Taylor3473f882001-02-23 17:55:21 +00005026
5027/************************************************************************
5028 * *
5029 * Implicit tree core function library *
5030 * *
5031 ************************************************************************/
5032
5033/**
5034 * xmlXPathRoot:
5035 * @ctxt: the XPath Parser context
5036 *
5037 * Initialize the context to the root of the document
5038 */
5039void
5040xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
5041 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
5042 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5043}
5044
5045/************************************************************************
5046 * *
5047 * The explicit core function library *
5048 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
5049 * *
5050 ************************************************************************/
5051
5052
5053/**
5054 * xmlXPathLastFunction:
5055 * @ctxt: the XPath Parser context
5056 * @nargs: the number of arguments
5057 *
5058 * Implement the last() XPath function
5059 * number last()
5060 * The last function returns the number of nodes in the context node list.
5061 */
5062void
5063xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5064 CHECK_ARITY(0);
5065 if (ctxt->context->contextSize >= 0) {
5066 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
5067#ifdef DEBUG_EXPR
5068 xmlGenericError(xmlGenericErrorContext,
5069 "last() : %d\n", ctxt->context->contextSize);
5070#endif
5071 } else {
5072 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
5073 }
5074}
5075
5076/**
5077 * xmlXPathPositionFunction:
5078 * @ctxt: the XPath Parser context
5079 * @nargs: the number of arguments
5080 *
5081 * Implement the position() XPath function
5082 * number position()
5083 * The position function returns the position of the context node in the
5084 * context node list. The first position is 1, and so the last positionr
5085 * will be equal to last().
5086 */
5087void
5088xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5089 CHECK_ARITY(0);
5090 if (ctxt->context->proximityPosition >= 0) {
5091 valuePush(ctxt,
5092 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
5093#ifdef DEBUG_EXPR
5094 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
5095 ctxt->context->proximityPosition);
5096#endif
5097 } else {
5098 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
5099 }
5100}
5101
5102/**
5103 * xmlXPathCountFunction:
5104 * @ctxt: the XPath Parser context
5105 * @nargs: the number of arguments
5106 *
5107 * Implement the count() XPath function
5108 * number count(node-set)
5109 */
5110void
5111xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5112 xmlXPathObjectPtr cur;
5113
5114 CHECK_ARITY(1);
5115 if ((ctxt->value == NULL) ||
5116 ((ctxt->value->type != XPATH_NODESET) &&
5117 (ctxt->value->type != XPATH_XSLT_TREE)))
5118 XP_ERROR(XPATH_INVALID_TYPE);
5119 cur = valuePop(ctxt);
5120
Daniel Veillard911f49a2001-04-07 15:39:35 +00005121 if ((cur == NULL) || (cur->nodesetval == NULL))
5122 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillardfe703322001-08-14 12:18:09 +00005123 else if (cur->type == XPATH_NODESET) {
Daniel Veillard911f49a2001-04-07 15:39:35 +00005124 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillardfe703322001-08-14 12:18:09 +00005125 } else {
5126 if ((cur->nodesetval->nodeNr != 1) ||
5127 (cur->nodesetval->nodeTab == NULL)) {
5128 valuePush(ctxt, xmlXPathNewFloat((double) 0));
5129 } else {
5130 xmlNodePtr tmp;
5131 int i = 0;
5132
5133 tmp = cur->nodesetval->nodeTab[0];
5134 if (tmp != NULL) {
5135 tmp = tmp->children;
5136 while (tmp != NULL) {
5137 tmp = tmp->next;
5138 i++;
5139 }
5140 }
5141 valuePush(ctxt, xmlXPathNewFloat((double) i));
5142 }
5143 }
Owen Taylor3473f882001-02-23 17:55:21 +00005144 xmlXPathFreeObject(cur);
5145}
5146
5147/**
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005148 * xmlXPathGetElementsByIds:
5149 * @doc: the document
5150 * @ids: a whitespace separated list of IDs
5151 *
5152 * Selects elements by their unique ID.
5153 *
5154 * Returns a node-set of selected elements.
5155 */
5156static xmlNodeSetPtr
5157xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
5158 xmlNodeSetPtr ret;
5159 const xmlChar *cur = ids;
5160 xmlChar *ID;
5161 xmlAttrPtr attr;
5162 xmlNodePtr elem = NULL;
5163
5164 ret = xmlXPathNodeSetCreate(NULL);
5165
5166 while (IS_BLANK(*cur)) cur++;
5167 while (*cur != 0) {
5168 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
5169 (*cur == '.') || (*cur == '-') ||
5170 (*cur == '_') || (*cur == ':') ||
5171 (IS_COMBINING(*cur)) ||
5172 (IS_EXTENDER(*cur)))
5173 cur++;
5174
5175 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
5176
5177 ID = xmlStrndup(ids, cur - ids);
5178 attr = xmlGetID(doc, ID);
5179 if (attr != NULL) {
5180 elem = attr->parent;
5181 xmlXPathNodeSetAdd(ret, elem);
5182 }
5183 if (ID != NULL)
5184 xmlFree(ID);
5185
5186 while (IS_BLANK(*cur)) cur++;
5187 ids = cur;
5188 }
5189 return(ret);
5190}
5191
5192/**
Owen Taylor3473f882001-02-23 17:55:21 +00005193 * xmlXPathIdFunction:
5194 * @ctxt: the XPath Parser context
5195 * @nargs: the number of arguments
5196 *
5197 * Implement the id() XPath function
5198 * node-set id(object)
5199 * The id function selects elements by their unique ID
5200 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
5201 * then the result is the union of the result of applying id to the
5202 * string value of each of the nodes in the argument node-set. When the
5203 * argument to id is of any other type, the argument is converted to a
5204 * string as if by a call to the string function; the string is split
5205 * into a whitespace-separated list of tokens (whitespace is any sequence
5206 * of characters matching the production S); the result is a node-set
5207 * containing the elements in the same document as the context node that
5208 * have a unique ID equal to any of the tokens in the list.
5209 */
5210void
5211xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005212 xmlChar *tokens;
5213 xmlNodeSetPtr ret;
5214 xmlXPathObjectPtr obj;
Owen Taylor3473f882001-02-23 17:55:21 +00005215
5216 CHECK_ARITY(1);
5217 obj = valuePop(ctxt);
5218 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
5219 if (obj->type == XPATH_NODESET) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005220 xmlNodeSetPtr ns;
Owen Taylor3473f882001-02-23 17:55:21 +00005221 int i;
5222
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005223 ret = xmlXPathNodeSetCreate(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005224
Daniel Veillard911f49a2001-04-07 15:39:35 +00005225 if (obj->nodesetval != NULL) {
5226 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005227 tokens =
5228 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
5229 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
5230 ret = xmlXPathNodeSetMerge(ret, ns);
5231 xmlXPathFreeNodeSet(ns);
5232 if (tokens != NULL)
5233 xmlFree(tokens);
Daniel Veillard911f49a2001-04-07 15:39:35 +00005234 }
Owen Taylor3473f882001-02-23 17:55:21 +00005235 }
5236
5237 xmlXPathFreeObject(obj);
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005238 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005239 return;
5240 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005241 obj = xmlXPathConvertString(obj);
Owen Taylor3473f882001-02-23 17:55:21 +00005242
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005243 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
5244 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Owen Taylor3473f882001-02-23 17:55:21 +00005245
Owen Taylor3473f882001-02-23 17:55:21 +00005246 xmlXPathFreeObject(obj);
5247 return;
5248}
5249
5250/**
5251 * xmlXPathLocalNameFunction:
5252 * @ctxt: the XPath Parser context
5253 * @nargs: the number of arguments
5254 *
5255 * Implement the local-name() XPath function
5256 * string local-name(node-set?)
5257 * The local-name function returns a string containing the local part
5258 * of the name of the node in the argument node-set that is first in
5259 * document order. If the node-set is empty or the first node has no
5260 * name, an empty string is returned. If the argument is omitted it
5261 * defaults to the context node.
5262 */
5263void
5264xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5265 xmlXPathObjectPtr cur;
5266
5267 if (nargs == 0) {
5268 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5269 nargs = 1;
5270 }
5271
5272 CHECK_ARITY(1);
5273 if ((ctxt->value == NULL) ||
5274 ((ctxt->value->type != XPATH_NODESET) &&
5275 (ctxt->value->type != XPATH_XSLT_TREE)))
5276 XP_ERROR(XPATH_INVALID_TYPE);
5277 cur = valuePop(ctxt);
5278
Daniel Veillard911f49a2001-04-07 15:39:35 +00005279 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005280 valuePush(ctxt, xmlXPathNewCString(""));
5281 } else {
5282 int i = 0; /* Should be first in document order !!!!! */
5283 switch (cur->nodesetval->nodeTab[i]->type) {
5284 case XML_ELEMENT_NODE:
5285 case XML_ATTRIBUTE_NODE:
5286 case XML_PI_NODE:
5287 valuePush(ctxt,
5288 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
5289 break;
5290 case XML_NAMESPACE_DECL:
5291 valuePush(ctxt, xmlXPathNewString(
5292 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
5293 break;
5294 default:
5295 valuePush(ctxt, xmlXPathNewCString(""));
5296 }
5297 }
5298 xmlXPathFreeObject(cur);
5299}
5300
5301/**
5302 * xmlXPathNamespaceURIFunction:
5303 * @ctxt: the XPath Parser context
5304 * @nargs: the number of arguments
5305 *
5306 * Implement the namespace-uri() XPath function
5307 * string namespace-uri(node-set?)
5308 * The namespace-uri function returns a string containing the
5309 * namespace URI of the expanded name of the node in the argument
5310 * node-set that is first in document order. If the node-set is empty,
5311 * the first node has no name, or the expanded name has no namespace
5312 * URI, an empty string is returned. If the argument is omitted it
5313 * defaults to the context node.
5314 */
5315void
5316xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5317 xmlXPathObjectPtr cur;
5318
5319 if (nargs == 0) {
5320 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5321 nargs = 1;
5322 }
5323 CHECK_ARITY(1);
5324 if ((ctxt->value == NULL) ||
5325 ((ctxt->value->type != XPATH_NODESET) &&
5326 (ctxt->value->type != XPATH_XSLT_TREE)))
5327 XP_ERROR(XPATH_INVALID_TYPE);
5328 cur = valuePop(ctxt);
5329
Daniel Veillard911f49a2001-04-07 15:39:35 +00005330 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005331 valuePush(ctxt, xmlXPathNewCString(""));
5332 } else {
5333 int i = 0; /* Should be first in document order !!!!! */
5334 switch (cur->nodesetval->nodeTab[i]->type) {
5335 case XML_ELEMENT_NODE:
5336 case XML_ATTRIBUTE_NODE:
5337 if (cur->nodesetval->nodeTab[i]->ns == NULL)
5338 valuePush(ctxt, xmlXPathNewCString(""));
5339 else
5340 valuePush(ctxt, xmlXPathNewString(
5341 cur->nodesetval->nodeTab[i]->ns->href));
5342 break;
5343 default:
5344 valuePush(ctxt, xmlXPathNewCString(""));
5345 }
5346 }
5347 xmlXPathFreeObject(cur);
5348}
5349
5350/**
5351 * xmlXPathNameFunction:
5352 * @ctxt: the XPath Parser context
5353 * @nargs: the number of arguments
5354 *
5355 * Implement the name() XPath function
5356 * string name(node-set?)
5357 * The name function returns a string containing a QName representing
5358 * the name of the node in the argument node-set that is first in documenti
5359 * order. The QName must represent the name with respect to the namespace
5360 * declarations in effect on the node whose name is being represented.
5361 * Typically, this will be the form in which the name occurred in the XML
5362 * source. This need not be the case if there are namespace declarations
5363 * in effect on the node that associate multiple prefixes with the same
5364 * namespace. However, an implementation may include information about
5365 * the original prefix in its representation of nodes; in this case, an
5366 * implementation can ensure that the returned string is always the same
5367 * as the QName used in the XML source. If the argument it omitted it
5368 * defaults to the context node.
5369 * Libxml keep the original prefix so the "real qualified name" used is
5370 * returned.
5371 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005372static void
Daniel Veillard04383752001-07-08 14:27:15 +00005373xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
5374{
Owen Taylor3473f882001-02-23 17:55:21 +00005375 xmlXPathObjectPtr cur;
5376
5377 if (nargs == 0) {
Daniel Veillard04383752001-07-08 14:27:15 +00005378 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5379 nargs = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005380 }
5381
5382 CHECK_ARITY(1);
Daniel Veillard04383752001-07-08 14:27:15 +00005383 if ((ctxt->value == NULL) ||
5384 ((ctxt->value->type != XPATH_NODESET) &&
5385 (ctxt->value->type != XPATH_XSLT_TREE)))
5386 XP_ERROR(XPATH_INVALID_TYPE);
Owen Taylor3473f882001-02-23 17:55:21 +00005387 cur = valuePop(ctxt);
5388
Daniel Veillard911f49a2001-04-07 15:39:35 +00005389 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Daniel Veillard04383752001-07-08 14:27:15 +00005390 valuePush(ctxt, xmlXPathNewCString(""));
Owen Taylor3473f882001-02-23 17:55:21 +00005391 } else {
Daniel Veillard04383752001-07-08 14:27:15 +00005392 int i = 0; /* Should be first in document order !!!!! */
Owen Taylor3473f882001-02-23 17:55:21 +00005393
Daniel Veillard04383752001-07-08 14:27:15 +00005394 switch (cur->nodesetval->nodeTab[i]->type) {
5395 case XML_ELEMENT_NODE:
5396 case XML_ATTRIBUTE_NODE:
5397 if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
5398 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL))
5399 valuePush(ctxt,
5400 xmlXPathNewString(cur->nodesetval->
5401 nodeTab[i]->name));
5402
5403 else {
5404 char name[2000];
5405
5406 snprintf(name, sizeof(name), "%s:%s",
5407 (char *) cur->nodesetval->nodeTab[i]->ns->
5408 prefix,
5409 (char *) cur->nodesetval->nodeTab[i]->name);
5410 name[sizeof(name) - 1] = 0;
5411 valuePush(ctxt, xmlXPathNewCString(name));
5412 }
5413 break;
5414 default:
5415 valuePush(ctxt,
5416 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
5417 xmlXPathLocalNameFunction(ctxt, 1);
5418 }
Owen Taylor3473f882001-02-23 17:55:21 +00005419 }
5420 xmlXPathFreeObject(cur);
5421}
5422
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005423
5424/**
Owen Taylor3473f882001-02-23 17:55:21 +00005425 * xmlXPathStringFunction:
5426 * @ctxt: the XPath Parser context
5427 * @nargs: the number of arguments
5428 *
5429 * Implement the string() XPath function
5430 * string string(object?)
5431 * he string function converts an object to a string as follows:
5432 * - A node-set is converted to a string by returning the value of
5433 * the node in the node-set that is first in document order.
5434 * If the node-set is empty, an empty string is returned.
5435 * - A number is converted to a string as follows
5436 * + NaN is converted to the string NaN
5437 * + positive zero is converted to the string 0
5438 * + negative zero is converted to the string 0
5439 * + positive infinity is converted to the string Infinity
5440 * + negative infinity is converted to the string -Infinity
5441 * + if the number is an integer, the number is represented in
5442 * decimal form as a Number with no decimal point and no leading
5443 * zeros, preceded by a minus sign (-) if the number is negative
5444 * + otherwise, the number is represented in decimal form as a
5445 * Number including a decimal point with at least one digit
5446 * before the decimal point and at least one digit after the
5447 * decimal point, preceded by a minus sign (-) if the number
5448 * is negative; there must be no leading zeros before the decimal
5449 * point apart possibly from the one required digit immediatelyi
5450 * before the decimal point; beyond the one required digit
5451 * after the decimal point there must be as many, but only as
5452 * many, more digits as are needed to uniquely distinguish the
5453 * number from all other IEEE 754 numeric values.
5454 * - The boolean false value is converted to the string false.
5455 * The boolean true value is converted to the string true.
5456 *
5457 * If the argument is omitted, it defaults to a node-set with the
5458 * context node as its only member.
5459 */
5460void
5461xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5462 xmlXPathObjectPtr cur;
5463
5464 if (nargs == 0) {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005465 valuePush(ctxt,
5466 xmlXPathWrapString(
5467 xmlXPathCastNodeToString(ctxt->context->node)));
5468 return;
Owen Taylor3473f882001-02-23 17:55:21 +00005469 }
5470
5471 CHECK_ARITY(1);
5472 cur = valuePop(ctxt);
5473 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005474 cur = xmlXPathConvertString(cur);
5475 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005476}
5477
5478/**
5479 * xmlXPathStringLengthFunction:
5480 * @ctxt: the XPath Parser context
5481 * @nargs: the number of arguments
5482 *
5483 * Implement the string-length() XPath function
5484 * number string-length(string?)
5485 * The string-length returns the number of characters in the string
5486 * (see [3.6 Strings]). If the argument is omitted, it defaults to
5487 * the context node converted to a string, in other words the value
5488 * of the context node.
5489 */
5490void
5491xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5492 xmlXPathObjectPtr cur;
5493
5494 if (nargs == 0) {
5495 if (ctxt->context->node == NULL) {
5496 valuePush(ctxt, xmlXPathNewFloat(0));
5497 } else {
5498 xmlChar *content;
5499
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005500 content = xmlXPathCastNodeToString(ctxt->context->node);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005501 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
Owen Taylor3473f882001-02-23 17:55:21 +00005502 xmlFree(content);
5503 }
5504 return;
5505 }
5506 CHECK_ARITY(1);
5507 CAST_TO_STRING;
5508 CHECK_TYPE(XPATH_STRING);
5509 cur = valuePop(ctxt);
Daniel Veillarde043ee12001-04-16 14:08:07 +00005510 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
Owen Taylor3473f882001-02-23 17:55:21 +00005511 xmlXPathFreeObject(cur);
5512}
5513
5514/**
5515 * xmlXPathConcatFunction:
5516 * @ctxt: the XPath Parser context
5517 * @nargs: the number of arguments
5518 *
5519 * Implement the concat() XPath function
5520 * string concat(string, string, string*)
5521 * The concat function returns the concatenation of its arguments.
5522 */
5523void
5524xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5525 xmlXPathObjectPtr cur, newobj;
5526 xmlChar *tmp;
5527
5528 if (nargs < 2) {
5529 CHECK_ARITY(2);
5530 }
5531
5532 CAST_TO_STRING;
5533 cur = valuePop(ctxt);
5534 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
5535 xmlXPathFreeObject(cur);
5536 return;
5537 }
5538 nargs--;
5539
5540 while (nargs > 0) {
5541 CAST_TO_STRING;
5542 newobj = valuePop(ctxt);
5543 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
5544 xmlXPathFreeObject(newobj);
5545 xmlXPathFreeObject(cur);
5546 XP_ERROR(XPATH_INVALID_TYPE);
5547 }
5548 tmp = xmlStrcat(newobj->stringval, cur->stringval);
5549 newobj->stringval = cur->stringval;
5550 cur->stringval = tmp;
5551
5552 xmlXPathFreeObject(newobj);
5553 nargs--;
5554 }
5555 valuePush(ctxt, cur);
5556}
5557
5558/**
5559 * xmlXPathContainsFunction:
5560 * @ctxt: the XPath Parser context
5561 * @nargs: the number of arguments
5562 *
5563 * Implement the contains() XPath function
5564 * boolean contains(string, string)
5565 * The contains function returns true if the first argument string
5566 * contains the second argument string, and otherwise returns false.
5567 */
5568void
5569xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5570 xmlXPathObjectPtr hay, needle;
5571
5572 CHECK_ARITY(2);
5573 CAST_TO_STRING;
5574 CHECK_TYPE(XPATH_STRING);
5575 needle = valuePop(ctxt);
5576 CAST_TO_STRING;
5577 hay = valuePop(ctxt);
5578 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5579 xmlXPathFreeObject(hay);
5580 xmlXPathFreeObject(needle);
5581 XP_ERROR(XPATH_INVALID_TYPE);
5582 }
5583 if (xmlStrstr(hay->stringval, needle->stringval))
5584 valuePush(ctxt, xmlXPathNewBoolean(1));
5585 else
5586 valuePush(ctxt, xmlXPathNewBoolean(0));
5587 xmlXPathFreeObject(hay);
5588 xmlXPathFreeObject(needle);
5589}
5590
5591/**
5592 * xmlXPathStartsWithFunction:
5593 * @ctxt: the XPath Parser context
5594 * @nargs: the number of arguments
5595 *
5596 * Implement the starts-with() XPath function
5597 * boolean starts-with(string, string)
5598 * The starts-with function returns true if the first argument string
5599 * starts with the second argument string, and otherwise returns false.
5600 */
5601void
5602xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5603 xmlXPathObjectPtr hay, needle;
5604 int n;
5605
5606 CHECK_ARITY(2);
5607 CAST_TO_STRING;
5608 CHECK_TYPE(XPATH_STRING);
5609 needle = valuePop(ctxt);
5610 CAST_TO_STRING;
5611 hay = valuePop(ctxt);
5612 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
5613 xmlXPathFreeObject(hay);
5614 xmlXPathFreeObject(needle);
5615 XP_ERROR(XPATH_INVALID_TYPE);
5616 }
5617 n = xmlStrlen(needle->stringval);
5618 if (xmlStrncmp(hay->stringval, needle->stringval, n))
5619 valuePush(ctxt, xmlXPathNewBoolean(0));
5620 else
5621 valuePush(ctxt, xmlXPathNewBoolean(1));
5622 xmlXPathFreeObject(hay);
5623 xmlXPathFreeObject(needle);
5624}
5625
5626/**
5627 * xmlXPathSubstringFunction:
5628 * @ctxt: the XPath Parser context
5629 * @nargs: the number of arguments
5630 *
5631 * Implement the substring() XPath function
5632 * string substring(string, number, number?)
5633 * The substring function returns the substring of the first argument
5634 * starting at the position specified in the second argument with
5635 * length specified in the third argument. For example,
5636 * substring("12345",2,3) returns "234". If the third argument is not
5637 * specified, it returns the substring starting at the position specified
5638 * in the second argument and continuing to the end of the string. For
5639 * example, substring("12345",2) returns "2345". More precisely, each
5640 * character in the string (see [3.6 Strings]) is considered to have a
5641 * numeric position: the position of the first character is 1, the position
5642 * of the second character is 2 and so on. The returned substring contains
5643 * those characters for which the position of the character is greater than
5644 * or equal to the second argument and, if the third argument is specified,
5645 * less than the sum of the second and third arguments; the comparisons
5646 * and addition used for the above follow the standard IEEE 754 rules. Thus:
5647 * - substring("12345", 1.5, 2.6) returns "234"
5648 * - substring("12345", 0, 3) returns "12"
5649 * - substring("12345", 0 div 0, 3) returns ""
5650 * - substring("12345", 1, 0 div 0) returns ""
5651 * - substring("12345", -42, 1 div 0) returns "12345"
5652 * - substring("12345", -1 div 0, 1 div 0) returns ""
5653 */
5654void
5655xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5656 xmlXPathObjectPtr str, start, len;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005657 double le=0, in;
5658 int i, l, m;
Owen Taylor3473f882001-02-23 17:55:21 +00005659 xmlChar *ret;
5660
Owen Taylor3473f882001-02-23 17:55:21 +00005661 if (nargs < 2) {
5662 CHECK_ARITY(2);
5663 }
5664 if (nargs > 3) {
5665 CHECK_ARITY(3);
5666 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005667 /*
5668 * take care of possible last (position) argument
5669 */
Owen Taylor3473f882001-02-23 17:55:21 +00005670 if (nargs == 3) {
5671 CAST_TO_NUMBER;
5672 CHECK_TYPE(XPATH_NUMBER);
5673 len = valuePop(ctxt);
5674 le = len->floatval;
5675 xmlXPathFreeObject(len);
Owen Taylor3473f882001-02-23 17:55:21 +00005676 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005677
Owen Taylor3473f882001-02-23 17:55:21 +00005678 CAST_TO_NUMBER;
5679 CHECK_TYPE(XPATH_NUMBER);
5680 start = valuePop(ctxt);
5681 in = start->floatval;
5682 xmlXPathFreeObject(start);
5683 CAST_TO_STRING;
5684 CHECK_TYPE(XPATH_STRING);
5685 str = valuePop(ctxt);
Daniel Veillard97ac1312001-05-30 19:14:17 +00005686 m = xmlUTF8Strlen((const unsigned char *)str->stringval);
Owen Taylor3473f882001-02-23 17:55:21 +00005687
Daniel Veillard97ac1312001-05-30 19:14:17 +00005688 /*
5689 * If last pos not present, calculate last position
5690 */
5691 if (nargs != 3)
5692 le = m;
5693
5694 /*
5695 * To meet our requirements, initial index calculations
5696 * must be done before we convert to integer format
5697 *
5698 * First we normalize indices
5699 */
5700 in -= 1.0;
5701 le += in;
5702 if (in < 0.0)
5703 in = 0.0;
5704 if (le > (double)m)
5705 le = (double)m;
5706
5707 /*
5708 * Now we go to integer form, rounding up
5709 */
Owen Taylor3473f882001-02-23 17:55:21 +00005710 i = (int) in;
5711 if (((double)i) != in) i++;
5712
Owen Taylor3473f882001-02-23 17:55:21 +00005713 l = (int) le;
5714 if (((double)l) != le) l++;
5715
Daniel Veillard97ac1312001-05-30 19:14:17 +00005716 if (l > m) l=m;
Owen Taylor3473f882001-02-23 17:55:21 +00005717
5718 /* number of chars to copy */
5719 l -= i;
5720
Daniel Veillard97ac1312001-05-30 19:14:17 +00005721 ret = xmlUTF8Strsub(str->stringval, i, l);
Owen Taylor3473f882001-02-23 17:55:21 +00005722 if (ret == NULL)
5723 valuePush(ctxt, xmlXPathNewCString(""));
5724 else {
5725 valuePush(ctxt, xmlXPathNewString(ret));
5726 xmlFree(ret);
5727 }
Daniel Veillard97ac1312001-05-30 19:14:17 +00005728
Owen Taylor3473f882001-02-23 17:55:21 +00005729 xmlXPathFreeObject(str);
5730}
5731
5732/**
5733 * xmlXPathSubstringBeforeFunction:
5734 * @ctxt: the XPath Parser context
5735 * @nargs: the number of arguments
5736 *
5737 * Implement the substring-before() XPath function
5738 * string substring-before(string, string)
5739 * The substring-before function returns the substring of the first
5740 * argument string that precedes the first occurrence of the second
5741 * argument string in the first argument string, or the empty string
5742 * if the first argument string does not contain the second argument
5743 * string. For example, substring-before("1999/04/01","/") returns 1999.
5744 */
5745void
5746xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5747 xmlXPathObjectPtr str;
5748 xmlXPathObjectPtr find;
5749 xmlBufferPtr target;
5750 const xmlChar *point;
5751 int offset;
5752
5753 CHECK_ARITY(2);
5754 CAST_TO_STRING;
5755 find = valuePop(ctxt);
5756 CAST_TO_STRING;
5757 str = valuePop(ctxt);
5758
5759 target = xmlBufferCreate();
5760 if (target) {
5761 point = xmlStrstr(str->stringval, find->stringval);
5762 if (point) {
5763 offset = (int)(point - str->stringval);
5764 xmlBufferAdd(target, str->stringval, offset);
5765 }
5766 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5767 xmlBufferFree(target);
5768 }
5769
5770 xmlXPathFreeObject(str);
5771 xmlXPathFreeObject(find);
5772}
5773
5774/**
5775 * xmlXPathSubstringAfterFunction:
5776 * @ctxt: the XPath Parser context
5777 * @nargs: the number of arguments
5778 *
5779 * Implement the substring-after() XPath function
5780 * string substring-after(string, string)
5781 * The substring-after function returns the substring of the first
5782 * argument string that follows the first occurrence of the second
5783 * argument string in the first argument string, or the empty stringi
5784 * if the first argument string does not contain the second argument
5785 * string. For example, substring-after("1999/04/01","/") returns 04/01,
5786 * and substring-after("1999/04/01","19") returns 99/04/01.
5787 */
5788void
5789xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5790 xmlXPathObjectPtr str;
5791 xmlXPathObjectPtr find;
5792 xmlBufferPtr target;
5793 const xmlChar *point;
5794 int offset;
5795
5796 CHECK_ARITY(2);
5797 CAST_TO_STRING;
5798 find = valuePop(ctxt);
5799 CAST_TO_STRING;
5800 str = valuePop(ctxt);
5801
5802 target = xmlBufferCreate();
5803 if (target) {
5804 point = xmlStrstr(str->stringval, find->stringval);
5805 if (point) {
5806 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
5807 xmlBufferAdd(target, &str->stringval[offset],
5808 xmlStrlen(str->stringval) - offset);
5809 }
5810 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5811 xmlBufferFree(target);
5812 }
5813
5814 xmlXPathFreeObject(str);
5815 xmlXPathFreeObject(find);
5816}
5817
5818/**
5819 * xmlXPathNormalizeFunction:
5820 * @ctxt: the XPath Parser context
5821 * @nargs: the number of arguments
5822 *
5823 * Implement the normalize-space() XPath function
5824 * string normalize-space(string?)
5825 * The normalize-space function returns the argument string with white
5826 * space normalized by stripping leading and trailing whitespace
5827 * and replacing sequences of whitespace characters by a single
5828 * space. Whitespace characters are the same allowed by the S production
5829 * in XML. If the argument is omitted, it defaults to the context
5830 * node converted to a string, in other words the value of the context node.
5831 */
5832void
5833xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5834 xmlXPathObjectPtr obj = NULL;
5835 xmlChar *source = NULL;
5836 xmlBufferPtr target;
5837 xmlChar blank;
5838
5839 if (nargs == 0) {
5840 /* Use current context node */
Daniel Veillardba0b8c92001-05-15 09:43:47 +00005841 valuePush(ctxt,
5842 xmlXPathWrapString(
5843 xmlXPathCastNodeToString(ctxt->context->node)));
Owen Taylor3473f882001-02-23 17:55:21 +00005844 nargs = 1;
5845 }
5846
5847 CHECK_ARITY(1);
5848 CAST_TO_STRING;
5849 CHECK_TYPE(XPATH_STRING);
5850 obj = valuePop(ctxt);
5851 source = obj->stringval;
5852
5853 target = xmlBufferCreate();
5854 if (target && source) {
5855
5856 /* Skip leading whitespaces */
5857 while (IS_BLANK(*source))
5858 source++;
5859
5860 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
5861 blank = 0;
5862 while (*source) {
5863 if (IS_BLANK(*source)) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005864 blank = 0x20;
Owen Taylor3473f882001-02-23 17:55:21 +00005865 } else {
5866 if (blank) {
5867 xmlBufferAdd(target, &blank, 1);
5868 blank = 0;
5869 }
5870 xmlBufferAdd(target, source, 1);
5871 }
5872 source++;
5873 }
5874
5875 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5876 xmlBufferFree(target);
5877 }
5878 xmlXPathFreeObject(obj);
5879}
5880
5881/**
5882 * xmlXPathTranslateFunction:
5883 * @ctxt: the XPath Parser context
5884 * @nargs: the number of arguments
5885 *
5886 * Implement the translate() XPath function
5887 * string translate(string, string, string)
5888 * The translate function returns the first argument string with
5889 * occurrences of characters in the second argument string replaced
5890 * by the character at the corresponding position in the third argument
5891 * string. For example, translate("bar","abc","ABC") returns the string
5892 * BAr. If there is a character in the second argument string with no
5893 * character at a corresponding position in the third argument string
5894 * (because the second argument string is longer than the third argument
5895 * string), then occurrences of that character in the first argument
5896 * string are removed. For example, translate("--aaa--","abc-","ABC")
5897 * returns "AAA". If a character occurs more than once in second
5898 * argument string, then the first occurrence determines the replacement
5899 * character. If the third argument string is longer than the second
5900 * argument string, then excess characters are ignored.
5901 */
5902void
5903xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde043ee12001-04-16 14:08:07 +00005904 xmlXPathObjectPtr str;
5905 xmlXPathObjectPtr from;
5906 xmlXPathObjectPtr to;
5907 xmlBufferPtr target;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005908 int offset, max;
Daniel Veillarde043ee12001-04-16 14:08:07 +00005909 xmlChar ch;
Daniel Veillard97ac1312001-05-30 19:14:17 +00005910 xmlChar *point;
5911 xmlChar *cptr;
Owen Taylor3473f882001-02-23 17:55:21 +00005912
Daniel Veillarde043ee12001-04-16 14:08:07 +00005913 CHECK_ARITY(3);
Owen Taylor3473f882001-02-23 17:55:21 +00005914
Daniel Veillarde043ee12001-04-16 14:08:07 +00005915 CAST_TO_STRING;
5916 to = valuePop(ctxt);
5917 CAST_TO_STRING;
5918 from = valuePop(ctxt);
5919 CAST_TO_STRING;
5920 str = valuePop(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005921
Daniel Veillarde043ee12001-04-16 14:08:07 +00005922 target = xmlBufferCreate();
5923 if (target) {
Daniel Veillard97ac1312001-05-30 19:14:17 +00005924 max = xmlUTF8Strlen(to->stringval);
5925 for (cptr = str->stringval; (ch=*cptr); ) {
5926 offset = xmlUTF8Strloc(from->stringval, cptr);
5927 if (offset >= 0) {
5928 if (offset < max) {
5929 point = xmlUTF8Strpos(to->stringval, offset);
5930 if (point)
5931 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
5932 }
5933 } else
5934 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
5935
5936 /* Step to next character in input */
5937 cptr++;
5938 if ( ch & 0x80 ) {
5939 /* if not simple ascii, verify proper format */
5940 if ( (ch & 0xc0) != 0xc0 ) {
5941 xmlGenericError(xmlGenericErrorContext,
5942 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
5943 break;
5944 }
5945 /* then skip over remaining bytes for this char */
5946 while ( (ch <<= 1) & 0x80 )
5947 if ( (*cptr++ & 0xc0) != 0x80 ) {
5948 xmlGenericError(xmlGenericErrorContext,
5949 "xmlXPathTranslateFunction: Invalid UTF8 string\n");
5950 break;
5951 }
5952 if (ch & 0x80) /* must have had error encountered */
5953 break;
5954 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00005955 }
Owen Taylor3473f882001-02-23 17:55:21 +00005956 }
Daniel Veillarde043ee12001-04-16 14:08:07 +00005957 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
5958 xmlBufferFree(target);
5959 xmlXPathFreeObject(str);
5960 xmlXPathFreeObject(from);
5961 xmlXPathFreeObject(to);
Owen Taylor3473f882001-02-23 17:55:21 +00005962}
5963
5964/**
5965 * xmlXPathBooleanFunction:
5966 * @ctxt: the XPath Parser context
5967 * @nargs: the number of arguments
5968 *
5969 * Implement the boolean() XPath function
5970 * boolean boolean(object)
5971 * he boolean function converts its argument to a boolean as follows:
5972 * - a number is true if and only if it is neither positive or
5973 * negative zero nor NaN
5974 * - a node-set is true if and only if it is non-empty
5975 * - a string is true if and only if its length is non-zero
5976 */
5977void
5978xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
5979 xmlXPathObjectPtr cur;
Owen Taylor3473f882001-02-23 17:55:21 +00005980
5981 CHECK_ARITY(1);
5982 cur = valuePop(ctxt);
5983 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00005984 cur = xmlXPathConvertBoolean(cur);
5985 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00005986}
5987
5988/**
5989 * xmlXPathNotFunction:
5990 * @ctxt: the XPath Parser context
5991 * @nargs: the number of arguments
5992 *
5993 * Implement the not() XPath function
5994 * boolean not(boolean)
5995 * The not function returns true if its argument is false,
5996 * and false otherwise.
5997 */
5998void
5999xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6000 CHECK_ARITY(1);
6001 CAST_TO_BOOLEAN;
6002 CHECK_TYPE(XPATH_BOOLEAN);
6003 ctxt->value->boolval = ! ctxt->value->boolval;
6004}
6005
6006/**
6007 * xmlXPathTrueFunction:
6008 * @ctxt: the XPath Parser context
6009 * @nargs: the number of arguments
6010 *
6011 * Implement the true() XPath function
6012 * boolean true()
6013 */
6014void
6015xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6016 CHECK_ARITY(0);
6017 valuePush(ctxt, xmlXPathNewBoolean(1));
6018}
6019
6020/**
6021 * xmlXPathFalseFunction:
6022 * @ctxt: the XPath Parser context
6023 * @nargs: the number of arguments
6024 *
6025 * Implement the false() XPath function
6026 * boolean false()
6027 */
6028void
6029xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6030 CHECK_ARITY(0);
6031 valuePush(ctxt, xmlXPathNewBoolean(0));
6032}
6033
6034/**
6035 * xmlXPathLangFunction:
6036 * @ctxt: the XPath Parser context
6037 * @nargs: the number of arguments
6038 *
6039 * Implement the lang() XPath function
6040 * boolean lang(string)
6041 * The lang function returns true or false depending on whether the
6042 * language of the context node as specified by xml:lang attributes
6043 * is the same as or is a sublanguage of the language specified by
6044 * the argument string. The language of the context node is determined
6045 * by the value of the xml:lang attribute on the context node, or, if
6046 * the context node has no xml:lang attribute, by the value of the
6047 * xml:lang attribute on the nearest ancestor of the context node that
6048 * has an xml:lang attribute. If there is no such attribute, then lang
6049 * returns false. If there is such an attribute, then lang returns
6050 * true if the attribute value is equal to the argument ignoring case,
6051 * or if there is some suffix starting with - such that the attribute
6052 * value is equal to the argument ignoring that suffix of the attribute
6053 * value and ignoring case.
6054 */
6055void
6056xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6057 xmlXPathObjectPtr val;
6058 const xmlChar *theLang;
6059 const xmlChar *lang;
6060 int ret = 0;
6061 int i;
6062
6063 CHECK_ARITY(1);
6064 CAST_TO_STRING;
6065 CHECK_TYPE(XPATH_STRING);
6066 val = valuePop(ctxt);
6067 lang = val->stringval;
6068 theLang = xmlNodeGetLang(ctxt->context->node);
6069 if ((theLang != NULL) && (lang != NULL)) {
6070 for (i = 0;lang[i] != 0;i++)
6071 if (toupper(lang[i]) != toupper(theLang[i]))
6072 goto not_equal;
6073 ret = 1;
6074 }
6075not_equal:
6076 xmlXPathFreeObject(val);
6077 valuePush(ctxt, xmlXPathNewBoolean(ret));
6078}
6079
6080/**
6081 * xmlXPathNumberFunction:
6082 * @ctxt: the XPath Parser context
6083 * @nargs: the number of arguments
6084 *
6085 * Implement the number() XPath function
6086 * number number(object?)
6087 */
6088void
6089xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6090 xmlXPathObjectPtr cur;
6091 double res;
6092
6093 if (nargs == 0) {
6094 if (ctxt->context->node == NULL) {
6095 valuePush(ctxt, xmlXPathNewFloat(0.0));
6096 } else {
6097 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
6098
6099 res = xmlXPathStringEvalNumber(content);
6100 valuePush(ctxt, xmlXPathNewFloat(res));
6101 xmlFree(content);
6102 }
6103 return;
6104 }
6105
6106 CHECK_ARITY(1);
6107 cur = valuePop(ctxt);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006108 cur = xmlXPathConvertNumber(cur);
6109 valuePush(ctxt, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00006110}
6111
6112/**
6113 * xmlXPathSumFunction:
6114 * @ctxt: the XPath Parser context
6115 * @nargs: the number of arguments
6116 *
6117 * Implement the sum() XPath function
6118 * number sum(node-set)
6119 * The sum function returns the sum of the values of the nodes in
6120 * the argument node-set.
6121 */
6122void
6123xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6124 xmlXPathObjectPtr cur;
6125 int i;
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006126 double res = 0.0;
Owen Taylor3473f882001-02-23 17:55:21 +00006127
6128 CHECK_ARITY(1);
6129 if ((ctxt->value == NULL) ||
6130 ((ctxt->value->type != XPATH_NODESET) &&
6131 (ctxt->value->type != XPATH_XSLT_TREE)))
6132 XP_ERROR(XPATH_INVALID_TYPE);
6133 cur = valuePop(ctxt);
6134
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006135 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006136 valuePush(ctxt, xmlXPathNewFloat(0.0));
6137 } else {
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006138 for (i = 0; i < cur->nodesetval->nodeNr; i++) {
6139 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +00006140 }
Daniel Veillardba0b8c92001-05-15 09:43:47 +00006141 valuePush(ctxt, xmlXPathNewFloat(res));
Owen Taylor3473f882001-02-23 17:55:21 +00006142 }
6143 xmlXPathFreeObject(cur);
6144}
6145
6146/**
6147 * xmlXPathFloorFunction:
6148 * @ctxt: the XPath Parser context
6149 * @nargs: the number of arguments
6150 *
6151 * Implement the floor() XPath function
6152 * number floor(number)
6153 * The floor function returns the largest (closest to positive infinity)
6154 * number that is not greater than the argument and that is an integer.
6155 */
6156void
6157xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6158 CHECK_ARITY(1);
6159 CAST_TO_NUMBER;
6160 CHECK_TYPE(XPATH_NUMBER);
6161#if 0
6162 ctxt->value->floatval = floor(ctxt->value->floatval);
6163#else
6164 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
6165 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
6166#endif
6167}
6168
6169/**
6170 * xmlXPathCeilingFunction:
6171 * @ctxt: the XPath Parser context
6172 * @nargs: the number of arguments
6173 *
6174 * Implement the ceiling() XPath function
6175 * number ceiling(number)
6176 * The ceiling function returns the smallest (closest to negative infinity)
6177 * number that is not less than the argument and that is an integer.
6178 */
6179void
6180xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6181 double f;
6182
6183 CHECK_ARITY(1);
6184 CAST_TO_NUMBER;
6185 CHECK_TYPE(XPATH_NUMBER);
6186
6187#if 0
6188 ctxt->value->floatval = ceil(ctxt->value->floatval);
6189#else
6190 f = (double)((int) ctxt->value->floatval);
6191 if (f != ctxt->value->floatval)
6192 ctxt->value->floatval = f + 1;
6193#endif
6194}
6195
6196/**
6197 * xmlXPathRoundFunction:
6198 * @ctxt: the XPath Parser context
6199 * @nargs: the number of arguments
6200 *
6201 * Implement the round() XPath function
6202 * number round(number)
6203 * The round function returns the number that is closest to the
6204 * argument and that is an integer. If there are two such numbers,
6205 * then the one that is even is returned.
6206 */
6207void
6208xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
6209 double f;
6210
6211 CHECK_ARITY(1);
6212 CAST_TO_NUMBER;
6213 CHECK_TYPE(XPATH_NUMBER);
6214
Daniel Veillardcda96922001-08-21 10:56:31 +00006215 if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
6216 (xmlXPathIsInf(ctxt->value->floatval) == 1) ||
6217 (xmlXPathIsInf(ctxt->value->floatval) == -1) ||
Owen Taylor3473f882001-02-23 17:55:21 +00006218 (ctxt->value->floatval == 0.0))
6219 return;
6220
6221#if 0
6222 f = floor(ctxt->value->floatval);
6223#else
6224 f = (double)((int) ctxt->value->floatval);
6225#endif
6226 if (ctxt->value->floatval < f + 0.5)
6227 ctxt->value->floatval = f;
6228 else
6229 ctxt->value->floatval = f + 1;
6230}
6231
6232/************************************************************************
6233 * *
6234 * The Parser *
6235 * *
6236 ************************************************************************/
6237
6238/*
6239 * a couple of forward declarations since we use a recursive call based
6240 * implementation.
6241 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006242static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006243static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006244static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006245#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006246static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt);
6247#define xmlXPathCompRelativeLocationPath xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00006248#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006249static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006250#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00006251static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
6252 int qualified);
Owen Taylor3473f882001-02-23 17:55:21 +00006253
6254/**
Daniel Veillard61d80a22001-04-27 17:13:01 +00006255 * xmlXPathCurrentChar:
6256 * @ctxt: the XPath parser context
6257 * @cur: pointer to the beginning of the char
6258 * @len: pointer to the length of the char read
6259 *
6260 * The current char value, if using UTF-8 this may actaully span multiple
6261 * bytes in the input buffer.
6262 *
6263 * Returns the current char value and its lenght
6264 */
6265
6266static int
6267xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
6268 unsigned char c;
6269 unsigned int val;
6270 const xmlChar *cur;
6271
6272 if (ctxt == NULL)
6273 return(0);
6274 cur = ctxt->cur;
6275
6276 /*
6277 * We are supposed to handle UTF8, check it's valid
6278 * From rfc2044: encoding of the Unicode values on UTF-8:
6279 *
6280 * UCS-4 range (hex.) UTF-8 octet sequence (binary)
6281 * 0000 0000-0000 007F 0xxxxxxx
6282 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
6283 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
6284 *
6285 * Check for the 0x110000 limit too
6286 */
6287 c = *cur;
6288 if (c & 0x80) {
6289 if ((cur[1] & 0xc0) != 0x80)
6290 goto encoding_error;
6291 if ((c & 0xe0) == 0xe0) {
6292
6293 if ((cur[2] & 0xc0) != 0x80)
6294 goto encoding_error;
6295 if ((c & 0xf0) == 0xf0) {
6296 if (((c & 0xf8) != 0xf0) ||
6297 ((cur[3] & 0xc0) != 0x80))
6298 goto encoding_error;
6299 /* 4-byte code */
6300 *len = 4;
6301 val = (cur[0] & 0x7) << 18;
6302 val |= (cur[1] & 0x3f) << 12;
6303 val |= (cur[2] & 0x3f) << 6;
6304 val |= cur[3] & 0x3f;
6305 } else {
6306 /* 3-byte code */
6307 *len = 3;
6308 val = (cur[0] & 0xf) << 12;
6309 val |= (cur[1] & 0x3f) << 6;
6310 val |= cur[2] & 0x3f;
6311 }
6312 } else {
6313 /* 2-byte code */
6314 *len = 2;
6315 val = (cur[0] & 0x1f) << 6;
6316 val |= cur[1] & 0x3f;
6317 }
6318 if (!IS_CHAR(val)) {
6319 XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
6320 }
6321 return(val);
6322 } else {
6323 /* 1-byte code */
6324 *len = 1;
6325 return((int) *cur);
6326 }
6327encoding_error:
6328 /*
6329 * If we detect an UTF8 error that probably mean that the
6330 * input encoding didn't get properly advertized in the
6331 * declaration header. Report the error and switch the encoding
6332 * to ISO-Latin-1 (if you don't like this policy, just declare the
6333 * encoding !)
6334 */
Daniel Veillard42596ad2001-05-22 16:57:14 +00006335 *len = 0;
Daniel Veillard61d80a22001-04-27 17:13:01 +00006336 XP_ERROR0(XPATH_ENCODING_ERROR);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006337}
6338
6339/**
Owen Taylor3473f882001-02-23 17:55:21 +00006340 * xmlXPathParseNCName:
6341 * @ctxt: the XPath Parser context
6342 *
6343 * parse an XML namespace non qualified name.
6344 *
6345 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
6346 *
6347 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
6348 * CombiningChar | Extender
6349 *
6350 * Returns the namespace name or NULL
6351 */
6352
6353xmlChar *
6354xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard2156a562001-04-28 12:24:34 +00006355 const xmlChar *in;
6356 xmlChar *ret;
6357 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006358
Daniel Veillard2156a562001-04-28 12:24:34 +00006359 /*
6360 * Accelerator for simple ASCII names
6361 */
6362 in = ctxt->cur;
6363 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6364 ((*in >= 0x41) && (*in <= 0x5A)) ||
6365 (*in == '_')) {
6366 in++;
6367 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6368 ((*in >= 0x41) && (*in <= 0x5A)) ||
6369 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard9a89a8a2001-06-27 11:13:35 +00006370 (*in == '_') || (*in == '.') ||
6371 (*in == '-'))
Daniel Veillard2156a562001-04-28 12:24:34 +00006372 in++;
6373 if ((*in == ' ') || (*in == '>') || (*in == '/') ||
6374 (*in == '[') || (*in == ']') || (*in == ':') ||
6375 (*in == '@') || (*in == '*')) {
6376 count = in - ctxt->cur;
6377 if (count == 0)
6378 return(NULL);
6379 ret = xmlStrndup(ctxt->cur, count);
6380 ctxt->cur = in;
6381 return(ret);
6382 }
6383 }
6384 return(xmlXPathParseNameComplex(ctxt, 0));
Owen Taylor3473f882001-02-23 17:55:21 +00006385}
6386
Daniel Veillard2156a562001-04-28 12:24:34 +00006387
Owen Taylor3473f882001-02-23 17:55:21 +00006388/**
6389 * xmlXPathParseQName:
6390 * @ctxt: the XPath Parser context
6391 * @prefix: a xmlChar **
6392 *
6393 * parse an XML qualified name
6394 *
6395 * [NS 5] QName ::= (Prefix ':')? LocalPart
6396 *
6397 * [NS 6] Prefix ::= NCName
6398 *
6399 * [NS 7] LocalPart ::= NCName
6400 *
6401 * Returns the function returns the local part, and prefix is updated
6402 * to get the Prefix if any.
6403 */
6404
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006405static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006406xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
6407 xmlChar *ret = NULL;
6408
6409 *prefix = NULL;
6410 ret = xmlXPathParseNCName(ctxt);
6411 if (CUR == ':') {
6412 *prefix = ret;
6413 NEXT;
6414 ret = xmlXPathParseNCName(ctxt);
6415 }
6416 return(ret);
6417}
6418
6419/**
6420 * xmlXPathParseName:
6421 * @ctxt: the XPath Parser context
6422 *
6423 * parse an XML name
6424 *
6425 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6426 * CombiningChar | Extender
6427 *
6428 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6429 *
6430 * Returns the namespace name or NULL
6431 */
6432
6433xmlChar *
6434xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006435 const xmlChar *in;
6436 xmlChar *ret;
6437 int count = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006438
Daniel Veillard61d80a22001-04-27 17:13:01 +00006439 /*
6440 * Accelerator for simple ASCII names
6441 */
6442 in = ctxt->cur;
6443 if (((*in >= 0x61) && (*in <= 0x7A)) ||
6444 ((*in >= 0x41) && (*in <= 0x5A)) ||
6445 (*in == '_') || (*in == ':')) {
6446 in++;
6447 while (((*in >= 0x61) && (*in <= 0x7A)) ||
6448 ((*in >= 0x41) && (*in <= 0x5A)) ||
6449 ((*in >= 0x30) && (*in <= 0x39)) ||
Daniel Veillard76d66f42001-05-16 21:05:17 +00006450 (*in == '_') || (*in == '-') ||
6451 (*in == ':') || (*in == '.'))
Daniel Veillard61d80a22001-04-27 17:13:01 +00006452 in++;
Daniel Veillard76d66f42001-05-16 21:05:17 +00006453 if ((*in > 0) && (*in < 0x80)) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006454 count = in - ctxt->cur;
6455 ret = xmlStrndup(ctxt->cur, count);
6456 ctxt->cur = in;
6457 return(ret);
6458 }
6459 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006460 return(xmlXPathParseNameComplex(ctxt, 1));
Owen Taylor3473f882001-02-23 17:55:21 +00006461}
6462
Daniel Veillard61d80a22001-04-27 17:13:01 +00006463static xmlChar *
Daniel Veillard2156a562001-04-28 12:24:34 +00006464xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006465 xmlChar buf[XML_MAX_NAMELEN + 5];
6466 int len = 0, l;
6467 int c;
6468
6469 /*
6470 * Handler for more complex cases
6471 */
6472 c = CUR_CHAR(l);
6473 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
Daniel Veillard2156a562001-04-28 12:24:34 +00006474 (c == '[') || (c == ']') || (c == '@') || /* accelerators */
6475 (c == '*') || /* accelerators */
Daniel Veillard61d80a22001-04-27 17:13:01 +00006476 (!IS_LETTER(c) && (c != '_') &&
Daniel Veillard2156a562001-04-28 12:24:34 +00006477 ((qualified) && (c != ':')))) {
Daniel Veillard61d80a22001-04-27 17:13:01 +00006478 return(NULL);
6479 }
6480
6481 while ((c != ' ') && (c != '>') && (c != '/') && /* test bigname.xml */
6482 ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
6483 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006484 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006485 (IS_COMBINING(c)) ||
6486 (IS_EXTENDER(c)))) {
6487 COPY_BUF(l,buf,len,c);
6488 NEXTL(l);
6489 c = CUR_CHAR(l);
6490 if (len >= XML_MAX_NAMELEN) {
6491 /*
6492 * Okay someone managed to make a huge name, so he's ready to pay
6493 * for the processing speed.
6494 */
6495 xmlChar *buffer;
6496 int max = len * 2;
6497
6498 buffer = (xmlChar *) xmlMalloc(max * sizeof(xmlChar));
6499 if (buffer == NULL) {
6500 XP_ERROR0(XPATH_MEMORY_ERROR);
6501 }
6502 memcpy(buffer, buf, len);
6503 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
6504 (c == '.') || (c == '-') ||
Daniel Veillard2156a562001-04-28 12:24:34 +00006505 (c == '_') || ((qualified) && (c == ':')) ||
Daniel Veillard61d80a22001-04-27 17:13:01 +00006506 (IS_COMBINING(c)) ||
6507 (IS_EXTENDER(c))) {
6508 if (len + 10 > max) {
6509 max *= 2;
6510 buffer = (xmlChar *) xmlRealloc(buffer,
6511 max * sizeof(xmlChar));
Daniel Veillard61d80a22001-04-27 17:13:01 +00006512 if (buffer == NULL) {
6513 XP_ERROR0(XPATH_MEMORY_ERROR);
6514 }
6515 }
6516 COPY_BUF(l,buffer,len,c);
6517 NEXTL(l);
6518 c = CUR_CHAR(l);
6519 }
6520 buffer[len] = 0;
6521 return(buffer);
6522 }
6523 }
Daniel Veillard2156a562001-04-28 12:24:34 +00006524 if (len == 0)
6525 return(NULL);
Daniel Veillard61d80a22001-04-27 17:13:01 +00006526 return(xmlStrndup(buf, len));
6527}
Owen Taylor3473f882001-02-23 17:55:21 +00006528/**
6529 * xmlXPathStringEvalNumber:
6530 * @str: A string to scan
6531 *
Bjorn Reese70a9da52001-04-21 16:57:29 +00006532 * [30a] Float ::= Number ('e' Digits?)?
6533 *
Owen Taylor3473f882001-02-23 17:55:21 +00006534 * [30] Number ::= Digits ('.' Digits?)?
6535 * | '.' Digits
6536 * [31] Digits ::= [0-9]+
6537 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006538 * Compile a Number in the string
Owen Taylor3473f882001-02-23 17:55:21 +00006539 * In complement of the Number expression, this function also handles
6540 * negative values : '-' Number.
6541 *
6542 * Returns the double value.
6543 */
6544double
6545xmlXPathStringEvalNumber(const xmlChar *str) {
6546 const xmlChar *cur = str;
6547 double ret = 0.0;
6548 double mult = 1;
Daniel Veillardb06c6142001-08-27 14:26:30 +00006549 int ok = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006550 int isneg = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006551 int exponent = 0;
6552 int is_exponent_negative = 0;
Daniel Veillardb06c6142001-08-27 14:26:30 +00006553#ifdef __GNUC__
6554 unsigned long tmp = 0;
6555#endif
Bjorn Reese70a9da52001-04-21 16:57:29 +00006556
Owen Taylor3473f882001-02-23 17:55:21 +00006557 while (IS_BLANK(*cur)) cur++;
6558 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
6559 return(xmlXPathNAN);
6560 }
6561 if (*cur == '-') {
6562 isneg = 1;
6563 cur++;
6564 }
Daniel Veillardb06c6142001-08-27 14:26:30 +00006565
6566#ifdef __GNUC__
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006567 /*
Daniel Veillardb06c6142001-08-27 14:26:30 +00006568 * tmp is a workaround against a gcc compiler bug
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006569 */
Owen Taylor3473f882001-02-23 17:55:21 +00006570 while ((*cur >= '0') && (*cur <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006571 tmp = tmp * 10 + (*cur - '0');
Owen Taylor3473f882001-02-23 17:55:21 +00006572 ok = 1;
6573 cur++;
6574 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006575 ret = (double) tmp;
Daniel Veillardb06c6142001-08-27 14:26:30 +00006576#else
6577 while ((*cur >= '0') && (*cur <= '9')) {
6578 ret = ret * 10 + (*cur - '0');
6579 ok = 1;
6580 cur++;
6581 }
6582#endif
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006583
Owen Taylor3473f882001-02-23 17:55:21 +00006584 if (*cur == '.') {
6585 cur++;
6586 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
6587 return(xmlXPathNAN);
6588 }
6589 while ((*cur >= '0') && (*cur <= '9')) {
6590 mult /= 10;
6591 ret = ret + (*cur - '0') * mult;
6592 cur++;
6593 }
6594 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006595 if ((*cur == 'e') || (*cur == 'E')) {
6596 cur++;
6597 if (*cur == '-') {
6598 is_exponent_negative = 1;
6599 cur++;
6600 }
6601 while ((*cur >= '0') && (*cur <= '9')) {
6602 exponent = exponent * 10 + (*cur - '0');
6603 cur++;
6604 }
6605 }
Owen Taylor3473f882001-02-23 17:55:21 +00006606 while (IS_BLANK(*cur)) cur++;
6607 if (*cur != 0) return(xmlXPathNAN);
6608 if (isneg) ret = -ret;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006609 if (is_exponent_negative) exponent = -exponent;
6610 ret *= pow(10.0, (double)exponent);
Owen Taylor3473f882001-02-23 17:55:21 +00006611 return(ret);
6612}
6613
6614/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006615 * xmlXPathCompNumber:
Owen Taylor3473f882001-02-23 17:55:21 +00006616 * @ctxt: the XPath Parser context
6617 *
6618 * [30] Number ::= Digits ('.' Digits?)?
6619 * | '.' Digits
6620 * [31] Digits ::= [0-9]+
6621 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006622 * Compile a Number, then push it on the stack
Owen Taylor3473f882001-02-23 17:55:21 +00006623 *
6624 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006625static void
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006626xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
6627{
Owen Taylor3473f882001-02-23 17:55:21 +00006628 double ret = 0.0;
6629 double mult = 1;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006630 int ok = 0, tmp = 0;
Bjorn Reese70a9da52001-04-21 16:57:29 +00006631 int exponent = 0;
6632 int is_exponent_negative = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006633
6634 CHECK_ERROR;
6635 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
6636 XP_ERROR(XPATH_NUMBER_ERROR);
6637 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006638 /*
6639 * Try to work around a gcc optimizer bug
6640 */
Owen Taylor3473f882001-02-23 17:55:21 +00006641 while ((CUR >= '0') && (CUR <= '9')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006642 tmp = tmp * 10 + (CUR - '0');
6643 ok = 1;
6644 NEXT;
Owen Taylor3473f882001-02-23 17:55:21 +00006645 }
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006646 ret = (double) tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006647 if (CUR == '.') {
6648 NEXT;
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006649 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
6650 XP_ERROR(XPATH_NUMBER_ERROR);
6651 }
6652 while ((CUR >= '0') && (CUR <= '9')) {
6653 mult /= 10;
6654 ret = ret + (CUR - '0') * mult;
6655 NEXT;
6656 }
Owen Taylor3473f882001-02-23 17:55:21 +00006657 }
Bjorn Reese70a9da52001-04-21 16:57:29 +00006658 if ((CUR == 'e') || (CUR == 'E')) {
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006659 NEXT;
6660 if (CUR == '-') {
6661 is_exponent_negative = 1;
6662 NEXT;
6663 }
6664 while ((CUR >= '0') && (CUR <= '9')) {
6665 exponent = exponent * 10 + (CUR - '0');
6666 NEXT;
6667 }
6668 if (is_exponent_negative)
6669 exponent = -exponent;
6670 ret *= pow(10.0, (double) exponent);
Bjorn Reese70a9da52001-04-21 16:57:29 +00006671 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006672 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
Daniel Veillardd79bcd12001-06-21 22:07:42 +00006673 xmlXPathNewFloat(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006674}
6675
6676/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006677 * xmlXPathParseLiteral:
6678 * @ctxt: the XPath Parser context
6679 *
6680 * Parse a Literal
6681 *
6682 * [29] Literal ::= '"' [^"]* '"'
6683 * | "'" [^']* "'"
6684 *
6685 * Returns the value found or NULL in case of error
6686 */
6687static xmlChar *
6688xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
6689 const xmlChar *q;
6690 xmlChar *ret = NULL;
6691
6692 if (CUR == '"') {
6693 NEXT;
6694 q = CUR_PTR;
6695 while ((IS_CHAR(CUR)) && (CUR != '"'))
6696 NEXT;
6697 if (!IS_CHAR(CUR)) {
6698 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6699 } else {
6700 ret = xmlStrndup(q, CUR_PTR - q);
6701 NEXT;
6702 }
6703 } else if (CUR == '\'') {
6704 NEXT;
6705 q = CUR_PTR;
6706 while ((IS_CHAR(CUR)) && (CUR != '\''))
6707 NEXT;
6708 if (!IS_CHAR(CUR)) {
6709 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
6710 } else {
6711 ret = xmlStrndup(q, CUR_PTR - q);
6712 NEXT;
6713 }
6714 } else {
6715 XP_ERROR0(XPATH_START_LITERAL_ERROR);
6716 }
6717 return(ret);
6718}
6719
6720/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006721 * xmlXPathCompLiteral:
Owen Taylor3473f882001-02-23 17:55:21 +00006722 * @ctxt: the XPath Parser context
6723 *
6724 * Parse a Literal and push it on the stack.
6725 *
6726 * [29] Literal ::= '"' [^"]* '"'
6727 * | "'" [^']* "'"
6728 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006729 * TODO: xmlXPathCompLiteral memory allocation could be improved.
Owen Taylor3473f882001-02-23 17:55:21 +00006730 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006731static void
6732xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006733 const xmlChar *q;
6734 xmlChar *ret = NULL;
6735
6736 if (CUR == '"') {
6737 NEXT;
6738 q = CUR_PTR;
6739 while ((IS_CHAR(CUR)) && (CUR != '"'))
6740 NEXT;
6741 if (!IS_CHAR(CUR)) {
6742 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6743 } else {
6744 ret = xmlStrndup(q, CUR_PTR - q);
6745 NEXT;
6746 }
6747 } else if (CUR == '\'') {
6748 NEXT;
6749 q = CUR_PTR;
6750 while ((IS_CHAR(CUR)) && (CUR != '\''))
6751 NEXT;
6752 if (!IS_CHAR(CUR)) {
6753 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
6754 } else {
6755 ret = xmlStrndup(q, CUR_PTR - q);
6756 NEXT;
6757 }
6758 } else {
6759 XP_ERROR(XPATH_START_LITERAL_ERROR);
6760 }
6761 if (ret == NULL) return;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006762 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
6763 xmlXPathNewString(ret), NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006764 xmlFree(ret);
6765}
6766
6767/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006768 * xmlXPathCompVariableReference:
Owen Taylor3473f882001-02-23 17:55:21 +00006769 * @ctxt: the XPath Parser context
6770 *
6771 * Parse a VariableReference, evaluate it and push it on the stack.
6772 *
6773 * The variable bindings consist of a mapping from variable names
6774 * to variable values. The value of a variable is an object, which
6775 * of any of the types that are possible for the value of an expression,
6776 * and may also be of additional types not specified here.
6777 *
6778 * Early evaluation is possible since:
6779 * The variable bindings [...] used to evaluate a subexpression are
6780 * always the same as those used to evaluate the containing expression.
6781 *
6782 * [36] VariableReference ::= '$' QName
6783 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006784static void
6785xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006786 xmlChar *name;
6787 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006788
6789 SKIP_BLANKS;
6790 if (CUR != '$') {
6791 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6792 }
6793 NEXT;
6794 name = xmlXPathParseQName(ctxt, &prefix);
6795 if (name == NULL) {
6796 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
6797 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00006798 ctxt->comp->last = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006799 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
6800 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006801 SKIP_BLANKS;
6802}
6803
6804/**
6805 * xmlXPathIsNodeType:
6806 * @ctxt: the XPath Parser context
6807 * @name: a name string
6808 *
6809 * Is the name given a NodeType one.
6810 *
6811 * [38] NodeType ::= 'comment'
6812 * | 'text'
6813 * | 'processing-instruction'
6814 * | 'node'
6815 *
6816 * Returns 1 if true 0 otherwise
6817 */
6818int
6819xmlXPathIsNodeType(const xmlChar *name) {
6820 if (name == NULL)
6821 return(0);
6822
6823 if (xmlStrEqual(name, BAD_CAST "comment"))
6824 return(1);
6825 if (xmlStrEqual(name, BAD_CAST "text"))
6826 return(1);
6827 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6828 return(1);
6829 if (xmlStrEqual(name, BAD_CAST "node"))
6830 return(1);
6831 return(0);
6832}
6833
6834/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006835 * xmlXPathCompFunctionCall:
Owen Taylor3473f882001-02-23 17:55:21 +00006836 * @ctxt: the XPath Parser context
6837 *
6838 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
6839 * [17] Argument ::= Expr
6840 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006841 * Compile a function call, the evaluation of all arguments are
Owen Taylor3473f882001-02-23 17:55:21 +00006842 * pushed on the stack
6843 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006844static void
6845xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006846 xmlChar *name;
6847 xmlChar *prefix;
Owen Taylor3473f882001-02-23 17:55:21 +00006848 int nbargs = 0;
6849
6850 name = xmlXPathParseQName(ctxt, &prefix);
6851 if (name == NULL) {
6852 XP_ERROR(XPATH_EXPR_ERROR);
6853 }
6854 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006855#ifdef DEBUG_EXPR
6856 if (prefix == NULL)
6857 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
6858 name);
6859 else
6860 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
6861 prefix, name);
6862#endif
6863
Owen Taylor3473f882001-02-23 17:55:21 +00006864 if (CUR != '(') {
6865 XP_ERROR(XPATH_EXPR_ERROR);
6866 }
6867 NEXT;
6868 SKIP_BLANKS;
6869
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006870 ctxt->comp->last = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006871 while (CUR != ')') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006872 int op1 = ctxt->comp->last;
6873 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006874 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006875 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00006876 nbargs++;
6877 if (CUR == ')') break;
6878 if (CUR != ',') {
6879 XP_ERROR(XPATH_EXPR_ERROR);
6880 }
6881 NEXT;
6882 SKIP_BLANKS;
6883 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00006884 PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0,
6885 name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00006886 NEXT;
6887 SKIP_BLANKS;
Owen Taylor3473f882001-02-23 17:55:21 +00006888}
6889
6890/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006891 * xmlXPathCompPrimaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006892 * @ctxt: the XPath Parser context
6893 *
6894 * [15] PrimaryExpr ::= VariableReference
6895 * | '(' Expr ')'
6896 * | Literal
6897 * | Number
6898 * | FunctionCall
6899 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006900 * Compile a primary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006901 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006902static void
6903xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00006904 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006905 if (CUR == '$') xmlXPathCompVariableReference(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006906 else if (CUR == '(') {
6907 NEXT;
6908 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006909 xmlXPathCompileExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006910 if (CUR != ')') {
6911 XP_ERROR(XPATH_EXPR_ERROR);
6912 }
6913 NEXT;
6914 SKIP_BLANKS;
6915 } else if (IS_DIGIT(CUR)) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006916 xmlXPathCompNumber(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006917 } else if ((CUR == '\'') || (CUR == '"')) {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006918 xmlXPathCompLiteral(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006919 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006920 xmlXPathCompFunctionCall(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006921 }
6922 SKIP_BLANKS;
6923}
6924
6925/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006926 * xmlXPathCompFilterExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00006927 * @ctxt: the XPath Parser context
6928 *
6929 * [20] FilterExpr ::= PrimaryExpr
6930 * | FilterExpr Predicate
6931 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006932 * Compile a filter expression.
Owen Taylor3473f882001-02-23 17:55:21 +00006933 * Square brackets are used to filter expressions in the same way that
6934 * they are used in location paths. It is an error if the expression to
6935 * be filtered does not evaluate to a node-set. The context node list
6936 * used for evaluating the expression in square brackets is the node-set
6937 * to be filtered listed in document order.
6938 */
6939
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00006940static void
6941xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
6942 xmlXPathCompPrimaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006943 CHECK_ERROR;
6944 SKIP_BLANKS;
6945
6946 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00006947 xmlXPathCompPredicate(ctxt, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00006948 SKIP_BLANKS;
6949 }
6950
6951
6952}
6953
6954/**
6955 * xmlXPathScanName:
6956 * @ctxt: the XPath Parser context
6957 *
6958 * Trickery: parse an XML name but without consuming the input flow
6959 * Needed to avoid insanity in the parser state.
6960 *
6961 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
6962 * CombiningChar | Extender
6963 *
6964 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
6965 *
6966 * [6] Names ::= Name (S Name)*
6967 *
6968 * Returns the Name parsed or NULL
6969 */
6970
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006971static xmlChar *
Owen Taylor3473f882001-02-23 17:55:21 +00006972xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
6973 xmlChar buf[XML_MAX_NAMELEN];
6974 int len = 0;
6975
6976 SKIP_BLANKS;
6977 if (!IS_LETTER(CUR) && (CUR != '_') &&
6978 (CUR != ':')) {
6979 return(NULL);
6980 }
6981
6982 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
6983 (NXT(len) == '.') || (NXT(len) == '-') ||
6984 (NXT(len) == '_') || (NXT(len) == ':') ||
6985 (IS_COMBINING(NXT(len))) ||
6986 (IS_EXTENDER(NXT(len)))) {
6987 buf[len] = NXT(len);
6988 len++;
6989 if (len >= XML_MAX_NAMELEN) {
6990 xmlGenericError(xmlGenericErrorContext,
6991 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
6992 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
6993 (NXT(len) == '.') || (NXT(len) == '-') ||
6994 (NXT(len) == '_') || (NXT(len) == ':') ||
6995 (IS_COMBINING(NXT(len))) ||
6996 (IS_EXTENDER(NXT(len))))
6997 len++;
6998 break;
6999 }
7000 }
7001 return(xmlStrndup(buf, len));
7002}
7003
7004/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007005 * xmlXPathCompPathExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007006 * @ctxt: the XPath Parser context
7007 *
7008 * [19] PathExpr ::= LocationPath
7009 * | FilterExpr
7010 * | FilterExpr '/' RelativeLocationPath
7011 * | FilterExpr '//' RelativeLocationPath
7012 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007013 * Compile a path expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007014 * The / operator and // operators combine an arbitrary expression
7015 * and a relative location path. It is an error if the expression
7016 * does not evaluate to a node-set.
7017 * The / operator does composition in the same way as when / is
7018 * used in a location path. As in location paths, // is short for
7019 * /descendant-or-self::node()/.
7020 */
7021
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007022static void
7023xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007024 int lc = 1; /* Should we branch to LocationPath ? */
7025 xmlChar *name = NULL; /* we may have to preparse a name to find out */
7026
7027 SKIP_BLANKS;
7028 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
7029 (CUR == '\'') || (CUR == '"')) {
7030 lc = 0;
7031 } else if (CUR == '*') {
7032 /* relative or absolute location path */
7033 lc = 1;
7034 } else if (CUR == '/') {
7035 /* relative or absolute location path */
7036 lc = 1;
7037 } else if (CUR == '@') {
7038 /* relative abbreviated attribute location path */
7039 lc = 1;
7040 } else if (CUR == '.') {
7041 /* relative abbreviated attribute location path */
7042 lc = 1;
7043 } else {
7044 /*
7045 * Problem is finding if we have a name here whether it's:
7046 * - a nodetype
7047 * - a function call in which case it's followed by '('
7048 * - an axis in which case it's followed by ':'
7049 * - a element name
7050 * We do an a priori analysis here rather than having to
7051 * maintain parsed token content through the recursive function
7052 * calls. This looks uglier but makes the code quite easier to
7053 * read/write/debug.
7054 */
7055 SKIP_BLANKS;
7056 name = xmlXPathScanName(ctxt);
7057 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
7058#ifdef DEBUG_STEP
7059 xmlGenericError(xmlGenericErrorContext,
7060 "PathExpr: Axis\n");
7061#endif
7062 lc = 1;
7063 xmlFree(name);
7064 } else if (name != NULL) {
7065 int len =xmlStrlen(name);
7066 int blank = 0;
7067
7068
7069 while (NXT(len) != 0) {
7070 if (NXT(len) == '/') {
7071 /* element name */
7072#ifdef DEBUG_STEP
7073 xmlGenericError(xmlGenericErrorContext,
7074 "PathExpr: AbbrRelLocation\n");
7075#endif
7076 lc = 1;
7077 break;
7078 } else if (IS_BLANK(NXT(len))) {
7079 /* skip to next */
7080 blank = 1;
7081 } else if (NXT(len) == ':') {
7082#ifdef DEBUG_STEP
7083 xmlGenericError(xmlGenericErrorContext,
7084 "PathExpr: AbbrRelLocation\n");
7085#endif
7086 lc = 1;
7087 break;
7088 } else if ((NXT(len) == '(')) {
7089 /* Note Type or Function */
7090 if (xmlXPathIsNodeType(name)) {
7091#ifdef DEBUG_STEP
7092 xmlGenericError(xmlGenericErrorContext,
7093 "PathExpr: Type search\n");
7094#endif
7095 lc = 1;
7096 } else {
7097#ifdef DEBUG_STEP
7098 xmlGenericError(xmlGenericErrorContext,
7099 "PathExpr: function call\n");
7100#endif
7101 lc = 0;
7102 }
7103 break;
7104 } else if ((NXT(len) == '[')) {
7105 /* element name */
7106#ifdef DEBUG_STEP
7107 xmlGenericError(xmlGenericErrorContext,
7108 "PathExpr: AbbrRelLocation\n");
7109#endif
7110 lc = 1;
7111 break;
7112 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
7113 (NXT(len) == '=')) {
7114 lc = 1;
7115 break;
7116 } else {
7117 lc = 1;
7118 break;
7119 }
7120 len++;
7121 }
7122 if (NXT(len) == 0) {
7123#ifdef DEBUG_STEP
7124 xmlGenericError(xmlGenericErrorContext,
7125 "PathExpr: AbbrRelLocation\n");
7126#endif
7127 /* element name */
7128 lc = 1;
7129 }
7130 xmlFree(name);
7131 } else {
7132 /* make sure all cases are covered explicitely */
7133 XP_ERROR(XPATH_EXPR_ERROR);
7134 }
7135 }
7136
7137 if (lc) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007138 if (CUR == '/') {
7139 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
7140 } else {
7141 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007142 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007143 xmlXPathCompLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007144 } else {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007145 xmlXPathCompFilterExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007146 CHECK_ERROR;
7147 if ((CUR == '/') && (NXT(1) == '/')) {
7148 SKIP(2);
7149 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007150
7151 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7152 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
7153 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
7154
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007155 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007156 } else if (CUR == '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007157 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007158 }
7159 }
7160 SKIP_BLANKS;
7161}
7162
7163/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007164 * xmlXPathCompUnionExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007165 * @ctxt: the XPath Parser context
7166 *
7167 * [18] UnionExpr ::= PathExpr
7168 * | UnionExpr '|' PathExpr
7169 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007170 * Compile an union expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007171 */
7172
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007173static void
7174xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
7175 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007176 CHECK_ERROR;
7177 SKIP_BLANKS;
7178 while (CUR == '|') {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007179 int op1 = ctxt->comp->last;
7180 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007181
7182 NEXT;
7183 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007184 xmlXPathCompPathExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007185
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007186 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
7187
Owen Taylor3473f882001-02-23 17:55:21 +00007188 SKIP_BLANKS;
7189 }
Owen Taylor3473f882001-02-23 17:55:21 +00007190}
7191
7192/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007193 * xmlXPathCompUnaryExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007194 * @ctxt: the XPath Parser context
7195 *
7196 * [27] UnaryExpr ::= UnionExpr
7197 * | '-' UnaryExpr
7198 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007199 * Compile an unary expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007200 */
7201
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007202static void
7203xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007204 int minus = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007205 int found = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007206
7207 SKIP_BLANKS;
Daniel Veillard68d7b672001-03-12 18:22:04 +00007208 while (CUR == '-') {
7209 minus = 1 - minus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007210 found = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007211 NEXT;
7212 SKIP_BLANKS;
7213 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007214
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007215 xmlXPathCompUnionExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007216 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007217 if (found) {
7218 if (minus)
7219 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
7220 else
7221 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007222 }
7223}
7224
7225/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007226 * xmlXPathCompMultiplicativeExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007227 * @ctxt: the XPath Parser context
7228 *
7229 * [26] MultiplicativeExpr ::= UnaryExpr
7230 * | MultiplicativeExpr MultiplyOperator UnaryExpr
7231 * | MultiplicativeExpr 'div' UnaryExpr
7232 * | MultiplicativeExpr 'mod' UnaryExpr
7233 * [34] MultiplyOperator ::= '*'
7234 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007235 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007236 */
7237
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007238static void
7239xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
7240 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007241 CHECK_ERROR;
7242 SKIP_BLANKS;
7243 while ((CUR == '*') ||
7244 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
7245 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
7246 int op = -1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007247 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007248
7249 if (CUR == '*') {
7250 op = 0;
7251 NEXT;
7252 } else if (CUR == 'd') {
7253 op = 1;
7254 SKIP(3);
7255 } else if (CUR == 'm') {
7256 op = 2;
7257 SKIP(3);
7258 }
7259 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007260 xmlXPathCompUnaryExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007261 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007262 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007263 SKIP_BLANKS;
7264 }
7265}
7266
7267/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007268 * xmlXPathCompAdditiveExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007269 * @ctxt: the XPath Parser context
7270 *
7271 * [25] AdditiveExpr ::= MultiplicativeExpr
7272 * | AdditiveExpr '+' MultiplicativeExpr
7273 * | AdditiveExpr '-' MultiplicativeExpr
7274 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007275 * Compile an Additive expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007276 */
7277
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007278static void
7279xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007280
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007281 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007282 CHECK_ERROR;
7283 SKIP_BLANKS;
7284 while ((CUR == '+') || (CUR == '-')) {
7285 int plus;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007286 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007287
7288 if (CUR == '+') plus = 1;
7289 else plus = 0;
7290 NEXT;
7291 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007292 xmlXPathCompMultiplicativeExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007293 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007294 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007295 SKIP_BLANKS;
7296 }
7297}
7298
7299/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007300 * xmlXPathCompRelationalExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007301 * @ctxt: the XPath Parser context
7302 *
7303 * [24] RelationalExpr ::= AdditiveExpr
7304 * | RelationalExpr '<' AdditiveExpr
7305 * | RelationalExpr '>' AdditiveExpr
7306 * | RelationalExpr '<=' AdditiveExpr
7307 * | RelationalExpr '>=' AdditiveExpr
7308 *
7309 * A <= B > C is allowed ? Answer from James, yes with
7310 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
7311 * which is basically what got implemented.
7312 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007313 * Compile a Relational expression, then push the result
Owen Taylor3473f882001-02-23 17:55:21 +00007314 * on the stack
7315 */
7316
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007317static void
7318xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
7319 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007320 CHECK_ERROR;
7321 SKIP_BLANKS;
7322 while ((CUR == '<') ||
7323 (CUR == '>') ||
7324 ((CUR == '<') && (NXT(1) == '=')) ||
7325 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007326 int inf, strict;
7327 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007328
7329 if (CUR == '<') inf = 1;
7330 else inf = 0;
7331 if (NXT(1) == '=') strict = 0;
7332 else strict = 1;
7333 NEXT;
7334 if (!strict) NEXT;
7335 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007336 xmlXPathCompAdditiveExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007337 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007338 PUSH_BINARY_EXPR(XPATH_OP_CMP, op1, ctxt->comp->last, inf, strict);
Owen Taylor3473f882001-02-23 17:55:21 +00007339 SKIP_BLANKS;
7340 }
7341}
7342
7343/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007344 * xmlXPathCompEqualityExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007345 * @ctxt: the XPath Parser context
7346 *
7347 * [23] EqualityExpr ::= RelationalExpr
7348 * | EqualityExpr '=' RelationalExpr
7349 * | EqualityExpr '!=' RelationalExpr
7350 *
7351 * A != B != C is allowed ? Answer from James, yes with
7352 * (RelationalExpr = RelationalExpr) = RelationalExpr
7353 * (RelationalExpr != RelationalExpr) != RelationalExpr
7354 * which is basically what got implemented.
7355 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007356 * Compile an Equality expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007357 *
7358 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007359static void
7360xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
7361 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007362 CHECK_ERROR;
7363 SKIP_BLANKS;
7364 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007365 int eq;
7366 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007367
7368 if (CUR == '=') eq = 1;
7369 else eq = 0;
7370 NEXT;
7371 if (!eq) NEXT;
7372 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007373 xmlXPathCompRelationalExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007374 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007375 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007376 SKIP_BLANKS;
7377 }
7378}
7379
7380/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007381 * xmlXPathCompAndExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007382 * @ctxt: the XPath Parser context
7383 *
7384 * [22] AndExpr ::= EqualityExpr
7385 * | AndExpr 'and' EqualityExpr
7386 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007387 * Compile an AND expression.
Owen Taylor3473f882001-02-23 17:55:21 +00007388 *
7389 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007390static void
7391xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
7392 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007393 CHECK_ERROR;
7394 SKIP_BLANKS;
7395 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007396 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007397 SKIP(3);
7398 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007399 xmlXPathCompEqualityExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007400 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007401 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007402 SKIP_BLANKS;
7403 }
7404}
7405
7406/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007407 * xmlXPathCompExpr:
Owen Taylor3473f882001-02-23 17:55:21 +00007408 * @ctxt: the XPath Parser context
7409 *
7410 * [14] Expr ::= OrExpr
7411 * [21] OrExpr ::= AndExpr
7412 * | OrExpr 'or' AndExpr
7413 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007414 * Parse and compile an expression
Owen Taylor3473f882001-02-23 17:55:21 +00007415 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007416static void
7417xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
7418 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007419 CHECK_ERROR;
7420 SKIP_BLANKS;
7421 while ((CUR == 'o') && (NXT(1) == 'r')) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007422 int op1 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007423 SKIP(2);
7424 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007425 xmlXPathCompAndExpr(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007426 CHECK_ERROR;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007427 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
7428 op1 = ctxt->comp->nbStep;
Owen Taylor3473f882001-02-23 17:55:21 +00007429 SKIP_BLANKS;
7430 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007431 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
7432 /* more ops could be optimized too */
7433 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
7434 }
Owen Taylor3473f882001-02-23 17:55:21 +00007435}
7436
7437/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007438 * xmlXPathCompPredicate:
Owen Taylor3473f882001-02-23 17:55:21 +00007439 * @ctxt: the XPath Parser context
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007440 * @filter: act as a filter
Owen Taylor3473f882001-02-23 17:55:21 +00007441 *
7442 * [8] Predicate ::= '[' PredicateExpr ']'
7443 * [9] PredicateExpr ::= Expr
7444 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007445 * Compile a predicate expression
Owen Taylor3473f882001-02-23 17:55:21 +00007446 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007447static void
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007448xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007449 int op1 = ctxt->comp->last;
7450
7451 SKIP_BLANKS;
7452 if (CUR != '[') {
7453 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7454 }
7455 NEXT;
7456 SKIP_BLANKS;
7457
7458 ctxt->comp->last = -1;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007459 xmlXPathCompileExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007460 CHECK_ERROR;
7461
7462 if (CUR != ']') {
7463 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
7464 }
7465
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007466 if (filter)
7467 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
7468 else
7469 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007470
7471 NEXT;
7472 SKIP_BLANKS;
7473}
7474
7475/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007476 * xmlXPathCompNodeTest:
Owen Taylor3473f882001-02-23 17:55:21 +00007477 * @ctxt: the XPath Parser context
7478 * @test: pointer to a xmlXPathTestVal
7479 * @type: pointer to a xmlXPathTypeVal
7480 * @prefix: placeholder for a possible name prefix
7481 *
7482 * [7] NodeTest ::= NameTest
7483 * | NodeType '(' ')'
7484 * | 'processing-instruction' '(' Literal ')'
7485 *
7486 * [37] NameTest ::= '*'
7487 * | NCName ':' '*'
7488 * | QName
7489 * [38] NodeType ::= 'comment'
7490 * | 'text'
7491 * | 'processing-instruction'
7492 * | 'node'
7493 *
7494 * Returns the name found and update @test, @type and @prefix appropriately
7495 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007496static xmlChar *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007497xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
7498 xmlXPathTypeVal *type, const xmlChar **prefix,
7499 xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00007500 int blanks;
7501
7502 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
7503 STRANGE;
7504 return(NULL);
7505 }
7506 *type = 0;
7507 *test = 0;
7508 *prefix = NULL;
7509 SKIP_BLANKS;
7510
7511 if ((name == NULL) && (CUR == '*')) {
7512 /*
7513 * All elements
7514 */
7515 NEXT;
7516 *test = NODE_TEST_ALL;
7517 return(NULL);
7518 }
7519
7520 if (name == NULL)
7521 name = xmlXPathParseNCName(ctxt);
7522 if (name == NULL) {
7523 XP_ERROR0(XPATH_EXPR_ERROR);
7524 }
7525
7526 blanks = IS_BLANK(CUR);
7527 SKIP_BLANKS;
7528 if (CUR == '(') {
7529 NEXT;
7530 /*
7531 * NodeType or PI search
7532 */
7533 if (xmlStrEqual(name, BAD_CAST "comment"))
7534 *type = NODE_TYPE_COMMENT;
7535 else if (xmlStrEqual(name, BAD_CAST "node"))
7536 *type = NODE_TYPE_NODE;
7537 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
7538 *type = NODE_TYPE_PI;
7539 else if (xmlStrEqual(name, BAD_CAST "text"))
7540 *type = NODE_TYPE_TEXT;
7541 else {
7542 if (name != NULL)
7543 xmlFree(name);
7544 XP_ERROR0(XPATH_EXPR_ERROR);
7545 }
7546
7547 *test = NODE_TEST_TYPE;
7548
7549 SKIP_BLANKS;
7550 if (*type == NODE_TYPE_PI) {
7551 /*
7552 * Specific case: search a PI by name.
7553 */
Owen Taylor3473f882001-02-23 17:55:21 +00007554 if (name != NULL)
7555 xmlFree(name);
Daniel Veillard82e49712001-04-26 14:38:03 +00007556 name = NULL;
7557 if (CUR != ')') {
7558 name = xmlXPathParseLiteral(ctxt);
7559 CHECK_ERROR 0;
7560 SKIP_BLANKS;
7561 }
Owen Taylor3473f882001-02-23 17:55:21 +00007562 }
7563 if (CUR != ')') {
7564 if (name != NULL)
7565 xmlFree(name);
7566 XP_ERROR0(XPATH_UNCLOSED_ERROR);
7567 }
7568 NEXT;
7569 return(name);
7570 }
7571 *test = NODE_TEST_NAME;
7572 if ((!blanks) && (CUR == ':')) {
7573 NEXT;
7574
7575 /*
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007576 * Since currently the parser context don't have a
7577 * namespace list associated:
7578 * The namespace name for this prefix can be computed
7579 * only at evaluation time. The compilation is done
7580 * outside of any context.
Owen Taylor3473f882001-02-23 17:55:21 +00007581 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007582#if 0
Owen Taylor3473f882001-02-23 17:55:21 +00007583 *prefix = xmlXPathNsLookup(ctxt->context, name);
7584 if (name != NULL)
7585 xmlFree(name);
7586 if (*prefix == NULL) {
7587 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
7588 }
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007589#else
7590 *prefix = name;
7591#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007592
7593 if (CUR == '*') {
7594 /*
7595 * All elements
7596 */
7597 NEXT;
7598 *test = NODE_TEST_ALL;
7599 return(NULL);
7600 }
7601
7602 name = xmlXPathParseNCName(ctxt);
7603 if (name == NULL) {
7604 XP_ERROR0(XPATH_EXPR_ERROR);
7605 }
7606 }
7607 return(name);
7608}
7609
7610/**
7611 * xmlXPathIsAxisName:
7612 * @name: a preparsed name token
7613 *
7614 * [6] AxisName ::= 'ancestor'
7615 * | 'ancestor-or-self'
7616 * | 'attribute'
7617 * | 'child'
7618 * | 'descendant'
7619 * | 'descendant-or-self'
7620 * | 'following'
7621 * | 'following-sibling'
7622 * | 'namespace'
7623 * | 'parent'
7624 * | 'preceding'
7625 * | 'preceding-sibling'
7626 * | 'self'
7627 *
7628 * Returns the axis or 0
7629 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00007630static xmlXPathAxisVal
Owen Taylor3473f882001-02-23 17:55:21 +00007631xmlXPathIsAxisName(const xmlChar *name) {
7632 xmlXPathAxisVal ret = 0;
7633 switch (name[0]) {
7634 case 'a':
7635 if (xmlStrEqual(name, BAD_CAST "ancestor"))
7636 ret = AXIS_ANCESTOR;
7637 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
7638 ret = AXIS_ANCESTOR_OR_SELF;
7639 if (xmlStrEqual(name, BAD_CAST "attribute"))
7640 ret = AXIS_ATTRIBUTE;
7641 break;
7642 case 'c':
7643 if (xmlStrEqual(name, BAD_CAST "child"))
7644 ret = AXIS_CHILD;
7645 break;
7646 case 'd':
7647 if (xmlStrEqual(name, BAD_CAST "descendant"))
7648 ret = AXIS_DESCENDANT;
7649 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
7650 ret = AXIS_DESCENDANT_OR_SELF;
7651 break;
7652 case 'f':
7653 if (xmlStrEqual(name, BAD_CAST "following"))
7654 ret = AXIS_FOLLOWING;
7655 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
7656 ret = AXIS_FOLLOWING_SIBLING;
7657 break;
7658 case 'n':
7659 if (xmlStrEqual(name, BAD_CAST "namespace"))
7660 ret = AXIS_NAMESPACE;
7661 break;
7662 case 'p':
7663 if (xmlStrEqual(name, BAD_CAST "parent"))
7664 ret = AXIS_PARENT;
7665 if (xmlStrEqual(name, BAD_CAST "preceding"))
7666 ret = AXIS_PRECEDING;
7667 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
7668 ret = AXIS_PRECEDING_SIBLING;
7669 break;
7670 case 's':
7671 if (xmlStrEqual(name, BAD_CAST "self"))
7672 ret = AXIS_SELF;
7673 break;
7674 }
7675 return(ret);
7676}
7677
7678/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007679 * xmlXPathCompStep:
Owen Taylor3473f882001-02-23 17:55:21 +00007680 * @ctxt: the XPath Parser context
7681 *
7682 * [4] Step ::= AxisSpecifier NodeTest Predicate*
7683 * | AbbreviatedStep
7684 *
7685 * [12] AbbreviatedStep ::= '.' | '..'
7686 *
7687 * [5] AxisSpecifier ::= AxisName '::'
7688 * | AbbreviatedAxisSpecifier
7689 *
7690 * [13] AbbreviatedAxisSpecifier ::= '@'?
7691 *
7692 * Modified for XPtr range support as:
7693 *
7694 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
7695 * | AbbreviatedStep
7696 * | 'range-to' '(' Expr ')' Predicate*
7697 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007698 * Compile one step in a Location Path
Owen Taylor3473f882001-02-23 17:55:21 +00007699 * A location step of . is short for self::node(). This is
7700 * particularly useful in conjunction with //. For example, the
7701 * location path .//para is short for
7702 * self::node()/descendant-or-self::node()/child::para
7703 * and so will select all para descendant elements of the context
7704 * node.
7705 * Similarly, a location step of .. is short for parent::node().
7706 * For example, ../title is short for parent::node()/child::title
7707 * and so will select the title children of the parent of the context
7708 * node.
7709 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007710static void
7711xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007712#ifdef LIBXML_XPTR_ENABLED
7713 int rangeto = 0;
7714 int op2 = -1;
7715#endif
7716
Owen Taylor3473f882001-02-23 17:55:21 +00007717 SKIP_BLANKS;
7718 if ((CUR == '.') && (NXT(1) == '.')) {
7719 SKIP(2);
7720 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007721 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
7722 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007723 } else if (CUR == '.') {
7724 NEXT;
7725 SKIP_BLANKS;
7726 } else {
7727 xmlChar *name = NULL;
7728 const xmlChar *prefix = NULL;
7729 xmlXPathTestVal test;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007730 xmlXPathAxisVal axis = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00007731 xmlXPathTypeVal type;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007732 int op1;
Owen Taylor3473f882001-02-23 17:55:21 +00007733
7734 /*
7735 * The modification needed for XPointer change to the production
7736 */
7737#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00007738 if (ctxt->xptr) {
Owen Taylor3473f882001-02-23 17:55:21 +00007739 name = xmlXPathParseNCName(ctxt);
7740 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007741 op2 = ctxt->comp->last;
Owen Taylor3473f882001-02-23 17:55:21 +00007742 xmlFree(name);
7743 SKIP_BLANKS;
7744 if (CUR != '(') {
7745 XP_ERROR(XPATH_EXPR_ERROR);
7746 }
7747 NEXT;
7748 SKIP_BLANKS;
7749
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007750 xmlXPathCompileExpr(ctxt);
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007751 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
Owen Taylor3473f882001-02-23 17:55:21 +00007752 CHECK_ERROR;
7753
7754 SKIP_BLANKS;
7755 if (CUR != ')') {
7756 XP_ERROR(XPATH_EXPR_ERROR);
7757 }
7758 NEXT;
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007759 rangeto = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00007760 goto eval_predicates;
7761 }
7762 }
7763#endif
Daniel Veillard2156a562001-04-28 12:24:34 +00007764 if (CUR == '*') {
7765 axis = AXIS_CHILD;
7766 } else {
7767 if (name == NULL)
7768 name = xmlXPathParseNCName(ctxt);
7769 if (name != NULL) {
7770 axis = xmlXPathIsAxisName(name);
7771 if (axis != 0) {
7772 SKIP_BLANKS;
7773 if ((CUR == ':') && (NXT(1) == ':')) {
7774 SKIP(2);
7775 xmlFree(name);
7776 name = NULL;
7777 } else {
7778 /* an element name can conflict with an axis one :-\ */
7779 axis = AXIS_CHILD;
7780 }
Owen Taylor3473f882001-02-23 17:55:21 +00007781 } else {
Owen Taylor3473f882001-02-23 17:55:21 +00007782 axis = AXIS_CHILD;
7783 }
Daniel Veillard2156a562001-04-28 12:24:34 +00007784 } else if (CUR == '@') {
7785 NEXT;
7786 axis = AXIS_ATTRIBUTE;
Owen Taylor3473f882001-02-23 17:55:21 +00007787 } else {
Daniel Veillard2156a562001-04-28 12:24:34 +00007788 axis = AXIS_CHILD;
Owen Taylor3473f882001-02-23 17:55:21 +00007789 }
Owen Taylor3473f882001-02-23 17:55:21 +00007790 }
7791
7792 CHECK_ERROR;
7793
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007794 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
Owen Taylor3473f882001-02-23 17:55:21 +00007795 if (test == 0)
7796 return;
7797
7798#ifdef DEBUG_STEP
7799 xmlGenericError(xmlGenericErrorContext,
7800 "Basis : computing new set\n");
7801#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007802
Owen Taylor3473f882001-02-23 17:55:21 +00007803#ifdef DEBUG_STEP
7804 xmlGenericError(xmlGenericErrorContext, "Basis : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007805 if (ctxt->value == NULL)
7806 xmlGenericError(xmlGenericErrorContext, "no value\n");
7807 else if (ctxt->value->nodesetval == NULL)
7808 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7809 else
7810 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007811#endif
Owen Taylor3473f882001-02-23 17:55:21 +00007812
7813eval_predicates:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007814 op1 = ctxt->comp->last;
7815 ctxt->comp->last = -1;
7816
Owen Taylor3473f882001-02-23 17:55:21 +00007817 SKIP_BLANKS;
7818 while (CUR == '[') {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007819 xmlXPathCompPredicate(ctxt, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00007820 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007821
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007822#ifdef LIBXML_XPTR_ENABLED
7823 if (rangeto) {
7824 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
7825 } else
7826#endif
7827 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
7828 test, type, (void *)prefix, (void *)name);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007829
Owen Taylor3473f882001-02-23 17:55:21 +00007830 }
7831#ifdef DEBUG_STEP
7832 xmlGenericError(xmlGenericErrorContext, "Step : ");
Daniel Veillardfd0c3eb2001-04-22 19:13:10 +00007833 if (ctxt->value == NULL)
7834 xmlGenericError(xmlGenericErrorContext, "no value\n");
7835 else if (ctxt->value->nodesetval == NULL)
7836 xmlGenericError(xmlGenericErrorContext, "Empty\n");
7837 else
7838 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
7839 ctxt->value->nodesetval);
Owen Taylor3473f882001-02-23 17:55:21 +00007840#endif
7841}
7842
7843/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007844 * xmlXPathCompRelativeLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007845 * @ctxt: the XPath Parser context
7846 *
7847 * [3] RelativeLocationPath ::= Step
7848 * | RelativeLocationPath '/' Step
7849 * | AbbreviatedRelativeLocationPath
7850 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
7851 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007852 * Compile a relative location path.
Owen Taylor3473f882001-02-23 17:55:21 +00007853 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007854static void
Owen Taylor3473f882001-02-23 17:55:21 +00007855#ifdef VMS
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007856xmlXPathCompRelLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007857#else
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007858xmlXPathCompRelativeLocationPath
Owen Taylor3473f882001-02-23 17:55:21 +00007859#endif
7860(xmlXPathParserContextPtr ctxt) {
7861 SKIP_BLANKS;
7862 if ((CUR == '/') && (NXT(1) == '/')) {
7863 SKIP(2);
7864 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007865 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7866 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00007867 } else if (CUR == '/') {
7868 NEXT;
7869 SKIP_BLANKS;
7870 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007871 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007872 SKIP_BLANKS;
7873 while (CUR == '/') {
7874 if ((CUR == '/') && (NXT(1) == '/')) {
7875 SKIP(2);
7876 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007877 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
Owen Taylor3473f882001-02-23 17:55:21 +00007878 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007879 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007880 } else if (CUR == '/') {
7881 NEXT;
7882 SKIP_BLANKS;
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007883 xmlXPathCompStep(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007884 }
7885 SKIP_BLANKS;
7886 }
7887}
7888
7889/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007890 * xmlXPathCompLocationPath:
Owen Taylor3473f882001-02-23 17:55:21 +00007891 * @ctxt: the XPath Parser context
7892 *
7893 * [1] LocationPath ::= RelativeLocationPath
7894 * | AbsoluteLocationPath
7895 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
7896 * | AbbreviatedAbsoluteLocationPath
7897 * [10] AbbreviatedAbsoluteLocationPath ::=
7898 * '//' RelativeLocationPath
7899 *
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007900 * Compile a location path
7901 *
Owen Taylor3473f882001-02-23 17:55:21 +00007902 * // is short for /descendant-or-self::node()/. For example,
7903 * //para is short for /descendant-or-self::node()/child::para and
7904 * so will select any para element in the document (even a para element
7905 * that is a document element will be selected by //para since the
7906 * document element node is a child of the root node); div//para is
7907 * short for div/descendant-or-self::node()/child::para and so will
7908 * select all para descendants of div children.
7909 */
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007910static void
7911xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
Owen Taylor3473f882001-02-23 17:55:21 +00007912 SKIP_BLANKS;
7913 if (CUR != '/') {
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007914 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007915 } else {
7916 while (CUR == '/') {
7917 if ((CUR == '/') && (NXT(1) == '/')) {
7918 SKIP(2);
7919 SKIP_BLANKS;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007920 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
7921 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007922 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007923 } else if (CUR == '/') {
7924 NEXT;
Daniel Veillard608ad072001-06-14 08:32:28 +00007925 SKIP_BLANKS;
7926 if ((CUR != 0 ) &&
7927 ((IS_LETTER(CUR)) || (CUR == '_') || (CUR == '.') ||
7928 (CUR == '@') || (CUR == '*')))
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00007929 xmlXPathCompRelativeLocationPath(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00007930 }
7931 }
7932 }
7933}
7934
Daniel Veillard9e7160d2001-03-18 23:17:47 +00007935/************************************************************************
7936 * *
7937 * XPath precompiled expression evaluation *
7938 * *
7939 ************************************************************************/
7940
Daniel Veillardf06307e2001-07-03 10:35:50 +00007941static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007942xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
7943
7944/**
7945 * xmlXPathNodeCollectAndTest:
7946 * @ctxt: the XPath Parser context
7947 * @op: the XPath precompiled step operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00007948 * @first: pointer to the first element in document order
7949 * @last: pointer to the last element in document order
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007950 *
7951 * This is the function implementing a step: based on the current list
7952 * of nodes, it builds up a new list, looking at all nodes under that
7953 * axis and selecting them it also do the predicate filtering
7954 *
7955 * Pushes the new NodeSet resulting from the search.
Daniel Veillardf06307e2001-07-03 10:35:50 +00007956 *
7957 * Returns the number of node traversed
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007958 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00007959static int
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007960xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
Daniel Veillardf06307e2001-07-03 10:35:50 +00007961 xmlXPathStepOpPtr op,
7962 xmlNodePtr * first, xmlNodePtr * last)
7963{
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007964 xmlXPathAxisVal axis = op->value;
7965 xmlXPathTestVal test = op->value2;
7966 xmlXPathTypeVal type = op->value3;
7967 const xmlChar *prefix = op->value4;
7968 const xmlChar *name = op->value5;
Daniel Veillarde043ee12001-04-16 14:08:07 +00007969 const xmlChar *URI = NULL;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007970
7971#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00007972 int n = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007973#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00007974 int i, t = 0;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007975 xmlNodeSetPtr ret, list;
7976 xmlXPathTraversalFunction next = NULL;
Daniel Veillardf06307e2001-07-03 10:35:50 +00007977 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007978 xmlNodePtr cur = NULL;
7979 xmlXPathObjectPtr obj;
7980 xmlNodeSetPtr nodelist;
7981 xmlNodePtr tmp;
7982
Daniel Veillardf06307e2001-07-03 10:35:50 +00007983 CHECK_TYPE0(XPATH_NODESET);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007984 obj = valuePop(ctxt);
7985 addNode = xmlXPathNodeSetAdd;
Daniel Veillarde043ee12001-04-16 14:08:07 +00007986 if (prefix != NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00007987 URI = xmlXPathNsLookup(ctxt->context, prefix);
7988 if (URI == NULL)
7989 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
Daniel Veillarde043ee12001-04-16 14:08:07 +00007990 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007991#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00007992 xmlGenericError(xmlGenericErrorContext, "new step : ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007993#endif
7994 switch (axis) {
7995 case AXIS_ANCESTOR:
7996#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00007997 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00007998#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00007999 first = NULL;
8000 next = xmlXPathNextAncestor;
8001 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008002 case AXIS_ANCESTOR_OR_SELF:
8003#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008004 xmlGenericError(xmlGenericErrorContext,
8005 "axis 'ancestors-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008006#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008007 first = NULL;
8008 next = xmlXPathNextAncestorOrSelf;
8009 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008010 case AXIS_ATTRIBUTE:
8011#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008012 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008013#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008014 first = NULL;
8015 last = NULL;
8016 next = xmlXPathNextAttribute;
8017 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008018 case AXIS_CHILD:
8019#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008020 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008021#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008022 last = NULL;
8023 next = xmlXPathNextChild;
8024 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008025 case AXIS_DESCENDANT:
8026#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008027 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008028#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008029 last = NULL;
8030 next = xmlXPathNextDescendant;
8031 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008032 case AXIS_DESCENDANT_OR_SELF:
8033#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008034 xmlGenericError(xmlGenericErrorContext,
8035 "axis 'descendant-or-self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008036#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008037 last = NULL;
8038 next = xmlXPathNextDescendantOrSelf;
8039 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008040 case AXIS_FOLLOWING:
8041#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008042 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008043#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008044 last = NULL;
8045 next = xmlXPathNextFollowing;
8046 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008047 case AXIS_FOLLOWING_SIBLING:
8048#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008049 xmlGenericError(xmlGenericErrorContext,
8050 "axis 'following-siblings' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008051#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008052 last = NULL;
8053 next = xmlXPathNextFollowingSibling;
8054 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008055 case AXIS_NAMESPACE:
8056#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008057 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008058#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008059 first = NULL;
8060 last = NULL;
8061 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8062 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008063 case AXIS_PARENT:
8064#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008065 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008066#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008067 first = NULL;
8068 next = xmlXPathNextParent;
8069 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008070 case AXIS_PRECEDING:
8071#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008072 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008073#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008074 first = NULL;
8075 next = xmlXPathNextPrecedingInternal;
8076 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008077 case AXIS_PRECEDING_SIBLING:
8078#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008079 xmlGenericError(xmlGenericErrorContext,
8080 "axis 'preceding-sibling' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008081#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008082 first = NULL;
8083 next = xmlXPathNextPrecedingSibling;
8084 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008085 case AXIS_SELF:
8086#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008087 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008088#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008089 first = NULL;
8090 last = NULL;
8091 next = xmlXPathNextSelf;
8092 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008093 }
8094 if (next == NULL)
Daniel Veillardf06307e2001-07-03 10:35:50 +00008095 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008096
8097 nodelist = obj->nodesetval;
8098 if (nodelist == NULL) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008099 xmlXPathFreeObject(obj);
8100 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8101 return(0);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008102 }
8103 addNode = xmlXPathNodeSetAddUnique;
8104 ret = NULL;
8105#ifdef DEBUG_STEP
8106 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008107 " context contains %d nodes\n", nodelist->nodeNr);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008108 switch (test) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00008109 case NODE_TEST_NONE:
8110 xmlGenericError(xmlGenericErrorContext,
8111 " searching for none !!!\n");
8112 break;
8113 case NODE_TEST_TYPE:
8114 xmlGenericError(xmlGenericErrorContext,
8115 " searching for type %d\n", type);
8116 break;
8117 case NODE_TEST_PI:
8118 xmlGenericError(xmlGenericErrorContext,
8119 " searching for PI !!!\n");
8120 break;
8121 case NODE_TEST_ALL:
8122 xmlGenericError(xmlGenericErrorContext,
8123 " searching for *\n");
8124 break;
8125 case NODE_TEST_NS:
8126 xmlGenericError(xmlGenericErrorContext,
8127 " searching for namespace %s\n",
8128 prefix);
8129 break;
8130 case NODE_TEST_NAME:
8131 xmlGenericError(xmlGenericErrorContext,
8132 " searching for name %s\n", name);
8133 if (prefix != NULL)
8134 xmlGenericError(xmlGenericErrorContext,
8135 " with namespace %s\n", prefix);
8136 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008137 }
8138 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8139#endif
8140 /*
8141 * 2.3 Node Tests
8142 * - For the attribute axis, the principal node type is attribute.
8143 * - For the namespace axis, the principal node type is namespace.
8144 * - For other axes, the principal node type is element.
8145 *
8146 * A node test * is true for any node of the
8147 * principal node type. For example, child::* willi
8148 * select all element children of the context node
8149 */
8150 tmp = ctxt->context->node;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008151 for (i = 0; i < nodelist->nodeNr; i++) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008152 ctxt->context->node = nodelist->nodeTab[i];
8153
Daniel Veillardf06307e2001-07-03 10:35:50 +00008154 cur = NULL;
8155 list = xmlXPathNodeSetCreate(NULL);
8156 do {
8157 cur = next(ctxt, cur);
8158 if (cur == NULL)
8159 break;
8160 if ((first != NULL) && (*first == cur))
8161 break;
8162 if (((t % 256) == 0) &&
8163 (first != NULL) && (*first != NULL) &&
8164 (xmlXPathCmpNodes(*first, cur) >= 0))
8165 break;
8166 if ((last != NULL) && (*last == cur))
8167 break;
8168 if (((t % 256) == 0) &&
8169 (last != NULL) && (*last != NULL) &&
8170 (xmlXPathCmpNodes(cur, *last) >= 0))
8171 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008172 t++;
Daniel Veillardf06307e2001-07-03 10:35:50 +00008173#ifdef DEBUG_STEP
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008174 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
8175#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008176 switch (test) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008177 case NODE_TEST_NONE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008178 ctxt->context->node = tmp;
8179 STRANGE return(t);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008180 case NODE_TEST_TYPE:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008181 if ((cur->type == type) ||
8182 ((type == NODE_TYPE_NODE) &&
8183 ((cur->type == XML_DOCUMENT_NODE) ||
8184 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8185 (cur->type == XML_ELEMENT_NODE) ||
8186 (cur->type == XML_PI_NODE) ||
8187 (cur->type == XML_COMMENT_NODE) ||
8188 (cur->type == XML_CDATA_SECTION_NODE) ||
Daniel Veillard7583a592001-07-08 13:15:55 +00008189 (cur->type == XML_TEXT_NODE))) ||
8190 ((type == NODE_TYPE_TEXT) &&
8191 (cur->type == XML_CDATA_SECTION_NODE))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008192#ifdef DEBUG_STEP
8193 n++;
8194#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008195 addNode(list, cur);
8196 }
8197 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008198 case NODE_TEST_PI:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008199 if (cur->type == XML_PI_NODE) {
8200 if ((name != NULL) &&
8201 (!xmlStrEqual(name, cur->name)))
8202 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008203#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008204 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008205#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008206 addNode(list, cur);
8207 }
8208 break;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008209 case NODE_TEST_ALL:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008210 if (axis == AXIS_ATTRIBUTE) {
8211 if (cur->type == XML_ATTRIBUTE_NODE) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008212#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008213 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008214#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008215 addNode(list, cur);
8216 }
8217 } else if (axis == AXIS_NAMESPACE) {
8218 if (cur->type == XML_NAMESPACE_DECL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008219#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008220 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008221#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008222 addNode(list, cur);
8223 }
8224 } else {
8225 if (cur->type == XML_ELEMENT_NODE) {
8226 if (prefix == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008227#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008228 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008229#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008230 addNode(list, cur);
8231 } else if ((cur->ns != NULL) &&
8232 (xmlStrEqual(URI, cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008233#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008234 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008235#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008236 addNode(list, cur);
8237 }
8238 }
8239 }
8240 break;
8241 case NODE_TEST_NS:{
8242 TODO;
8243 break;
8244 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008245 case NODE_TEST_NAME:
Daniel Veillardf06307e2001-07-03 10:35:50 +00008246 switch (cur->type) {
8247 case XML_ELEMENT_NODE:
8248 if (xmlStrEqual(name, cur->name)) {
8249 if (prefix == NULL) {
8250 if (cur->ns == NULL) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008251#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008252 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008253#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008254 addNode(list, cur);
8255 }
8256 } else {
8257 if ((cur->ns != NULL) &&
8258 (xmlStrEqual(URI,
8259 cur->ns->href))) {
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008260#ifdef DEBUG_STEP
Daniel Veillardf06307e2001-07-03 10:35:50 +00008261 n++;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008262#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008263 addNode(list, cur);
8264 }
8265 }
8266 }
8267 break;
8268 case XML_ATTRIBUTE_NODE:{
8269 xmlAttrPtr attr = (xmlAttrPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008270
Daniel Veillardf06307e2001-07-03 10:35:50 +00008271 if (xmlStrEqual(name, attr->name)) {
8272 if (prefix == NULL) {
8273 if ((attr->ns == NULL) ||
8274 (attr->ns->prefix == NULL)) {
8275#ifdef DEBUG_STEP
8276 n++;
8277#endif
8278 addNode(list,
8279 (xmlNodePtr) attr);
8280 }
8281 } else {
8282 if ((attr->ns != NULL) &&
8283 (xmlStrEqual(URI,
8284 attr->ns->
8285 href))) {
8286#ifdef DEBUG_STEP
8287 n++;
8288#endif
8289 addNode(list,
8290 (xmlNodePtr) attr);
8291 }
8292 }
8293 }
8294 break;
8295 }
8296 case XML_NAMESPACE_DECL:
8297 if (cur->type == XML_NAMESPACE_DECL) {
8298 xmlNsPtr ns = (xmlNsPtr) cur;
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008299
Daniel Veillardf06307e2001-07-03 10:35:50 +00008300 if ((ns->prefix != NULL) && (name != NULL)
8301 && (xmlStrEqual(ns->prefix, name))) {
8302#ifdef DEBUG_STEP
8303 n++;
8304#endif
8305 addNode(list, cur);
8306 }
8307 }
8308 break;
8309 default:
8310 break;
8311 }
8312 break;
8313 break;
8314 }
8315 } while (cur != NULL);
8316
8317 /*
8318 * If there is some predicate filtering do it now
8319 */
8320 if (op->ch2 != -1) {
8321 xmlXPathObjectPtr obj2;
8322
8323 valuePush(ctxt, xmlXPathWrapNodeSet(list));
8324 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
8325 CHECK_TYPE0(XPATH_NODESET);
8326 obj2 = valuePop(ctxt);
8327 list = obj2->nodesetval;
8328 obj2->nodesetval = NULL;
8329 xmlXPathFreeObject(obj2);
8330 }
8331 if (ret == NULL) {
8332 ret = list;
8333 } else {
8334 ret = xmlXPathNodeSetMerge(ret, list);
8335 xmlXPathFreeNodeSet(list);
8336 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008337 }
8338 ctxt->context->node = tmp;
8339#ifdef DEBUG_STEP
8340 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00008341 "\nExamined %d nodes, found %d nodes at that step\n",
8342 t, n);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008343#endif
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008344 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00008345 if ((obj->boolval) && (obj->user != NULL)) {
8346 ctxt->value->boolval = 1;
8347 ctxt->value->user = obj->user;
8348 obj->user = NULL;
8349 obj->boolval = 0;
8350 }
8351 xmlXPathFreeObject(obj);
Daniel Veillardf06307e2001-07-03 10:35:50 +00008352 return(t);
8353}
8354
8355/**
8356 * xmlXPathNodeCollectAndTestNth:
8357 * @ctxt: the XPath Parser context
8358 * @op: the XPath precompiled step operation
8359 * @indx: the index to collect
8360 * @first: pointer to the first element in document order
8361 * @last: pointer to the last element in document order
8362 *
8363 * This is the function implementing a step: based on the current list
8364 * of nodes, it builds up a new list, looking at all nodes under that
8365 * axis and selecting them it also do the predicate filtering
8366 *
8367 * Pushes the new NodeSet resulting from the search.
8368 * Returns the number of node traversed
8369 */
8370static int
8371xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
8372 xmlXPathStepOpPtr op, int indx,
8373 xmlNodePtr * first, xmlNodePtr * last)
8374{
8375 xmlXPathAxisVal axis = op->value;
8376 xmlXPathTestVal test = op->value2;
8377 xmlXPathTypeVal type = op->value3;
8378 const xmlChar *prefix = op->value4;
8379 const xmlChar *name = op->value5;
8380 const xmlChar *URI = NULL;
8381 int n = 0, t = 0;
8382
8383 int i;
8384 xmlNodeSetPtr list;
8385 xmlXPathTraversalFunction next = NULL;
8386 void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
8387 xmlNodePtr cur = NULL;
8388 xmlXPathObjectPtr obj;
8389 xmlNodeSetPtr nodelist;
8390 xmlNodePtr tmp;
8391
8392 CHECK_TYPE0(XPATH_NODESET);
8393 obj = valuePop(ctxt);
8394 addNode = xmlXPathNodeSetAdd;
8395 if (prefix != NULL) {
8396 URI = xmlXPathNsLookup(ctxt->context, prefix);
8397 if (URI == NULL)
8398 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
8399 }
8400#ifdef DEBUG_STEP_NTH
8401 xmlGenericError(xmlGenericErrorContext, "new step : ");
8402 if (first != NULL) {
8403 if (*first != NULL)
8404 xmlGenericError(xmlGenericErrorContext, "first = %s ",
8405 (*first)->name);
8406 else
8407 xmlGenericError(xmlGenericErrorContext, "first = NULL ");
8408 }
8409 if (last != NULL) {
8410 if (*last != NULL)
8411 xmlGenericError(xmlGenericErrorContext, "last = %s ",
8412 (*last)->name);
8413 else
8414 xmlGenericError(xmlGenericErrorContext, "last = NULL ");
8415 }
8416#endif
8417 switch (axis) {
8418 case AXIS_ANCESTOR:
8419#ifdef DEBUG_STEP_NTH
8420 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
8421#endif
8422 first = NULL;
8423 next = xmlXPathNextAncestor;
8424 break;
8425 case AXIS_ANCESTOR_OR_SELF:
8426#ifdef DEBUG_STEP_NTH
8427 xmlGenericError(xmlGenericErrorContext,
8428 "axis 'ancestors-or-self' ");
8429#endif
8430 first = NULL;
8431 next = xmlXPathNextAncestorOrSelf;
8432 break;
8433 case AXIS_ATTRIBUTE:
8434#ifdef DEBUG_STEP_NTH
8435 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
8436#endif
8437 first = NULL;
8438 last = NULL;
8439 next = xmlXPathNextAttribute;
8440 break;
8441 case AXIS_CHILD:
8442#ifdef DEBUG_STEP_NTH
8443 xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
8444#endif
8445 last = NULL;
8446 next = xmlXPathNextChild;
8447 break;
8448 case AXIS_DESCENDANT:
8449#ifdef DEBUG_STEP_NTH
8450 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
8451#endif
8452 last = NULL;
8453 next = xmlXPathNextDescendant;
8454 break;
8455 case AXIS_DESCENDANT_OR_SELF:
8456#ifdef DEBUG_STEP_NTH
8457 xmlGenericError(xmlGenericErrorContext,
8458 "axis 'descendant-or-self' ");
8459#endif
8460 last = NULL;
8461 next = xmlXPathNextDescendantOrSelf;
8462 break;
8463 case AXIS_FOLLOWING:
8464#ifdef DEBUG_STEP_NTH
8465 xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
8466#endif
8467 last = NULL;
8468 next = xmlXPathNextFollowing;
8469 break;
8470 case AXIS_FOLLOWING_SIBLING:
8471#ifdef DEBUG_STEP_NTH
8472 xmlGenericError(xmlGenericErrorContext,
8473 "axis 'following-siblings' ");
8474#endif
8475 last = NULL;
8476 next = xmlXPathNextFollowingSibling;
8477 break;
8478 case AXIS_NAMESPACE:
8479#ifdef DEBUG_STEP_NTH
8480 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
8481#endif
8482 last = NULL;
8483 first = NULL;
8484 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
8485 break;
8486 case AXIS_PARENT:
8487#ifdef DEBUG_STEP_NTH
8488 xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
8489#endif
8490 first = NULL;
8491 next = xmlXPathNextParent;
8492 break;
8493 case AXIS_PRECEDING:
8494#ifdef DEBUG_STEP_NTH
8495 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
8496#endif
8497 first = NULL;
8498 next = xmlXPathNextPrecedingInternal;
8499 break;
8500 case AXIS_PRECEDING_SIBLING:
8501#ifdef DEBUG_STEP_NTH
8502 xmlGenericError(xmlGenericErrorContext,
8503 "axis 'preceding-sibling' ");
8504#endif
8505 first = NULL;
8506 next = xmlXPathNextPrecedingSibling;
8507 break;
8508 case AXIS_SELF:
8509#ifdef DEBUG_STEP_NTH
8510 xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
8511#endif
8512 first = NULL;
8513 last = NULL;
8514 next = xmlXPathNextSelf;
8515 break;
8516 }
8517 if (next == NULL)
8518 return(0);
8519
8520 nodelist = obj->nodesetval;
8521 if (nodelist == NULL) {
8522 xmlXPathFreeObject(obj);
8523 valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
8524 return(0);
8525 }
8526 addNode = xmlXPathNodeSetAddUnique;
8527#ifdef DEBUG_STEP_NTH
8528 xmlGenericError(xmlGenericErrorContext,
8529 " context contains %d nodes\n", nodelist->nodeNr);
8530 switch (test) {
8531 case NODE_TEST_NONE:
8532 xmlGenericError(xmlGenericErrorContext,
8533 " searching for none !!!\n");
8534 break;
8535 case NODE_TEST_TYPE:
8536 xmlGenericError(xmlGenericErrorContext,
8537 " searching for type %d\n", type);
8538 break;
8539 case NODE_TEST_PI:
8540 xmlGenericError(xmlGenericErrorContext,
8541 " searching for PI !!!\n");
8542 break;
8543 case NODE_TEST_ALL:
8544 xmlGenericError(xmlGenericErrorContext,
8545 " searching for *\n");
8546 break;
8547 case NODE_TEST_NS:
8548 xmlGenericError(xmlGenericErrorContext,
8549 " searching for namespace %s\n",
8550 prefix);
8551 break;
8552 case NODE_TEST_NAME:
8553 xmlGenericError(xmlGenericErrorContext,
8554 " searching for name %s\n", name);
8555 if (prefix != NULL)
8556 xmlGenericError(xmlGenericErrorContext,
8557 " with namespace %s\n", prefix);
8558 break;
8559 }
8560 xmlGenericError(xmlGenericErrorContext, "Testing : ");
8561#endif
8562 /*
8563 * 2.3 Node Tests
8564 * - For the attribute axis, the principal node type is attribute.
8565 * - For the namespace axis, the principal node type is namespace.
8566 * - For other axes, the principal node type is element.
8567 *
8568 * A node test * is true for any node of the
8569 * principal node type. For example, child::* willi
8570 * select all element children of the context node
8571 */
8572 tmp = ctxt->context->node;
8573 list = xmlXPathNodeSetCreate(NULL);
8574 for (i = 0; i < nodelist->nodeNr; i++) {
8575 ctxt->context->node = nodelist->nodeTab[i];
8576
8577 cur = NULL;
8578 n = 0;
8579 do {
8580 cur = next(ctxt, cur);
8581 if (cur == NULL)
8582 break;
8583 if ((first != NULL) && (*first == cur))
8584 break;
8585 if (((t % 256) == 0) &&
8586 (first != NULL) && (*first != NULL) &&
8587 (xmlXPathCmpNodes(*first, cur) >= 0))
8588 break;
8589 if ((last != NULL) && (*last == cur))
8590 break;
8591 if (((t % 256) == 0) &&
8592 (last != NULL) && (*last != NULL) &&
8593 (xmlXPathCmpNodes(cur, *last) >= 0))
8594 break;
8595 t++;
8596 switch (test) {
8597 case NODE_TEST_NONE:
8598 ctxt->context->node = tmp;
8599 STRANGE return(0);
8600 case NODE_TEST_TYPE:
8601 if ((cur->type == type) ||
8602 ((type == NODE_TYPE_NODE) &&
8603 ((cur->type == XML_DOCUMENT_NODE) ||
8604 (cur->type == XML_HTML_DOCUMENT_NODE) ||
8605 (cur->type == XML_ELEMENT_NODE) ||
8606 (cur->type == XML_PI_NODE) ||
8607 (cur->type == XML_COMMENT_NODE) ||
8608 (cur->type == XML_CDATA_SECTION_NODE) ||
8609 (cur->type == XML_TEXT_NODE)))) {
8610 n++;
8611 if (n == indx)
8612 addNode(list, cur);
8613 }
8614 break;
8615 case NODE_TEST_PI:
8616 if (cur->type == XML_PI_NODE) {
8617 if ((name != NULL) &&
8618 (!xmlStrEqual(name, cur->name)))
8619 break;
8620 n++;
8621 if (n == indx)
8622 addNode(list, cur);
8623 }
8624 break;
8625 case NODE_TEST_ALL:
8626 if (axis == AXIS_ATTRIBUTE) {
8627 if (cur->type == XML_ATTRIBUTE_NODE) {
8628 n++;
8629 if (n == indx)
8630 addNode(list, cur);
8631 }
8632 } else if (axis == AXIS_NAMESPACE) {
8633 if (cur->type == XML_NAMESPACE_DECL) {
8634 n++;
8635 if (n == indx)
8636 addNode(list, cur);
8637 }
8638 } else {
8639 if (cur->type == XML_ELEMENT_NODE) {
8640 if (prefix == NULL) {
8641 n++;
8642 if (n == indx)
8643 addNode(list, cur);
8644 } else if ((cur->ns != NULL) &&
8645 (xmlStrEqual(URI, cur->ns->href))) {
8646 n++;
8647 if (n == indx)
8648 addNode(list, cur);
8649 }
8650 }
8651 }
8652 break;
8653 case NODE_TEST_NS:{
8654 TODO;
8655 break;
8656 }
8657 case NODE_TEST_NAME:
8658 switch (cur->type) {
8659 case XML_ELEMENT_NODE:
8660 if (xmlStrEqual(name, cur->name)) {
8661 if (prefix == NULL) {
8662 if (cur->ns == NULL) {
8663 n++;
8664 if (n == indx)
8665 addNode(list, cur);
8666 }
8667 } else {
8668 if ((cur->ns != NULL) &&
8669 (xmlStrEqual(URI,
8670 cur->ns->href))) {
8671 n++;
8672 if (n == indx)
8673 addNode(list, cur);
8674 }
8675 }
8676 }
8677 break;
8678 case XML_ATTRIBUTE_NODE:{
8679 xmlAttrPtr attr = (xmlAttrPtr) cur;
8680
8681 if (xmlStrEqual(name, attr->name)) {
8682 if (prefix == NULL) {
8683 if ((attr->ns == NULL) ||
8684 (attr->ns->prefix == NULL)) {
8685 n++;
8686 if (n == indx)
8687 addNode(list, cur);
8688 }
8689 } else {
8690 if ((attr->ns != NULL) &&
8691 (xmlStrEqual(URI,
8692 attr->ns->
8693 href))) {
8694 n++;
8695 if (n == indx)
8696 addNode(list, cur);
8697 }
8698 }
8699 }
8700 break;
8701 }
8702 case XML_NAMESPACE_DECL:
8703 if (cur->type == XML_NAMESPACE_DECL) {
8704 xmlNsPtr ns = (xmlNsPtr) cur;
8705
8706 if ((ns->prefix != NULL) && (name != NULL)
8707 && (xmlStrEqual(ns->prefix, name))) {
8708 n++;
8709 if (n == indx)
8710 addNode(list, cur);
8711 }
8712 }
8713 break;
8714 default:
8715 break;
8716 }
8717 break;
8718 break;
8719 }
8720 } while (n < indx);
8721 }
8722 ctxt->context->node = tmp;
8723#ifdef DEBUG_STEP_NTH
8724 xmlGenericError(xmlGenericErrorContext,
8725 "\nExamined %d nodes, found %d nodes at that step\n",
8726 t, list->nodeNr);
8727#endif
Daniel Veillardf06307e2001-07-03 10:35:50 +00008728 valuePush(ctxt, xmlXPathWrapNodeSet(list));
Daniel Veillard0ab5cab2001-08-14 16:43:10 +00008729 if ((obj->boolval) && (obj->user != NULL)) {
8730 ctxt->value->boolval = 1;
8731 ctxt->value->user = obj->user;
8732 obj->user = NULL;
8733 obj->boolval = 0;
8734 }
8735 xmlXPathFreeObject(obj);
Daniel Veillardf06307e2001-07-03 10:35:50 +00008736 return(t);
8737}
8738
8739/**
8740 * xmlXPathCompOpEvalFirst:
8741 * @ctxt: the XPath parser context with the compiled expression
8742 * @op: an XPath compiled operation
8743 * @first: the first elem found so far
8744 *
8745 * Evaluate the Precompiled XPath operation searching only the first
8746 * element in document order
8747 *
8748 * Returns the number of examined objects.
8749 */
8750static int
8751xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
8752 xmlXPathStepOpPtr op, xmlNodePtr * first)
8753{
8754 int total = 0, cur;
8755 xmlXPathCompExprPtr comp;
8756 xmlXPathObjectPtr arg1, arg2;
8757
8758 comp = ctxt->comp;
8759 switch (op->op) {
8760 case XPATH_OP_END:
8761 return (0);
8762 case XPATH_OP_UNION:
8763 total =
8764 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8765 first);
8766 if ((ctxt->value != NULL)
8767 && (ctxt->value->type == XPATH_NODESET)
8768 && (ctxt->value->nodesetval != NULL)
8769 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8770 /*
8771 * limit tree traversing to first node in the result
8772 */
8773 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8774 *first = ctxt->value->nodesetval->nodeTab[0];
8775 }
8776 cur =
8777 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
8778 first);
8779 CHECK_TYPE0(XPATH_NODESET);
8780 arg2 = valuePop(ctxt);
8781
8782 CHECK_TYPE0(XPATH_NODESET);
8783 arg1 = valuePop(ctxt);
8784
8785 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8786 arg2->nodesetval);
8787 valuePush(ctxt, arg1);
8788 xmlXPathFreeObject(arg2);
8789 /* optimizer */
8790 if (total > cur)
8791 xmlXPathCompSwap(op);
8792 return (total + cur);
8793 case XPATH_OP_ROOT:
8794 xmlXPathRoot(ctxt);
8795 return (0);
8796 case XPATH_OP_NODE:
8797 if (op->ch1 != -1)
8798 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8799 if (op->ch2 != -1)
8800 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8801 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8802 return (total);
8803 case XPATH_OP_RESET:
8804 if (op->ch1 != -1)
8805 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8806 if (op->ch2 != -1)
8807 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8808 ctxt->context->node = NULL;
8809 return (total);
8810 case XPATH_OP_COLLECT:{
8811 if (op->ch1 == -1)
8812 return (total);
8813
8814 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8815
8816 /*
8817 * Optimization for [n] selection where n is a number
8818 */
8819 if ((op->ch2 != -1) &&
8820 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8821 (comp->steps[op->ch2].ch1 == -1) &&
8822 (comp->steps[op->ch2].ch2 != -1) &&
8823 (comp->steps[comp->steps[op->ch2].ch2].op ==
8824 XPATH_OP_VALUE)) {
8825 xmlXPathObjectPtr val;
8826
8827 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8828 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8829 int indx = (int) val->floatval;
8830
8831 if (val->floatval == (float) indx) {
8832 xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
8833 first, NULL);
8834 return (total);
8835 }
8836 }
8837 }
8838 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
8839 return (total);
8840 }
8841 case XPATH_OP_VALUE:
8842 valuePush(ctxt,
8843 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8844 return (0);
8845 case XPATH_OP_SORT:
8846 if (op->ch1 != -1)
8847 total +=
8848 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
8849 first);
8850 if ((ctxt->value != NULL)
8851 && (ctxt->value->type == XPATH_NODESET)
8852 && (ctxt->value->nodesetval != NULL))
8853 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8854 return (total);
8855 default:
8856 return (xmlXPathCompOpEval(ctxt, op));
8857 }
8858}
8859
8860/**
8861 * xmlXPathCompOpEvalLast:
8862 * @ctxt: the XPath parser context with the compiled expression
8863 * @op: an XPath compiled operation
8864 * @last: the last elem found so far
8865 *
8866 * Evaluate the Precompiled XPath operation searching only the last
8867 * element in document order
8868 *
8869 * Returns the number of node traversed
8870 */
8871static int
8872xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
8873 xmlNodePtr * last)
8874{
8875 int total = 0, cur;
8876 xmlXPathCompExprPtr comp;
8877 xmlXPathObjectPtr arg1, arg2;
8878
8879 comp = ctxt->comp;
8880 switch (op->op) {
8881 case XPATH_OP_END:
8882 return (0);
8883 case XPATH_OP_UNION:
8884 total =
8885 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
8886 if ((ctxt->value != NULL)
8887 && (ctxt->value->type == XPATH_NODESET)
8888 && (ctxt->value->nodesetval != NULL)
8889 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8890 /*
8891 * limit tree traversing to first node in the result
8892 */
8893 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8894 *last =
8895 ctxt->value->nodesetval->nodeTab[ctxt->value->
8896 nodesetval->nodeNr -
8897 1];
8898 }
8899 cur =
8900 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
8901 if ((ctxt->value != NULL)
8902 && (ctxt->value->type == XPATH_NODESET)
8903 && (ctxt->value->nodesetval != NULL)
8904 && (ctxt->value->nodesetval->nodeNr >= 1)) {
8905 }
8906 CHECK_TYPE0(XPATH_NODESET);
8907 arg2 = valuePop(ctxt);
8908
8909 CHECK_TYPE0(XPATH_NODESET);
8910 arg1 = valuePop(ctxt);
8911
8912 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
8913 arg2->nodesetval);
8914 valuePush(ctxt, arg1);
8915 xmlXPathFreeObject(arg2);
8916 /* optimizer */
8917 if (total > cur)
8918 xmlXPathCompSwap(op);
8919 return (total + cur);
8920 case XPATH_OP_ROOT:
8921 xmlXPathRoot(ctxt);
8922 return (0);
8923 case XPATH_OP_NODE:
8924 if (op->ch1 != -1)
8925 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8926 if (op->ch2 != -1)
8927 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8928 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
8929 return (total);
8930 case XPATH_OP_RESET:
8931 if (op->ch1 != -1)
8932 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8933 if (op->ch2 != -1)
8934 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
8935 ctxt->context->node = NULL;
8936 return (total);
8937 case XPATH_OP_COLLECT:{
8938 if (op->ch1 == -1)
8939 return (0);
8940
8941 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
8942
8943 /*
8944 * Optimization for [n] selection where n is a number
8945 */
8946 if ((op->ch2 != -1) &&
8947 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
8948 (comp->steps[op->ch2].ch1 == -1) &&
8949 (comp->steps[op->ch2].ch2 != -1) &&
8950 (comp->steps[comp->steps[op->ch2].ch2].op ==
8951 XPATH_OP_VALUE)) {
8952 xmlXPathObjectPtr val;
8953
8954 val = comp->steps[comp->steps[op->ch2].ch2].value4;
8955 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
8956 int indx = (int) val->floatval;
8957
8958 if (val->floatval == (float) indx) {
8959 total +=
8960 xmlXPathNodeCollectAndTestNth(ctxt, op,
8961 indx, NULL,
8962 last);
8963 return (total);
8964 }
8965 }
8966 }
8967 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
8968 return (total);
8969 }
8970 case XPATH_OP_VALUE:
8971 valuePush(ctxt,
8972 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
8973 return (0);
8974 case XPATH_OP_SORT:
8975 if (op->ch1 != -1)
8976 total +=
8977 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1],
8978 last);
8979 if ((ctxt->value != NULL)
8980 && (ctxt->value->type == XPATH_NODESET)
8981 && (ctxt->value->nodesetval != NULL))
8982 xmlXPathNodeSetSort(ctxt->value->nodesetval);
8983 return (total);
8984 default:
8985 return (xmlXPathCompOpEval(ctxt, op));
8986 }
Daniel Veillardd8df6c02001-04-05 16:54:14 +00008987}
8988
Owen Taylor3473f882001-02-23 17:55:21 +00008989/**
Daniel Veillard9e7160d2001-03-18 23:17:47 +00008990 * xmlXPathCompOpEval:
8991 * @ctxt: the XPath parser context with the compiled expression
8992 * @op: an XPath compiled operation
8993 *
8994 * Evaluate the Precompiled XPath operation
Daniel Veillardf06307e2001-07-03 10:35:50 +00008995 * Returns the number of node traversed
Daniel Veillard9e7160d2001-03-18 23:17:47 +00008996 */
Daniel Veillardf06307e2001-07-03 10:35:50 +00008997static int
8998xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
8999{
9000 int total = 0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009001 int equal, ret;
9002 xmlXPathCompExprPtr comp;
9003 xmlXPathObjectPtr arg1, arg2;
9004
9005 comp = ctxt->comp;
9006 switch (op->op) {
Daniel Veillardf06307e2001-07-03 10:35:50 +00009007 case XPATH_OP_END:
9008 return (0);
9009 case XPATH_OP_AND:
9010 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9011 xmlXPathBooleanFunction(ctxt, 1);
9012 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
9013 return (total);
9014 arg2 = valuePop(ctxt);
9015 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9016 xmlXPathBooleanFunction(ctxt, 1);
9017 arg1 = valuePop(ctxt);
9018 arg1->boolval &= arg2->boolval;
9019 valuePush(ctxt, arg1);
9020 xmlXPathFreeObject(arg2);
9021 return (total);
9022 case XPATH_OP_OR:
9023 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9024 xmlXPathBooleanFunction(ctxt, 1);
9025 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
9026 return (total);
9027 arg2 = valuePop(ctxt);
9028 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9029 xmlXPathBooleanFunction(ctxt, 1);
9030 arg1 = valuePop(ctxt);
9031 arg1->boolval |= arg2->boolval;
9032 valuePush(ctxt, arg1);
9033 xmlXPathFreeObject(arg2);
9034 return (total);
9035 case XPATH_OP_EQUAL:
9036 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9037 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9038 equal = xmlXPathEqualValues(ctxt);
9039 if (op->value)
9040 valuePush(ctxt, xmlXPathNewBoolean(equal));
9041 else
9042 valuePush(ctxt, xmlXPathNewBoolean(!equal));
9043 return (total);
9044 case XPATH_OP_CMP:
9045 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9046 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9047 ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
9048 valuePush(ctxt, xmlXPathNewBoolean(ret));
9049 return (total);
9050 case XPATH_OP_PLUS:
9051 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9052 if (op->ch2 != -1)
9053 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9054 if (op->value == 0)
9055 xmlXPathSubValues(ctxt);
9056 else if (op->value == 1)
9057 xmlXPathAddValues(ctxt);
9058 else if (op->value == 2)
9059 xmlXPathValueFlipSign(ctxt);
9060 else if (op->value == 3) {
9061 CAST_TO_NUMBER;
9062 CHECK_TYPE0(XPATH_NUMBER);
9063 }
9064 return (total);
9065 case XPATH_OP_MULT:
9066 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9067 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9068 if (op->value == 0)
9069 xmlXPathMultValues(ctxt);
9070 else if (op->value == 1)
9071 xmlXPathDivValues(ctxt);
9072 else if (op->value == 2)
9073 xmlXPathModValues(ctxt);
9074 return (total);
9075 case XPATH_OP_UNION:
9076 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9077 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9078 CHECK_TYPE0(XPATH_NODESET);
9079 arg2 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009080
Daniel Veillardf06307e2001-07-03 10:35:50 +00009081 CHECK_TYPE0(XPATH_NODESET);
9082 arg1 = valuePop(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009083
Daniel Veillardf06307e2001-07-03 10:35:50 +00009084 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
9085 arg2->nodesetval);
9086 valuePush(ctxt, arg1);
9087 xmlXPathFreeObject(arg2);
9088 return (total);
9089 case XPATH_OP_ROOT:
9090 xmlXPathRoot(ctxt);
9091 return (total);
9092 case XPATH_OP_NODE:
9093 if (op->ch1 != -1)
9094 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9095 if (op->ch2 != -1)
9096 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9097 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
9098 return (total);
9099 case XPATH_OP_RESET:
9100 if (op->ch1 != -1)
9101 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9102 if (op->ch2 != -1)
9103 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9104 ctxt->context->node = NULL;
9105 return (total);
9106 case XPATH_OP_COLLECT:{
9107 if (op->ch1 == -1)
9108 return (total);
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009109
Daniel Veillardf06307e2001-07-03 10:35:50 +00009110 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009111
Daniel Veillardf06307e2001-07-03 10:35:50 +00009112 /*
9113 * Optimization for [n] selection where n is a number
9114 */
9115 if ((op->ch2 != -1) &&
9116 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
9117 (comp->steps[op->ch2].ch1 == -1) &&
9118 (comp->steps[op->ch2].ch2 != -1) &&
9119 (comp->steps[comp->steps[op->ch2].ch2].op ==
9120 XPATH_OP_VALUE)) {
9121 xmlXPathObjectPtr val;
Daniel Veillard42596ad2001-05-22 16:57:14 +00009122
Daniel Veillardf06307e2001-07-03 10:35:50 +00009123 val = comp->steps[comp->steps[op->ch2].ch2].value4;
9124 if ((val != NULL) && (val->type == XPATH_NUMBER)) {
9125 int indx = (int) val->floatval;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009126
Daniel Veillardf06307e2001-07-03 10:35:50 +00009127 if (val->floatval == (float) indx) {
9128 total +=
9129 xmlXPathNodeCollectAndTestNth(ctxt, op,
9130 indx, NULL,
9131 NULL);
9132 return (total);
9133 }
9134 }
9135 }
9136 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
9137 return (total);
9138 }
9139 case XPATH_OP_VALUE:
9140 valuePush(ctxt,
9141 xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
9142 return (total);
9143 case XPATH_OP_VARIABLE:{
9144 if (op->ch1 != -1)
9145 total +=
9146 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9147 if (op->value5 == NULL)
9148 valuePush(ctxt,
9149 xmlXPathVariableLookup(ctxt->context,
9150 op->value4));
9151 else {
9152 const xmlChar *URI;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009153
Daniel Veillardf06307e2001-07-03 10:35:50 +00009154 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9155 if (URI == NULL) {
9156 xmlGenericError(xmlGenericErrorContext,
9157 "xmlXPathRunEval: variable %s bound to undefined prefix %s\n",
9158 op->value4, op->value5);
9159 return (total);
9160 }
9161 valuePush(ctxt,
9162 xmlXPathVariableLookupNS(ctxt->context,
9163 op->value4, URI));
9164 }
9165 return (total);
9166 }
9167 case XPATH_OP_FUNCTION:{
9168 xmlXPathFunction func;
9169 const xmlChar *oldFunc, *oldFuncURI;
9170
9171 if (op->ch1 != -1)
9172 total +=
9173 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9174 if (op->cache != NULL)
9175 func = (xmlXPathFunction) op->cache;
9176 else {
9177 const xmlChar *URI = NULL;
9178
9179 if (op->value5 == NULL)
9180 func =
9181 xmlXPathFunctionLookup(ctxt->context,
9182 op->value4);
9183 else {
9184 URI = xmlXPathNsLookup(ctxt->context, op->value5);
9185 if (URI == NULL) {
9186 xmlGenericError(xmlGenericErrorContext,
9187 "xmlXPathRunEval: function %s bound to undefined prefix %s\n",
9188 op->value4, op->value5);
9189 return (total);
9190 }
9191 func = xmlXPathFunctionLookupNS(ctxt->context,
9192 op->value4, URI);
9193 }
9194 if (func == NULL) {
9195 xmlGenericError(xmlGenericErrorContext,
9196 "xmlXPathRunEval: function %s not found\n",
9197 op->value4);
9198 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
9199 return (total);
9200 }
9201 op->cache = (void *) func;
9202 op->cacheURI = (void *) URI;
9203 }
9204 oldFunc = ctxt->context->function;
9205 oldFuncURI = ctxt->context->functionURI;
9206 ctxt->context->function = op->value4;
9207 ctxt->context->functionURI = op->cacheURI;
9208 func(ctxt, op->value);
9209 ctxt->context->function = oldFunc;
9210 ctxt->context->functionURI = oldFuncURI;
9211 return (total);
9212 }
9213 case XPATH_OP_ARG:
9214 if (op->ch1 != -1)
9215 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9216 if (op->ch2 != -1)
9217 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
9218 return (total);
9219 case XPATH_OP_PREDICATE:
9220 case XPATH_OP_FILTER:{
9221 xmlXPathObjectPtr res;
9222 xmlXPathObjectPtr obj, tmp;
9223 xmlNodeSetPtr newset = NULL;
9224 xmlNodeSetPtr oldset;
9225 xmlNodePtr oldnode;
9226 int i;
9227
9228 /*
9229 * Optimization for ()[1] selection i.e. the first elem
9230 */
9231 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9232 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9233 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
9234 xmlXPathObjectPtr val;
9235
9236 val = comp->steps[op->ch2].value4;
9237 if ((val != NULL) && (val->type == XPATH_NUMBER) &&
9238 (val->floatval == 1.0)) {
9239 xmlNodePtr first = NULL;
9240
9241 total +=
9242 xmlXPathCompOpEvalFirst(ctxt,
9243 &comp->steps[op->ch1],
9244 &first);
9245 /*
9246 * The nodeset should be in document order,
9247 * Keep only the first value
9248 */
9249 if ((ctxt->value != NULL) &&
9250 (ctxt->value->type == XPATH_NODESET) &&
9251 (ctxt->value->nodesetval != NULL) &&
9252 (ctxt->value->nodesetval->nodeNr > 1))
9253 ctxt->value->nodesetval->nodeNr = 1;
9254 return (total);
9255 }
9256 }
9257 /*
9258 * Optimization for ()[last()] selection i.e. the last elem
9259 */
9260 if ((op->ch1 != -1) && (op->ch2 != -1) &&
9261 (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
9262 (comp->steps[op->ch2].op == XPATH_OP_SORT)) {
9263 int f = comp->steps[op->ch2].ch1;
9264
9265 if ((f != -1) &&
9266 (comp->steps[f].op == XPATH_OP_FUNCTION) &&
9267 (comp->steps[f].value5 == NULL) &&
9268 (comp->steps[f].value == 0) &&
9269 (comp->steps[f].value4 != NULL) &&
9270 (xmlStrEqual
9271 (comp->steps[f].value4, BAD_CAST "last"))) {
9272 xmlNodePtr last = NULL;
9273
9274 total +=
9275 xmlXPathCompOpEvalLast(ctxt,
9276 &comp->steps[op->ch1],
9277 &last);
9278 /*
9279 * The nodeset should be in document order,
9280 * Keep only the last value
9281 */
9282 if ((ctxt->value != NULL) &&
9283 (ctxt->value->type == XPATH_NODESET) &&
9284 (ctxt->value->nodesetval != NULL) &&
9285 (ctxt->value->nodesetval->nodeTab != NULL) &&
9286 (ctxt->value->nodesetval->nodeNr > 1)) {
9287 ctxt->value->nodesetval->nodeTab[0] =
9288 ctxt->value->nodesetval->nodeTab[ctxt->
9289 value->
9290 nodesetval->
9291 nodeNr -
9292 1];
9293 ctxt->value->nodesetval->nodeNr = 1;
9294 }
9295 return (total);
9296 }
9297 }
9298
9299 if (op->ch1 != -1)
9300 total +=
9301 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9302 if (op->ch2 == -1)
9303 return (total);
9304 if (ctxt->value == NULL)
9305 return (total);
9306
9307 oldnode = ctxt->context->node;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009308
9309#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009310 /*
9311 * Hum are we filtering the result of an XPointer expression
9312 */
9313 if (ctxt->value->type == XPATH_LOCATIONSET) {
9314 xmlLocationSetPtr newlocset = NULL;
9315 xmlLocationSetPtr oldlocset;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009316
Daniel Veillardf06307e2001-07-03 10:35:50 +00009317 /*
9318 * Extract the old locset, and then evaluate the result of the
9319 * expression for all the element in the locset. use it to grow
9320 * up a new locset.
9321 */
9322 CHECK_TYPE0(XPATH_LOCATIONSET);
9323 obj = valuePop(ctxt);
9324 oldlocset = obj->user;
9325 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009326
Daniel Veillardf06307e2001-07-03 10:35:50 +00009327 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
9328 ctxt->context->contextSize = 0;
9329 ctxt->context->proximityPosition = 0;
9330 if (op->ch2 != -1)
9331 total +=
9332 xmlXPathCompOpEval(ctxt,
9333 &comp->steps[op->ch2]);
9334 res = valuePop(ctxt);
9335 if (res != NULL)
9336 xmlXPathFreeObject(res);
9337 valuePush(ctxt, obj);
9338 CHECK_ERROR0;
9339 return (total);
9340 }
9341 newlocset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009342
Daniel Veillardf06307e2001-07-03 10:35:50 +00009343 for (i = 0; i < oldlocset->locNr; i++) {
9344 /*
9345 * Run the evaluation with a node list made of a
9346 * single item in the nodelocset.
9347 */
9348 ctxt->context->node = oldlocset->locTab[i]->user;
9349 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9350 valuePush(ctxt, tmp);
9351 ctxt->context->contextSize = oldlocset->locNr;
9352 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009353
Daniel Veillardf06307e2001-07-03 10:35:50 +00009354 if (op->ch2 != -1)
9355 total +=
9356 xmlXPathCompOpEval(ctxt,
9357 &comp->steps[op->ch2]);
9358 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009359
Daniel Veillardf06307e2001-07-03 10:35:50 +00009360 /*
9361 * The result of the evaluation need to be tested to
9362 * decided whether the filter succeeded or not
9363 */
9364 res = valuePop(ctxt);
9365 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9366 xmlXPtrLocationSetAdd(newlocset,
9367 xmlXPathObjectCopy
9368 (oldlocset->locTab[i]));
9369 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009370
Daniel Veillardf06307e2001-07-03 10:35:50 +00009371 /*
9372 * Cleanup
9373 */
9374 if (res != NULL)
9375 xmlXPathFreeObject(res);
9376 if (ctxt->value == tmp) {
9377 res = valuePop(ctxt);
9378 xmlXPathFreeObject(res);
9379 }
9380
9381 ctxt->context->node = NULL;
9382 }
9383
9384 /*
9385 * The result is used as the new evaluation locset.
9386 */
9387 xmlXPathFreeObject(obj);
9388 ctxt->context->node = NULL;
9389 ctxt->context->contextSize = -1;
9390 ctxt->context->proximityPosition = -1;
9391 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
9392 ctxt->context->node = oldnode;
9393 return (total);
9394 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009395#endif /* LIBXML_XPTR_ENABLED */
9396
Daniel Veillardf06307e2001-07-03 10:35:50 +00009397 /*
9398 * Extract the old set, and then evaluate the result of the
9399 * expression for all the element in the set. use it to grow
9400 * up a new set.
9401 */
9402 CHECK_TYPE0(XPATH_NODESET);
9403 obj = valuePop(ctxt);
9404 oldset = obj->nodesetval;
Daniel Veillard911f49a2001-04-07 15:39:35 +00009405
Daniel Veillardf06307e2001-07-03 10:35:50 +00009406 oldnode = ctxt->context->node;
9407 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009408
Daniel Veillardf06307e2001-07-03 10:35:50 +00009409 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
9410 ctxt->context->contextSize = 0;
9411 ctxt->context->proximityPosition = 0;
9412 if (op->ch2 != -1)
9413 total +=
9414 xmlXPathCompOpEval(ctxt,
9415 &comp->steps[op->ch2]);
9416 res = valuePop(ctxt);
9417 if (res != NULL)
9418 xmlXPathFreeObject(res);
9419 valuePush(ctxt, obj);
9420 ctxt->context->node = oldnode;
9421 CHECK_ERROR0;
9422 } else {
9423 /*
9424 * Initialize the new set.
9425 */
9426 newset = xmlXPathNodeSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009427
Daniel Veillardf06307e2001-07-03 10:35:50 +00009428 for (i = 0; i < oldset->nodeNr; i++) {
9429 /*
9430 * Run the evaluation with a node list made of
9431 * a single item in the nodeset.
9432 */
9433 ctxt->context->node = oldset->nodeTab[i];
9434 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9435 valuePush(ctxt, tmp);
9436 ctxt->context->contextSize = oldset->nodeNr;
9437 ctxt->context->proximityPosition = i + 1;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009438
Daniel Veillardf06307e2001-07-03 10:35:50 +00009439 if (op->ch2 != -1)
9440 total +=
9441 xmlXPathCompOpEval(ctxt,
9442 &comp->steps[op->ch2]);
9443 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009444
Daniel Veillardf06307e2001-07-03 10:35:50 +00009445 /*
9446 * The result of the evaluation need to be tested to
9447 * decided whether the filter succeeded or not
9448 */
9449 res = valuePop(ctxt);
9450 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
9451 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
9452 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009453
Daniel Veillardf06307e2001-07-03 10:35:50 +00009454 /*
9455 * Cleanup
9456 */
9457 if (res != NULL)
9458 xmlXPathFreeObject(res);
9459 if (ctxt->value == tmp) {
9460 res = valuePop(ctxt);
9461 xmlXPathFreeObject(res);
9462 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009463
Daniel Veillardf06307e2001-07-03 10:35:50 +00009464 ctxt->context->node = NULL;
9465 }
9466
9467 /*
9468 * The result is used as the new evaluation set.
9469 */
9470 xmlXPathFreeObject(obj);
9471 ctxt->context->node = NULL;
9472 ctxt->context->contextSize = -1;
9473 ctxt->context->proximityPosition = -1;
9474 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
9475 }
9476 ctxt->context->node = oldnode;
9477 return (total);
9478 }
9479 case XPATH_OP_SORT:
9480 if (op->ch1 != -1)
9481 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9482 if ((ctxt->value != NULL) &&
9483 (ctxt->value->type == XPATH_NODESET) &&
9484 (ctxt->value->nodesetval != NULL))
9485 xmlXPathNodeSetSort(ctxt->value->nodesetval);
9486 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009487#ifdef LIBXML_XPTR_ENABLED
Daniel Veillardf06307e2001-07-03 10:35:50 +00009488 case XPATH_OP_RANGETO:{
9489 xmlXPathObjectPtr range;
9490 xmlXPathObjectPtr res, obj;
9491 xmlXPathObjectPtr tmp;
9492 xmlLocationSetPtr newset = NULL;
9493 xmlNodeSetPtr oldset;
9494 int i;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009495
Daniel Veillardf06307e2001-07-03 10:35:50 +00009496 if (op->ch1 != -1)
9497 total +=
9498 xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
9499 if (op->ch2 == -1)
9500 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009501
Daniel Veillardf06307e2001-07-03 10:35:50 +00009502 CHECK_TYPE0(XPATH_NODESET);
9503 obj = valuePop(ctxt);
9504 oldset = obj->nodesetval;
9505 ctxt->context->node = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009506
Daniel Veillardf06307e2001-07-03 10:35:50 +00009507 newset = xmlXPtrLocationSetCreate(NULL);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009508
Daniel Veillardf06307e2001-07-03 10:35:50 +00009509 if (oldset != NULL) {
9510 for (i = 0; i < oldset->nodeNr; i++) {
9511 /*
9512 * Run the evaluation with a node list made of a single item
9513 * in the nodeset.
9514 */
9515 ctxt->context->node = oldset->nodeTab[i];
9516 tmp = xmlXPathNewNodeSet(ctxt->context->node);
9517 valuePush(ctxt, tmp);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009518
Daniel Veillardf06307e2001-07-03 10:35:50 +00009519 if (op->ch2 != -1)
9520 total +=
9521 xmlXPathCompOpEval(ctxt,
9522 &comp->steps[op->ch2]);
9523 CHECK_ERROR0;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009524
Daniel Veillardf06307e2001-07-03 10:35:50 +00009525 /*
9526 * The result of the evaluation need to be tested to
9527 * decided whether the filter succeeded or not
9528 */
9529 res = valuePop(ctxt);
9530 range =
9531 xmlXPtrNewRangeNodeObject(oldset->nodeTab[i],
9532 res);
9533 if (range != NULL) {
9534 xmlXPtrLocationSetAdd(newset, range);
9535 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009536
Daniel Veillardf06307e2001-07-03 10:35:50 +00009537 /*
9538 * Cleanup
9539 */
9540 if (res != NULL)
9541 xmlXPathFreeObject(res);
9542 if (ctxt->value == tmp) {
9543 res = valuePop(ctxt);
9544 xmlXPathFreeObject(res);
9545 }
9546
9547 ctxt->context->node = NULL;
9548 }
9549 }
9550
9551 /*
9552 * The result is used as the new evaluation set.
9553 */
9554 xmlXPathFreeObject(obj);
9555 ctxt->context->node = NULL;
9556 ctxt->context->contextSize = -1;
9557 ctxt->context->proximityPosition = -1;
9558 valuePush(ctxt, xmlXPtrWrapLocationSet(newset));
9559 return (total);
9560 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009561#endif /* LIBXML_XPTR_ENABLED */
9562 }
9563 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf06307e2001-07-03 10:35:50 +00009564 "XPath: unknown precompiled operation %d\n", op->op);
9565 return (total);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009566}
9567
9568/**
9569 * xmlXPathRunEval:
9570 * @ctxt: the XPath parser context with the compiled expression
9571 *
9572 * Evaluate the Precompiled XPath expression in the given context.
9573 */
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009574static void
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009575xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
9576 xmlXPathCompExprPtr comp;
9577
9578 if ((ctxt == NULL) || (ctxt->comp == NULL))
9579 return;
9580
9581 if (ctxt->valueTab == NULL) {
9582 /* Allocate the value stack */
9583 ctxt->valueTab = (xmlXPathObjectPtr *)
9584 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
9585 if (ctxt->valueTab == NULL) {
9586 xmlFree(ctxt);
9587 xmlGenericError(xmlGenericErrorContext,
9588 "xmlXPathRunEval: out of memory\n");
9589 return;
9590 }
9591 ctxt->valueNr = 0;
9592 ctxt->valueMax = 10;
9593 ctxt->value = NULL;
9594 }
9595 comp = ctxt->comp;
9596 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
9597}
9598
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009599/************************************************************************
9600 * *
9601 * Public interfaces *
9602 * *
9603 ************************************************************************/
9604
9605/**
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009606 * xmlXPathEvalPredicate:
9607 * @ctxt: the XPath context
9608 * @res: the Predicate Expression evaluation result
9609 *
9610 * Evaluate a predicate result for the current node.
9611 * A PredicateExpr is evaluated by evaluating the Expr and converting
9612 * the result to a boolean. If the result is a number, the result will
9613 * be converted to true if the number is equal to the position of the
9614 * context node in the context node list (as returned by the position
9615 * function) and will be converted to false otherwise; if the result
9616 * is not a number, then the result will be converted as if by a call
9617 * to the boolean function.
9618 *
9619 * Return 1 if predicate is true, 0 otherwise
9620 */
9621int
9622xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
9623 if (res == NULL) return(0);
9624 switch (res->type) {
9625 case XPATH_BOOLEAN:
9626 return(res->boolval);
9627 case XPATH_NUMBER:
9628 return(res->floatval == ctxt->proximityPosition);
9629 case XPATH_NODESET:
9630 case XPATH_XSLT_TREE:
Daniel Veillardd8df6c02001-04-05 16:54:14 +00009631 if (res->nodesetval == NULL)
9632 return(0);
Daniel Veillardfbf8a2d2001-03-19 15:58:54 +00009633 return(res->nodesetval->nodeNr != 0);
9634 case XPATH_STRING:
9635 return((res->stringval != NULL) &&
9636 (xmlStrlen(res->stringval) != 0));
9637 default:
9638 STRANGE
9639 }
9640 return(0);
9641}
9642
9643/**
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009644 * xmlXPathEvaluatePredicateResult:
9645 * @ctxt: the XPath Parser context
9646 * @res: the Predicate Expression evaluation result
9647 *
9648 * Evaluate a predicate result for the current node.
9649 * A PredicateExpr is evaluated by evaluating the Expr and converting
9650 * the result to a boolean. If the result is a number, the result will
9651 * be converted to true if the number is equal to the position of the
9652 * context node in the context node list (as returned by the position
9653 * function) and will be converted to false otherwise; if the result
9654 * is not a number, then the result will be converted as if by a call
9655 * to the boolean function.
9656 *
9657 * Return 1 if predicate is true, 0 otherwise
9658 */
9659int
9660xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
9661 xmlXPathObjectPtr res) {
9662 if (res == NULL) return(0);
9663 switch (res->type) {
9664 case XPATH_BOOLEAN:
9665 return(res->boolval);
9666 case XPATH_NUMBER:
9667 return(res->floatval == ctxt->context->proximityPosition);
9668 case XPATH_NODESET:
9669 case XPATH_XSLT_TREE:
Daniel Veillard73639a72001-04-10 14:31:39 +00009670 if (res->nodesetval == NULL)
Daniel Veillard911f49a2001-04-07 15:39:35 +00009671 return(0);
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009672 return(res->nodesetval->nodeNr != 0);
9673 case XPATH_STRING:
9674 return((res->stringval != NULL) &&
9675 (xmlStrlen(res->stringval) != 0));
9676 default:
9677 STRANGE
9678 }
9679 return(0);
9680}
9681
9682/**
9683 * xmlXPathCompile:
9684 * @str: the XPath expression
9685 *
9686 * Compile an XPath expression
9687 *
9688 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9689 * the caller has to free the object.
9690 */
9691xmlXPathCompExprPtr
9692xmlXPathCompile(const xmlChar *str) {
9693 xmlXPathParserContextPtr ctxt;
9694 xmlXPathCompExprPtr comp;
9695
9696 xmlXPathInit();
9697
9698 ctxt = xmlXPathNewParserContext(str, NULL);
9699 xmlXPathCompileExpr(ctxt);
9700
Daniel Veillard40af6492001-04-22 08:50:55 +00009701 if (*ctxt->cur != 0) {
9702 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9703 comp = NULL;
9704 } else {
9705 comp = ctxt->comp;
9706 ctxt->comp = NULL;
9707 }
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009708 xmlXPathFreeParserContext(ctxt);
Daniel Veillardf06307e2001-07-03 10:35:50 +00009709#ifdef DEBUG_EVAL_COUNTS
9710 if (comp != NULL) {
9711 comp->string = xmlStrdup(str);
9712 comp->nb = 0;
9713 }
9714#endif
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009715 return(comp);
9716}
9717
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009718/**
9719 * xmlXPathCompiledEval:
9720 * @comp: the compiled XPath expression
Owen Taylor3473f882001-02-23 17:55:21 +00009721 * @ctx: the XPath context
9722 *
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009723 * Evaluate the Precompiled XPath expression in the given context.
Owen Taylor3473f882001-02-23 17:55:21 +00009724 *
9725 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9726 * the caller has to free the object.
9727 */
9728xmlXPathObjectPtr
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009729xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
Owen Taylor3473f882001-02-23 17:55:21 +00009730 xmlXPathParserContextPtr ctxt;
9731 xmlXPathObjectPtr res, tmp, init = NULL;
9732 int stack = 0;
9733
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009734 if ((comp == NULL) || (ctx == NULL))
9735 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00009736 xmlXPathInit();
9737
9738 CHECK_CONTEXT(ctx)
9739
Daniel Veillardf06307e2001-07-03 10:35:50 +00009740#ifdef DEBUG_EVAL_COUNTS
9741 comp->nb++;
9742 if ((comp->string != NULL) && (comp->nb > 100)) {
9743 fprintf(stderr, "100 x %s\n", comp->string);
9744 comp->nb = 0;
9745 }
9746#endif
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009747 ctxt = xmlXPathCompParserContext(comp, ctx);
9748 xmlXPathRunEval(ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009749
9750 if (ctxt->value == NULL) {
9751 xmlGenericError(xmlGenericErrorContext,
9752 "xmlXPathEval: evaluation failed\n");
9753 res = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00009754 } else {
9755 res = valuePop(ctxt);
9756 }
9757
Daniel Veillardf06307e2001-07-03 10:35:50 +00009758
Owen Taylor3473f882001-02-23 17:55:21 +00009759 do {
9760 tmp = valuePop(ctxt);
9761 if (tmp != NULL) {
9762 if (tmp != init)
9763 stack++;
9764 xmlXPathFreeObject(tmp);
9765 }
9766 } while (tmp != NULL);
9767 if ((stack != 0) && (res != NULL)) {
9768 xmlGenericError(xmlGenericErrorContext,
9769 "xmlXPathEval: %d object left on the stack\n",
9770 stack);
9771 }
9772 if (ctxt->error != XPATH_EXPRESSION_OK) {
9773 xmlXPathFreeObject(res);
9774 res = NULL;
9775 }
9776
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009777
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009778 ctxt->comp = NULL;
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009779 xmlXPathFreeParserContext(ctxt);
9780 return(res);
9781}
9782
Daniel Veillardafcbe1c2001-03-19 10:57:13 +00009783/**
9784 * xmlXPathEvalExpr:
9785 * @ctxt: the XPath Parser context
9786 *
9787 * Parse and evaluate an XPath expression in the given context,
9788 * then push the result on the context stack
9789 */
9790void
9791xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
9792 xmlXPathCompileExpr(ctxt);
9793 xmlXPathRunEval(ctxt);
9794}
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009795
9796/**
9797 * xmlXPathEval:
9798 * @str: the XPath expression
9799 * @ctx: the XPath context
9800 *
9801 * Evaluate the XPath Location Path in the given context.
9802 *
9803 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
9804 * the caller has to free the object.
9805 */
9806xmlXPathObjectPtr
9807xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
9808 xmlXPathParserContextPtr ctxt;
9809 xmlXPathObjectPtr res, tmp, init = NULL;
9810 int stack = 0;
9811
9812 xmlXPathInit();
9813
9814 CHECK_CONTEXT(ctx)
9815
9816 ctxt = xmlXPathNewParserContext(str, ctx);
9817 xmlXPathEvalExpr(ctxt);
Daniel Veillard9e7160d2001-03-18 23:17:47 +00009818
9819 if (ctxt->value == NULL) {
9820 xmlGenericError(xmlGenericErrorContext,
9821 "xmlXPathEval: evaluation failed\n");
9822 res = NULL;
9823 } else if (*ctxt->cur != 0) {
9824 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9825 res = NULL;
9826 } else {
9827 res = valuePop(ctxt);
9828 }
9829
9830 do {
9831 tmp = valuePop(ctxt);
9832 if (tmp != NULL) {
9833 if (tmp != init)
9834 stack++;
9835 xmlXPathFreeObject(tmp);
9836 }
9837 } while (tmp != NULL);
9838 if ((stack != 0) && (res != NULL)) {
9839 xmlGenericError(xmlGenericErrorContext,
9840 "xmlXPathEval: %d object left on the stack\n",
9841 stack);
9842 }
9843 if (ctxt->error != XPATH_EXPRESSION_OK) {
9844 xmlXPathFreeObject(res);
9845 res = NULL;
9846 }
9847
Owen Taylor3473f882001-02-23 17:55:21 +00009848 xmlXPathFreeParserContext(ctxt);
9849 return(res);
9850}
9851
9852/**
9853 * xmlXPathEvalExpression:
9854 * @str: the XPath expression
9855 * @ctxt: the XPath context
9856 *
9857 * Evaluate the XPath expression in the given context.
9858 *
9859 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
9860 * the caller has to free the object.
9861 */
9862xmlXPathObjectPtr
9863xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
9864 xmlXPathParserContextPtr pctxt;
9865 xmlXPathObjectPtr res, tmp;
9866 int stack = 0;
9867
9868 xmlXPathInit();
9869
9870 CHECK_CONTEXT(ctxt)
9871
9872 pctxt = xmlXPathNewParserContext(str, ctxt);
9873 xmlXPathEvalExpr(pctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00009874
9875 if (*pctxt->cur != 0) {
9876 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
9877 res = NULL;
9878 } else {
9879 res = valuePop(pctxt);
9880 }
9881 do {
9882 tmp = valuePop(pctxt);
9883 if (tmp != NULL) {
9884 xmlXPathFreeObject(tmp);
9885 stack++;
9886 }
9887 } while (tmp != NULL);
9888 if ((stack != 0) && (res != NULL)) {
9889 xmlGenericError(xmlGenericErrorContext,
9890 "xmlXPathEvalExpression: %d object left on the stack\n",
9891 stack);
9892 }
9893 xmlXPathFreeParserContext(pctxt);
9894 return(res);
9895}
9896
9897/**
9898 * xmlXPathRegisterAllFunctions:
9899 * @ctxt: the XPath context
9900 *
9901 * Registers all default XPath functions in this context
9902 */
9903void
9904xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
9905{
9906 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
9907 xmlXPathBooleanFunction);
9908 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
9909 xmlXPathCeilingFunction);
9910 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
9911 xmlXPathCountFunction);
9912 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
9913 xmlXPathConcatFunction);
9914 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
9915 xmlXPathContainsFunction);
9916 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
9917 xmlXPathIdFunction);
9918 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
9919 xmlXPathFalseFunction);
9920 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
9921 xmlXPathFloorFunction);
9922 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
9923 xmlXPathLastFunction);
9924 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
9925 xmlXPathLangFunction);
9926 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
9927 xmlXPathLocalNameFunction);
9928 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
9929 xmlXPathNotFunction);
9930 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
9931 xmlXPathNameFunction);
9932 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
9933 xmlXPathNamespaceURIFunction);
9934 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
9935 xmlXPathNormalizeFunction);
9936 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
9937 xmlXPathNumberFunction);
9938 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
9939 xmlXPathPositionFunction);
9940 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
9941 xmlXPathRoundFunction);
9942 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
9943 xmlXPathStringFunction);
9944 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
9945 xmlXPathStringLengthFunction);
9946 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
9947 xmlXPathStartsWithFunction);
9948 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
9949 xmlXPathSubstringFunction);
9950 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
9951 xmlXPathSubstringBeforeFunction);
9952 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
9953 xmlXPathSubstringAfterFunction);
9954 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
9955 xmlXPathSumFunction);
9956 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
9957 xmlXPathTrueFunction);
9958 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
9959 xmlXPathTranslateFunction);
9960}
9961
9962#endif /* LIBXML_XPATH_ENABLED */