blob: 9fc3b1668e84d08e46ef76a4e86f0cf64022ce6d [file] [log] [blame]
Daniel Veillard1566d3a1999-07-15 14:24:29 +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 Working Draft internal 5 July 1999
7 * http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html
8 * Public reference:
9 * http://www.w3.org/TR/WD-xpath/
10 *
11 * See COPYRIGHT for the status of this software
12 *
13 * Author: Daniel.Veillard@w3.org
14 */
15
Daniel Veillardb05deb71999-08-10 19:04:08 +000016#include <config.h>
Daniel Veillard1566d3a1999-07-15 14:24:29 +000017#include <malloc.h>
Daniel Veillardb05deb71999-08-10 19:04:08 +000018#ifdef HAVE_MATH_H
Daniel Veillard1566d3a1999-07-15 14:24:29 +000019#include <math.h>
Daniel Veillardb05deb71999-08-10 19:04:08 +000020#endif
21#ifdef HAVE_MATH_H
22#include <float.h>
23#endif
24#ifdef HAVE_IEEEFP_H
25#include <ieeefp.h>
26#endif
27#ifdef HAVE_NAN_H
28#include <nan.h>
29#endif
Daniel Veillard1566d3a1999-07-15 14:24:29 +000030#include <stdio.h>
Daniel Veillardb96e6431999-08-29 21:02:19 +000031#include <ctype.h>
Daniel Veillard1566d3a1999-07-15 14:24:29 +000032#include "tree.h"
Daniel Veillardb96e6431999-08-29 21:02:19 +000033#include "valid.h"
Daniel Veillard1566d3a1999-07-15 14:24:29 +000034#include "xpath.h"
35#include "parserInternals.h"
36
37/*
Daniel Veillarde2d034d1999-07-27 19:52:06 +000038 * Setup stuff for floating point
Daniel Veillard991e63d1999-08-15 23:32:28 +000039 * The lack of portability of this section of the libc is annoying !
Daniel Veillard1566d3a1999-07-15 14:24:29 +000040 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +000041double xmlXPathNAN = 0;
42double xmlXPathPINF = 1;
43double xmlXPathMINF = -1;
44
Daniel Veillardb05deb71999-08-10 19:04:08 +000045#ifndef isinf
46#ifndef HAVE_ISINF
47
48#if HAVE_FPCLASS
49
50int isinf(double d) {
51 fpclass_t type = fpclass(d);
52 switch (type) {
53 case FP_NINF:
54 return(-1);
55 case FP_PINF:
56 return(1);
57 default:
58 return(0);
59 }
60 return(0);
61}
62
63#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
64
65#if HAVE_FP_CLASS_H
66#include <fp_class.h>
67#endif
68
69int isinf(double d) {
70#if HAVE_FP_CLASS
71 int fpclass = fp_class(d);
72#else
73 int fpclass = fp_class_d(d);
74#endif
75 if (fpclass == FP_POS_INF)
76 return(1);
77 if (fpclass == FP_NEG_INF)
78 return(-1);
79 return(0);
80}
81
82#elif defined(HAVE_CLASS)
83
84int isinf(double d) {
85 int fpclass = class(d);
86 if (fpclass == FP_PLUS_INF)
87 return(1);
88 if (fpclass == FP_MINUS_INF)
89 return(-1);
90 return(0);
91}
92#elif defined(finite) || defined(HAVE_FINITE)
93int isinf(double x) { return !finite(x) && x==x; }
Daniel Veillard991e63d1999-08-15 23:32:28 +000094#elif defined(HUGE_VAL)
95static int isinf(double x)
96{
97 if (x == HUGE_VAL)
98 return(1);
99 if (x == -HUGE_VAL)
100 return(-1);
101 return(0);
102}
103#endif
Daniel Veillardb05deb71999-08-10 19:04:08 +0000104
105#endif /* ! HAVE_ISINF */
106#endif /* ! defined(isinf) */
107
108#ifndef isnan
109#ifndef HAVE_ISNAN
110
111#ifdef HAVE_ISNAND
112#define isnan(f) isnand(f)
113#endif /* HAVE_iSNAND */
114
115#endif /* ! HAVE_iSNAN */
116#endif /* ! defined(isnan) */
117
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000118/**
119 * xmlXPathInit:
120 *
121 * Initialize the XPath environment
122 */
123void
124xmlXPathInit(void) {
125 static int initialized = 0;
126
127 if (initialized) return;
128
129 xmlXPathNAN = 0;
130 xmlXPathNAN /= 0;
131
132 xmlXPathPINF = 1;
133 xmlXPathPINF /= 0;
134
135 xmlXPathMINF = -1;
136 xmlXPathMINF /= 0;
137
138 initialized = 1;
139}
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000140
141/* #define DEBUG */
142/* #define DEBUG_STEP */
143/* #define DEBUG_EXPR */
144
145FILE *xmlXPathDebug = NULL;
146
147#define TODO \
148 fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \
149 __FILE__, __LINE__);
150
151#define STRANGE \
152 fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \
153 __FILE__, __LINE__);
154
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000155double xmlXPathStringEvalNumber(const CHAR *str);
Daniel Veillardb96e6431999-08-29 21:02:19 +0000156void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000157
158/************************************************************************
159 * *
160 * Parser stacks related functions and macros *
161 * *
162 ************************************************************************/
163
164/*
165 * Generic function for accessing stacks in the Parser Context
166 */
167
168#define PUSH_AND_POP(type, name) \
169extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
170 if (ctxt->name##Nr >= ctxt->name##Max) { \
171 ctxt->name##Max *= 2; \
172 ctxt->name##Tab = (void *) realloc(ctxt->name##Tab, \
173 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
174 if (ctxt->name##Tab == NULL) { \
Daniel Veillardb05deb71999-08-10 19:04:08 +0000175 fprintf(xmlXPathDebug, "realloc failed !\n"); \
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000176 exit(1); \
177 } \
178 } \
179 ctxt->name##Tab[ctxt->name##Nr] = value; \
180 ctxt->name = value; \
181 return(ctxt->name##Nr++); \
182} \
183extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
184 type ret; \
185 if (ctxt->name##Nr <= 0) return(0); \
186 ctxt->name##Nr--; \
187 if (ctxt->name##Nr > 0) \
188 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
189 else \
190 ctxt->name = NULL; \
191 ret = ctxt->name##Tab[ctxt->name##Nr]; \
192 ctxt->name##Tab[ctxt->name##Nr] = 0; \
193 return(ret); \
194} \
195
196PUSH_AND_POP(xmlXPathObjectPtr, value)
197
198/*
199 * Macros for accessing the content. Those should be used only by the parser,
200 * and not exported.
201 *
202 * Dirty macros, i.e. one need to make assumption on the context to use them
203 *
204 * CUR_PTR return the current pointer to the CHAR to be parsed.
205 * CUR returns the current CHAR value, i.e. a 8 bit value if compiled
206 * in ISO-Latin or UTF-8, and the current 16 bit value if compiled
207 * in UNICODE mode. This should be used internally by the parser
208 * only to compare to ASCII values otherwise it would break when
209 * running with UTF-8 encoding.
210 * NXT(n) returns the n'th next CHAR. Same as CUR is should be used only
211 * to compare on ASCII based substring.
212 * SKIP(n) Skip n CHAR, and must also be used only to skip ASCII defined
213 * strings within the parser.
214 * CURRENT Returns the current char value, with the full decoding of
215 * UTF-8 if we are using this mode. It returns an int.
216 * NEXT Skip to the next character, this does the proper decoding
217 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
218 * It returns the pointer to the current CHAR.
219 */
220
221#define CUR (*ctxt->cur)
222#define SKIP(val) ctxt->cur += (val)
223#define NXT(val) ctxt->cur[(val)]
224#define CUR_PTR ctxt->cur
225
226#define SKIP_BLANKS \
227 while (IS_BLANK(*(ctxt->cur))) NEXT
228
229#ifndef USE_UTF_8
230#define CURRENT (*ctxt->cur)
231#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
232#else
233#endif
234
235/************************************************************************
236 * *
237 * Error handling routines *
238 * *
239 ************************************************************************/
240
241#define XPATH_EXPRESSION_OK 0
242#define XPATH_NUMBER_ERROR 1
243#define XPATH_UNFINISHED_LITERAL_ERROR 2
244#define XPATH_START_LITERAL_ERROR 3
245#define XPATH_VARIABLE_REF_ERROR 4
246#define XPATH_UNDEF_VARIABLE_ERROR 5
247#define XPATH_INVALID_PREDICATE_ERROR 6
248#define XPATH_EXPR_ERROR 7
249#define XPATH_UNCLOSED_ERROR 8
250#define XPATH_UNKNOWN_FUNC_ERROR 9
251#define XPATH_INVALID_OPERAND 10
252#define XPATH_INVALID_TYPE 11
253#define XPATH_INVALID_ARITY 12
254
255const char *xmlXPathErrorMessages[] = {
256 "Ok",
257 "Number encoding",
258 "Unfinished litteral",
259 "Start of litteral",
260 "Expected $ for variable reference",
261 "Undefined variable",
262 "Invalid predicate",
263 "Invalid expression",
264 "Missing closing curly brace",
265 "Unregistered function",
266 "Invalid operand",
267 "Invalid type",
268 "Invalid number of arguments",
269};
270
271/**
272 * xmlXPathError:
273 * @ctxt: the XPath Parser context
274 * @file: the file name
275 * @line: the line number
276 * @no: the error number
277 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000278 * Create a new xmlNodeSetPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000279 *
280 * Returns the newly created object.
281 */
282void
283xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
284 int line, int no) {
285 int n;
Daniel Veillardb96e6431999-08-29 21:02:19 +0000286 const CHAR *cur;
287 const CHAR *base;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000288
289 fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line,
290 xmlXPathErrorMessages[no]);
291
292 cur = ctxt->cur;
293 base = ctxt->base;
294 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
295 cur--;
296 }
297 n = 0;
298 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
299 cur--;
300 if ((*cur == '\n') || (*cur == '\r')) cur++;
301 base = cur;
302 n = 0;
303 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
304 fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++);
305 n++;
306 }
307 fprintf(xmlXPathDebug, "\n");
308 cur = ctxt->cur;
309 while ((*cur == '\n') || (*cur == '\r'))
310 cur--;
311 n = 0;
312 while ((cur != base) && (n++ < 80)) {
313 fprintf(xmlXPathDebug, " ");
314 base++;
315 }
316 fprintf(xmlXPathDebug,"^\n");
317}
318
319#define CHECK_ERROR \
320 if (ctxt->error != XPATH_EXPRESSION_OK) return
321
322#define ERROR(X) \
323 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
324 ctxt->error = (X); return; }
325
Daniel Veillard991e63d1999-08-15 23:32:28 +0000326#define ERROR0(X) \
327 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
328 ctxt->error = (X); return(0); }
329
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000330#define CHECK_TYPE(typeval) \
331 if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \
332 ERROR(XPATH_INVALID_TYPE) \
333
334
335/************************************************************************
336 * *
337 * Routines to handle NodeSets *
338 * *
339 ************************************************************************/
340
341#define XML_NODESET_DEFAULT 10
342/**
343 * xmlXPathNodeSetCreate:
344 * @val: an initial xmlNodePtr, or NULL
345 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000346 * Create a new xmlNodeSetPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000347 *
348 * Returns the newly created object.
349 */
350xmlNodeSetPtr
351xmlXPathNodeSetCreate(xmlNodePtr val) {
352 xmlNodeSetPtr ret;
353
354 ret = (xmlNodeSetPtr) malloc(sizeof(xmlNodeSet));
355 if (ret == NULL) {
356 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
357 return(NULL);
358 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000359 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000360 if (val != NULL) {
361 ret->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
362 sizeof(xmlNodePtr));
363 if (ret->nodeTab == NULL) {
364 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
365 return(NULL);
366 }
367 memset(ret->nodeTab, 0 ,
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000368 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000369 ret->nodeMax = XML_NODESET_DEFAULT;
370 ret->nodeTab[ret->nodeNr++] = val;
371 }
372 return(ret);
373}
374
375/**
376 * xmlXPathNodeSetAdd:
377 * @cur: the initial node set
378 * @val: a new xmlNodePtr
379 *
380 * add a new xmlNodePtr ot an existing NodeSet
381 */
382void
383xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
384 int i;
385
386 if (val == NULL) return;
387
388 /*
389 * check against doublons
390 */
391 for (i = 0;i < cur->nodeNr;i++)
392 if (cur->nodeTab[i] == val) return;
393
394 /*
395 * grow the nodeTab if needed
396 */
397 if (cur->nodeMax == 0) {
398 cur->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
399 sizeof(xmlNodePtr));
400 if (cur->nodeTab == NULL) {
401 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
402 return;
403 }
404 memset(cur->nodeTab, 0 ,
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000405 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000406 cur->nodeMax = XML_NODESET_DEFAULT;
407 } else if (cur->nodeNr == cur->nodeMax) {
408 xmlNodePtr *temp;
409
410 cur->nodeMax *= 2;
411 temp = (xmlNodePtr *) realloc(cur->nodeTab, cur->nodeMax *
412 sizeof(xmlNodePtr));
413 if (temp == NULL) {
414 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
415 return;
416 }
Daniel Veillardb96e6431999-08-29 21:02:19 +0000417 cur->nodeTab = temp;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000418 }
419 cur->nodeTab[cur->nodeNr++] = val;
420}
421
422/**
423 * xmlXPathNodeSetMerge:
424 * @val1: the first NodeSet
425 * @val2: the second NodeSet
426 *
427 * Merges two nodesets, all nodes from @val2 are added to @val1
428 *
429 * Returns val1 once extended or NULL in case of error.
430 */
431xmlNodeSetPtr
432xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
433 int i;
434
435 if (val1 == NULL) return(NULL);
436 if (val2 == NULL) return(val1);
437
438 /*
439 * !!!!! this can be optimized a lot, knowing that both
440 * val1 and val2 already have unicity of their values.
441 */
442
443 for (i = 0;i < val2->nodeNr;i++)
444 xmlXPathNodeSetAdd(val1, val2->nodeTab[i]);
445
446 return(val1);
447}
448
449/**
450 * xmlXPathNodeSetDel:
451 * @cur: the initial node set
452 * @val: an xmlNodePtr
453 *
454 * Removes an xmlNodePtr from an existing NodeSet
455 */
456void
457xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
458 int i;
459
460 if (cur == NULL) return;
461 if (val == NULL) return;
462
463 /*
464 * check against doublons
465 */
466 for (i = 0;i < cur->nodeNr;i++)
467 if (cur->nodeTab[i] == val) break;
468
469 if (i >= cur->nodeNr) {
470#ifdef DEBUG
471 fprintf(xmlXPathDebug,
472 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
473 val->name);
474#endif
475 return;
476 }
477 cur->nodeNr--;
478 for (;i < cur->nodeNr;i++)
479 cur->nodeTab[i] = cur->nodeTab[i + 1];
480 cur->nodeTab[cur->nodeNr] = NULL;
481}
482
483/**
484 * xmlXPathNodeSetRemove:
485 * @cur: the initial node set
486 * @val: the index to remove
487 *
488 * Removes an entry from an existing NodeSet list.
489 */
490void
491xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
492 if (cur == NULL) return;
493 if (val >= cur->nodeNr) return;
494 cur->nodeNr--;
495 for (;val < cur->nodeNr;val++)
496 cur->nodeTab[val] = cur->nodeTab[val + 1];
497 cur->nodeTab[cur->nodeNr] = NULL;
498}
499
500/**
501 * xmlXPathFreeNodeSet:
502 * @obj: the xmlNodeSetPtr to free
503 *
504 * Free the NodeSet compound (not the actual nodes !).
505 */
506void
507xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
508 if (obj == NULL) return;
509 if (obj->nodeTab != NULL) {
510#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000511 memset(obj->nodeTab, 0xB , (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000512#endif
513 free(obj->nodeTab);
514 }
515#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000516 memset(obj, 0xB , (size_t) sizeof(xmlNodeSet));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000517#endif
518 free(obj);
519}
520
Daniel Veillardb96e6431999-08-29 21:02:19 +0000521#if defined(DEBUG) || defined(DEBUG_STEP)
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000522/**
523 * xmlXPathDebugNodeSet:
524 * @output: a FILE * for the output
525 * @obj: the xmlNodeSetPtr to free
526 *
527 * Quick display of a NodeSet
528 */
529void
530xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) {
531 int i;
532
533 if (output == NULL) output = xmlXPathDebug;
534 if (obj == NULL) {
535 fprintf(output, "NodeSet == NULL !\n");
536 return;
537 }
538 if (obj->nodeNr == 0) {
539 fprintf(output, "NodeSet is empty\n");
540 return;
541 }
542 if (obj->nodeTab == NULL) {
543 fprintf(output, " nodeTab == NULL !\n");
544 return;
545 }
546 for (i = 0; i < obj->nodeNr; i++) {
547 if (obj->nodeTab[i] == NULL) {
548 fprintf(output, " NULL !\n");
549 return;
550 }
Daniel Veillardb05deb71999-08-10 19:04:08 +0000551 if (obj->nodeTab[i]->type == XML_DOCUMENT_NODE)
552 fprintf(output, " /");
553 else if (obj->nodeTab[i]->name == NULL)
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000554 fprintf(output, " noname!");
555 else fprintf(output, " %s", obj->nodeTab[i]->name);
556 }
557 fprintf(output, "\n");
558}
559#endif
560
561/************************************************************************
562 * *
563 * Routines to handle Variable *
564 * *
565 * UNIMPLEMENTED CURRENTLY *
566 * *
567 ************************************************************************/
568
569/**
570 * xmlXPathVariablelookup:
571 * @ctxt: the XPath Parser context
572 * @prefix: the variable name namespace if any
573 * @name: the variable name
574 *
575 * Search in the Variable array of the context for the given
576 * variable value.
577 *
578 * UNIMPLEMENTED: always return NULL.
579 *
580 * Returns the value or NULL if not found
581 */
582xmlXPathObjectPtr
583xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt,
584 const CHAR *prefix, const CHAR *name) {
585 return(NULL);
586}
587
588/************************************************************************
589 * *
590 * Routines to handle Values *
591 * *
592 ************************************************************************/
593
594/* Allocations are terrible, one need to optimize all this !!! */
595
596/**
597 * xmlXPathNewFloat:
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000598 * @val: the double value
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000599 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000600 * Create a new xmlXPathObjectPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000601 *
602 * Returns the newly created object.
603 */
604xmlXPathObjectPtr
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000605xmlXPathNewFloat(double val) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000606 xmlXPathObjectPtr ret;
607
608 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
609 if (ret == NULL) {
610 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
611 return(NULL);
612 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000613 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000614 ret->type = XPATH_NUMBER;
615 ret->floatval = val;
616 return(ret);
617}
618
619/**
620 * xmlXPathNewBoolean:
621 * @val: the boolean value
622 *
623 * Create a new xmlXPathObjectPtr of type boolean and of value @val
624 *
625 * Returns the newly created object.
626 */
627xmlXPathObjectPtr
628xmlXPathNewBoolean(int val) {
629 xmlXPathObjectPtr ret;
630
631 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
632 if (ret == NULL) {
633 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
634 return(NULL);
635 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000636 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000637 ret->type = XPATH_BOOLEAN;
638 ret->boolval = (val != 0);
639 return(ret);
640}
641
642/**
Daniel Veillardb96e6431999-08-29 21:02:19 +0000643 * xmlXPathNewString:
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000644 * @val: the CHAR * value
645 *
646 * Create a new xmlXPathObjectPtr of type string and of value @val
647 *
648 * Returns the newly created object.
649 */
650xmlXPathObjectPtr
651xmlXPathNewString(const CHAR *val) {
652 xmlXPathObjectPtr ret;
653
654 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
655 if (ret == NULL) {
656 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
657 return(NULL);
658 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000659 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000660 ret->type = XPATH_STRING;
661 ret->stringval = xmlStrdup(val);
662 return(ret);
663}
664
665/**
Daniel Veillardb96e6431999-08-29 21:02:19 +0000666 * xmlXPathNewCString:
667 * @val: the char * value
668 *
669 * Create a new xmlXPathObjectPtr of type string and of value @val
670 *
671 * Returns the newly created object.
672 */
673xmlXPathObjectPtr
674xmlXPathNewCString(const char *val) {
675 xmlXPathObjectPtr ret;
676
677 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
678 if (ret == NULL) {
679 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
680 return(NULL);
681 }
682 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
683 ret->type = XPATH_STRING;
684 ret->stringval = xmlStrdup(BAD_CAST val);
685 return(ret);
686}
687
688/**
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000689 * xmlXPathNewNodeSet:
690 * @val: the NodePtr value
691 *
692 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
693 * it with the single Node @val
694 *
695 * Returns the newly created object.
696 */
697xmlXPathObjectPtr
698xmlXPathNewNodeSet(xmlNodePtr val) {
699 xmlXPathObjectPtr ret;
700
701 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
702 if (ret == NULL) {
703 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
704 return(NULL);
705 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000706 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000707 ret->type = XPATH_NODESET;
708 ret->nodesetval = xmlXPathNodeSetCreate(val);
709 return(ret);
710}
711
712/**
713 * xmlXPathNewNodeSetList:
714 * @val: an existing NodeSet
715 *
716 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
717 * it with the Nodeset @val
718 *
719 * Returns the newly created object.
720 */
721xmlXPathObjectPtr
722xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
723 xmlXPathObjectPtr ret;
724
725 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
726 if (ret == NULL) {
727 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
728 return(NULL);
729 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000730 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000731 ret->type = XPATH_NODESET;
732 ret->nodesetval = val;
733 return(ret);
734}
735
736/**
737 * xmlXPathFreeObject:
738 * @obj: the object to free
739 *
740 * Free up an xmlXPathObjectPtr object.
741 */
742void
743xmlXPathFreeObject(xmlXPathObjectPtr obj) {
744 if (obj == NULL) return;
745 if (obj->nodesetval != NULL)
746 xmlXPathFreeNodeSet(obj->nodesetval);
747 if (obj->stringval != NULL)
748 free(obj->stringval);
749#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000750 memset(obj, 0xB , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000751#endif
752 free(obj);
753}
754
755/************************************************************************
756 * *
757 * Routines to handle XPath contexts *
758 * *
759 ************************************************************************/
760
761/**
762 * xmlXPathNewContext:
763 * @doc: the XML document
764 * @variables: the variable list
765 * @functions: the function list
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000766 *
767 * Create a new xmlXPathContext
768 *
769 * Returns the xmlXPathContext just allocated.
770 */
771xmlXPathContextPtr
Daniel Veillardb96e6431999-08-29 21:02:19 +0000772xmlXPathNewContext(xmlDocPtr doc, void *variables, void *functions) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000773 xmlXPathContextPtr ret;
774
775 ret = (xmlXPathContextPtr) malloc(sizeof(xmlXPathContext));
776 if (ret == NULL) {
777 fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n");
778 return(NULL);
779 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000780 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000781 ret->doc = doc;
782 ret->variables = variables;
783 ret->functions = functions;
Daniel Veillardb96e6431999-08-29 21:02:19 +0000784 ret->namespaces = NULL;
785 ret->nsNr = 0;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000786 return(ret);
787}
788
789/**
790 * xmlXPathFreeContext:
791 * @ctxt: the context to free
792 *
793 * Free up an xmlXPathContext
794 */
795void
796xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000797 if (ctxt->namespaces != NULL)
798 free(ctxt->namespaces);
799
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000800#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000801 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000802#endif
803 free(ctxt);
804}
805
806/************************************************************************
807 * *
808 * Routines to handle XPath parser contexts *
809 * *
810 ************************************************************************/
811
812#define CHECK_CTXT \
813 if (ctxt == NULL) { \
814 fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \
815 __FILE__, __LINE__); \
816 } \
817
818
819#define CHECK_CONTEXT \
820 if (ctxt == NULL) { \
821 fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \
822 __FILE__, __LINE__); \
823 } \
824 if (ctxt->doc == NULL) { \
825 fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \
826 __FILE__, __LINE__); \
827 } \
828 if (ctxt->doc->root == NULL) { \
829 fprintf(xmlXPathDebug, \
830 "%s:%d Internal error: document without root\n", \
831 __FILE__, __LINE__); \
832 } \
833
834
835/**
836 * xmlXPathNewParserContext:
837 * @str: the XPath expression
838 * @ctxt: the XPath context
839 *
840 * Create a new xmlXPathParserContext
841 *
842 * Returns the xmlXPathParserContext just allocated.
843 */
844xmlXPathParserContextPtr
845xmlXPathNewParserContext(const CHAR *str, xmlXPathContextPtr ctxt) {
846 xmlXPathParserContextPtr ret;
847
848 ret = (xmlXPathParserContextPtr) malloc(sizeof(xmlXPathParserContext));
849 if (ret == NULL) {
850 fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n");
851 return(NULL);
852 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000853 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000854 ret->cur = ret->base = str;
855 ret->context = ctxt;
856
857 /* Allocate the value stack */
858 ret->valueTab = (xmlXPathObjectPtr *)
859 malloc(10 * sizeof(xmlXPathObjectPtr));
860 ret->valueNr = 0;
861 ret->valueMax = 10;
862 ret->value = NULL;
863 return(ret);
864}
865
866/**
867 * xmlXPathFreeParserContext:
868 * @ctxt: the context to free
869 *
870 * Free up an xmlXPathParserContext
871 */
872void
873xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
874 if (ctxt->valueTab != NULL) {
875#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000876 memset(ctxt->valueTab, 0xB , 10 * (size_t) sizeof(xmlXPathObjectPtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000877#endif
878 free(ctxt->valueTab);
879 }
880#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000881 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathParserContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000882#endif
883 free(ctxt);
884}
885
886/************************************************************************
887 * *
888 * The implicit core function library *
889 * *
890 ************************************************************************/
891
892/*
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000893 * Auto-pop and cast to a number
894 */
895void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
896
897#define CHECK_ARITY(x) \
898 if (nargs != (x)) { \
899 ERROR(XPATH_INVALID_ARITY); \
900 } \
901
902
903#define POP_FLOAT \
904 arg = valuePop(ctxt); \
905 if (arg == NULL) { \
906 ERROR(XPATH_INVALID_OPERAND); \
907 } \
908 if (arg->type != XPATH_NUMBER) { \
909 valuePush(ctxt, arg); \
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000910 xmlXPathNumberFunction(ctxt, 1); \
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000911 arg = valuePop(ctxt); \
912 }
913
914/**
Daniel Veillard991e63d1999-08-15 23:32:28 +0000915 * xmlXPathEqualNodeSetString
916 * @arg: the nodeset object argument
917 * @str: the string to compare to.
918 *
919 * Implement the equal operation on XPath objects content: @arg1 == @arg2
920 * If one object to be compared is a node-set and the other is a string,
921 * then the comparison will be true if and only if there is a node in
922 * the node-set such that the result of performing the comparison on the
923 * string-value of the node and the other string is true.
924 *
925 * Returns 0 or 1 depending on the results of the test.
926 */
927int
928xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const CHAR *str) {
929 int i;
930 xmlNodeSetPtr ns;
931 CHAR *str2;
932
933 if ((str == NULL) || (arg == NULL) || (arg->type != XPATH_NODESET))
934 return(0);
935 ns = arg->nodesetval;
936 for (i = 0;i < ns->nodeNr;i++) {
937 str2 = xmlNodeGetContent(ns->nodeTab[i]);
938 if ((str2 != NULL) && (!xmlStrcmp(str, str2))) {
939 free(str2);
940 return(1);
941 }
942 free(str2);
943 }
944 return(0);
945}
946
947/**
948 * xmlXPathEqualNodeSetFloat
949 * @arg: the nodeset object argument
950 * @f: the float to compare to
951 *
952 * Implement the equal operation on XPath objects content: @arg1 == @arg2
953 * If one object to be compared is a node-set and the other is a number,
954 * then the comparison will be true if and only if there is a node in
955 * the node-set such that the result of performing the comparison on the
956 * number to be compared and on the result of converting the string-value
957 * of that node to a number using the number function is true.
958 *
959 * Returns 0 or 1 depending on the results of the test.
960 */
961int
962xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, float f) {
Daniel Veillardb96e6431999-08-29 21:02:19 +0000963 char buf[100] = "";
Daniel Veillard991e63d1999-08-15 23:32:28 +0000964
965 if ((arg == NULL) || (arg->type != XPATH_NODESET))
966 return(0);
967
968 if (isnan(f))
969 sprintf(buf, "NaN");
970 else if (isinf(f) > 0)
971 sprintf(buf, "+Infinity");
972 else if (isinf(f) < 0)
973 sprintf(buf, "-Infinity");
974 else
975 sprintf(buf, "%0g", f);
976
Daniel Veillardb96e6431999-08-29 21:02:19 +0000977 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
Daniel Veillard991e63d1999-08-15 23:32:28 +0000978}
979
980
981/**
982 * xmlXPathEqualNodeSets
983 * @arg1: first nodeset object argument
984 * @arg2: second nodeset object argument
985 *
986 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
987 * If both objects to be compared are node-sets, then the comparison
988 * will be true if and only if there is a node in the first node-set and
989 * a node in the second node-set such that the result of performing the
990 * comparison on the string-values of the two nodes is true.
991 *
992 * (needless to say, this is a costly operation)
993 *
994 * Returns 0 or 1 depending on the results of the test.
995 */
996int
997xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
998 int i;
999 xmlNodeSetPtr ns;
1000 CHAR *str;
1001
1002 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET))
1003 return(0);
1004 if ((arg2 == NULL) || (arg2->type != XPATH_NODESET))
1005 return(0);
1006
1007 ns = arg1->nodesetval;
1008 for (i = 0;i < ns->nodeNr;i++) {
1009 str = xmlNodeGetContent(ns->nodeTab[i]);
1010 if ((str != NULL) && (xmlXPathEqualNodeSetString(arg2, str))) {
1011 free(str);
1012 return(1);
1013 }
1014 free(str);
1015 }
1016 return(0);
1017}
1018
1019/**
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001020 * xmlXPathEqualValues:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001021 * @ctxt: the XPath Parser context
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001022 *
1023 * Implement the equal operation on XPath objects content: @arg1 == @arg2
1024 *
1025 * Returns 0 or 1 depending on the results of the test.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001026 */
1027int
Daniel Veillard991e63d1999-08-15 23:32:28 +00001028xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
1029 xmlXPathObjectPtr arg1, arg2;
1030 int ret = 0;
1031
1032 arg1 = valuePop(ctxt);
1033 if (arg1 == NULL)
1034 ERROR0(XPATH_INVALID_OPERAND);
1035
1036 arg2 = valuePop(ctxt);
1037 if (arg2 == NULL) {
1038 xmlXPathFreeObject(arg1);
1039 ERROR0(XPATH_INVALID_OPERAND);
1040 }
1041
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001042 if (arg1 == arg2) {
1043#ifdef DEBUG_EXPR
1044 fprintf(xmlXPathDebug, "Equal: by pointer\n");
1045#endif
1046 return(1);
1047 }
Daniel Veillard991e63d1999-08-15 23:32:28 +00001048
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001049 switch (arg1->type) {
1050 case XPATH_UNDEFINED:
1051#ifdef DEBUG_EXPR
1052 fprintf(xmlXPathDebug, "Equal: undefined\n");
1053#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001054 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001055 case XPATH_NODESET:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001056 switch (arg2->type) {
1057 case XPATH_UNDEFINED:
1058#ifdef DEBUG_EXPR
1059 fprintf(xmlXPathDebug, "Equal: undefined\n");
1060#endif
1061 break;
1062 case XPATH_NODESET:
1063 ret = xmlXPathEqualNodeSets(arg1, arg2);
1064 break;
1065 case XPATH_BOOLEAN:
1066 if ((arg1->nodesetval == NULL) ||
1067 (arg1->nodesetval->nodeNr == 0)) ret = 0;
1068 else
1069 ret = 1;
1070 ret = (ret == arg2->boolval);
1071 break;
1072 case XPATH_NUMBER:
1073 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
1074 break;
1075 case XPATH_STRING:
1076 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
1077 break;
1078 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001079 break;
1080 case XPATH_BOOLEAN:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001081 switch (arg2->type) {
1082 case XPATH_UNDEFINED:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001083#ifdef DEBUG_EXPR
Daniel Veillard991e63d1999-08-15 23:32:28 +00001084 fprintf(xmlXPathDebug, "Equal: undefined\n");
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001085#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001086 break;
1087 case XPATH_NODESET:
1088 if ((arg2->nodesetval == NULL) ||
1089 (arg2->nodesetval->nodeNr == 0)) ret = 0;
1090 else
1091 ret = 1;
1092 break;
1093 case XPATH_BOOLEAN:
1094#ifdef DEBUG_EXPR
1095 fprintf(xmlXPathDebug, "Equal: %d boolean %d \n",
1096 arg1->boolval, arg2->boolval);
1097#endif
1098 ret = (arg1->boolval == arg2->boolval);
1099 break;
1100 case XPATH_NUMBER:
1101 if (arg2->floatval) ret = 1;
1102 else ret = 0;
1103 ret = (arg1->boolval == ret);
1104 break;
1105 case XPATH_STRING:
1106 if ((arg2->stringval == NULL) ||
1107 (arg2->stringval[0] == 0)) ret = 0;
1108 else
1109 ret = 1;
1110 ret = (arg1->boolval == ret);
1111 break;
1112 }
1113 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001114 case XPATH_NUMBER:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001115 switch (arg2->type) {
1116 case XPATH_UNDEFINED:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001117#ifdef DEBUG_EXPR
Daniel Veillard991e63d1999-08-15 23:32:28 +00001118 fprintf(xmlXPathDebug, "Equal: undefined\n");
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001119#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001120 break;
1121 case XPATH_NODESET:
1122 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
1123 break;
1124 case XPATH_BOOLEAN:
1125 if (arg1->floatval) ret = 1;
1126 else ret = 0;
1127 ret = (arg2->boolval == ret);
1128 break;
1129 case XPATH_STRING:
1130 valuePush(ctxt, arg2);
1131 xmlXPathNumberFunction(ctxt, 1);
1132 arg2 = valuePop(ctxt);
1133 /* no break on purpose */
1134 case XPATH_NUMBER:
1135 ret = (arg1->floatval == arg2->floatval);
1136 break;
1137 }
1138 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001139 case XPATH_STRING:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001140 switch (arg2->type) {
1141 case XPATH_UNDEFINED:
1142#ifdef DEBUG_EXPR
1143 fprintf(xmlXPathDebug, "Equal: undefined\n");
1144#endif
1145 break;
1146 case XPATH_NODESET:
1147 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
1148 break;
1149 case XPATH_BOOLEAN:
1150 if ((arg1->stringval == NULL) ||
1151 (arg1->stringval[0] == 0)) ret = 0;
1152 else
1153 ret = 1;
1154 ret = (arg2->boolval == ret);
1155 break;
1156 case XPATH_STRING:
1157 ret = !xmlStrcmp(arg1->stringval, arg2->stringval);
1158 break;
1159 case XPATH_NUMBER:
1160 valuePush(ctxt, arg1);
1161 xmlXPathNumberFunction(ctxt, 1);
1162 arg1 = valuePop(ctxt);
1163 ret = (arg1->floatval == arg2->floatval);
1164 break;
1165 }
1166 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001167#ifdef DEBUG_EXPR
1168 fprintf(xmlXPathDebug, "Equal: %s string %s \n",
1169 arg1->stringval, arg2->stringval);
1170#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001171 ret = !xmlStrcmp(arg1->stringval, arg2->stringval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001172 }
Daniel Veillard991e63d1999-08-15 23:32:28 +00001173 xmlXPathFreeObject(arg1);
1174 xmlXPathFreeObject(arg2);
1175 return(ret);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001176}
1177
1178/**
1179 * xmlXPathCompareValues:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001180 * @ctxt: the XPath Parser context
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001181 * @inf: less than (1) or greater than (2)
1182 * @strict: is the comparison strict
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001183 *
1184 * Implement the compare operation on XPath objects:
1185 * @arg1 < @arg2 (1, 1, ...
1186 * @arg1 <= @arg2 (1, 0, ...
1187 * @arg1 > @arg2 (0, 1, ...
1188 * @arg1 >= @arg2 (0, 0, ...
1189 *
Daniel Veillard991e63d1999-08-15 23:32:28 +00001190 * When neither object to be compared is a node-set and the operator is
1191 * <=, <, >=, >, then the objects are compared by converted both objects
1192 * to numbers and comparing the numbers according to IEEE 754. The <
1193 * comparison will be true if and only if the first number is less than the
1194 * second number. The <= comparison will be true if and only if the first
1195 * number is less than or equal to the second number. The > comparison
1196 * will be true if and only if the first number is greater than the second
1197 * number. The >= comparison will be true if and only if the first number
1198 * is greater than or equal to the second number.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001199 */
1200int
Daniel Veillard991e63d1999-08-15 23:32:28 +00001201xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
1202 int ret = 0;
1203 xmlXPathObjectPtr arg1, arg2;
1204
1205 arg2 = valuePop(ctxt);
1206 if ((arg2 == NULL) || (arg2->type == XPATH_NODESET)) {
1207 if (arg2 != NULL)
1208 xmlXPathFreeObject(arg2);
1209 ERROR0(XPATH_INVALID_OPERAND);
1210 }
1211
1212 arg1 = valuePop(ctxt);
1213 if ((arg1 == NULL) || (arg1->type == XPATH_NODESET)) {
1214 if (arg1 != NULL)
1215 xmlXPathFreeObject(arg1);
1216 xmlXPathFreeObject(arg2);
1217 ERROR0(XPATH_INVALID_OPERAND);
1218 }
1219
1220 if (arg1->type != XPATH_NUMBER) {
1221 valuePush(ctxt, arg1);
1222 xmlXPathNumberFunction(ctxt, 1);
1223 arg1 = valuePop(ctxt);
1224 }
1225 if (arg1->type != XPATH_NUMBER) {
1226 xmlXPathFreeObject(arg1);
1227 xmlXPathFreeObject(arg2);
1228 ERROR0(XPATH_INVALID_OPERAND);
1229 }
1230 if (arg2->type != XPATH_NUMBER) {
1231 valuePush(ctxt, arg2);
1232 xmlXPathNumberFunction(ctxt, 1);
1233 arg2 = valuePop(ctxt);
1234 }
1235 if (arg2->type != XPATH_NUMBER) {
1236 xmlXPathFreeObject(arg1);
1237 xmlXPathFreeObject(arg2);
1238 ERROR0(XPATH_INVALID_OPERAND);
1239 }
1240 /*
1241 * Add tests for infinity and nan
1242 * => feedback on 3.4 for Inf and NaN
1243 */
1244 if (inf && strict)
1245 ret = (arg1->floatval < arg2->floatval);
1246 else if (inf && !strict)
1247 ret = (arg1->floatval <= arg2->floatval);
1248 else if (!inf && strict)
1249 ret = (arg1->floatval > arg2->floatval);
1250 else if (!inf && !strict)
1251 ret = (arg1->floatval >= arg2->floatval);
1252 xmlXPathFreeObject(arg1);
1253 xmlXPathFreeObject(arg2);
1254 return(ret);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001255}
1256
1257/**
1258 * xmlXPathValueFlipSign:
1259 * @ctxt: the XPath Parser context
1260 *
1261 * Implement the unary - operation on an XPath object
1262 * The numeric operators convert their operands to numbers as if
1263 * by calling the number function.
1264 */
1265void
1266xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
1267 xmlXPathObjectPtr arg;
1268
1269 POP_FLOAT
1270 arg->floatval = -arg->floatval;
1271 valuePush(ctxt, arg);
1272}
1273
1274/**
1275 * xmlXPathAddValues:
1276 * @ctxt: the XPath Parser context
1277 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001278 * Implement the add operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001279 * The numeric operators convert their operands to numbers as if
1280 * by calling the number function.
1281 */
1282void
1283xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
1284 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001285 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001286
1287 POP_FLOAT
1288 val = arg->floatval;
1289 xmlXPathFreeObject(arg);
1290
1291 POP_FLOAT
1292 arg->floatval += val;
1293 valuePush(ctxt, arg);
1294}
1295
1296/**
1297 * xmlXPathSubValues:
1298 * @ctxt: the XPath Parser context
1299 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001300 * Implement the substraction operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001301 * The numeric operators convert their operands to numbers as if
1302 * by calling the number function.
1303 */
1304void
1305xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
1306 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001307 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001308
1309 POP_FLOAT
1310 val = arg->floatval;
1311 xmlXPathFreeObject(arg);
1312
1313 POP_FLOAT
1314 arg->floatval -= val;
1315 valuePush(ctxt, arg);
1316}
1317
1318/**
1319 * xmlXPathMultValues:
1320 * @ctxt: the XPath Parser context
1321 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001322 * Implement the multiply operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001323 * The numeric operators convert their operands to numbers as if
1324 * by calling the number function.
1325 */
1326void
1327xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
1328 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001329 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001330
1331 POP_FLOAT
1332 val = arg->floatval;
1333 xmlXPathFreeObject(arg);
1334
1335 POP_FLOAT
1336 arg->floatval *= val;
1337 valuePush(ctxt, arg);
1338}
1339
1340/**
1341 * xmlXPathDivValues:
1342 * @ctxt: the XPath Parser context
1343 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001344 * Implement the div operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001345 * The numeric operators convert their operands to numbers as if
1346 * by calling the number function.
1347 */
1348void
1349xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
1350 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001351 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001352
1353 POP_FLOAT
1354 val = arg->floatval;
1355 xmlXPathFreeObject(arg);
1356
1357 POP_FLOAT
1358 arg->floatval /= val;
1359 valuePush(ctxt, arg);
1360}
1361
1362/**
1363 * xmlXPathModValues:
1364 * @ctxt: the XPath Parser context
1365 *
1366 * Implement the div operation on XPath objects: @arg1 / @arg2
1367 * The numeric operators convert their operands to numbers as if
1368 * by calling the number function.
1369 */
1370void
1371xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
1372 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001373 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001374
1375 POP_FLOAT
1376 val = arg->floatval;
1377 xmlXPathFreeObject(arg);
1378
1379 POP_FLOAT
1380 arg->floatval /= val;
1381 valuePush(ctxt, arg);
1382}
1383
1384/************************************************************************
1385 * *
1386 * The traversal functions *
1387 * *
1388 ************************************************************************/
1389
1390#define AXIS_ANCESTOR 1
1391#define AXIS_ANCESTOR_OR_SELF 2
1392#define AXIS_ATTRIBUTE 3
1393#define AXIS_CHILD 4
1394#define AXIS_DESCENDANT 5
1395#define AXIS_DESCENDANT_OR_SELF 6
1396#define AXIS_FOLLOWING 7
1397#define AXIS_FOLLOWING_SIBLING 8
1398#define AXIS_NAMESPACE 9
1399#define AXIS_PARENT 10
1400#define AXIS_PRECEDING 11
1401#define AXIS_PRECEDING_SIBLING 12
1402#define AXIS_SELF 13
1403
1404/*
1405 * A traversal function enumerates nodes along an axis.
1406 * Initially it must be called with NULL, and it indicates
1407 * termination on the axis by returning NULL.
1408 */
1409typedef xmlNodePtr (*xmlXPathTraversalFunction)
1410 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
1411
1412/**
1413 * mlXPathNextSelf:
1414 * @ctxt: the XPath Parser context
1415 * @cur: the current node in the traversal
1416 *
1417 * Traversal function for the "self" direction
1418 * he self axis contains just the context node itself
Daniel Veillardb96e6431999-08-29 21:02:19 +00001419 *
1420 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001421 */
1422xmlNodePtr
1423xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1424 if (cur == NULL)
1425 return(ctxt->context->node);
1426 return(NULL);
1427}
1428
1429/**
1430 * mlXPathNextChild:
1431 * @ctxt: the XPath Parser context
1432 * @cur: the current node in the traversal
1433 *
1434 * Traversal function for the "child" direction
1435 * The child axis contains the children of the context node in document order.
Daniel Veillardb96e6431999-08-29 21:02:19 +00001436 *
1437 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001438 */
1439xmlNodePtr
1440xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001441 if (cur == NULL) {
1442 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
1443 return(ctxt->context->doc->root);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001444 return(ctxt->context->node->childs);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001445 }
1446 if (ctxt->context->node->type == XML_DOCUMENT_NODE)
1447 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001448 return(cur->next);
1449}
1450
1451/**
1452 * mlXPathNextDescendant:
1453 * @ctxt: the XPath Parser context
1454 * @cur: the current node in the traversal
1455 *
1456 * Traversal function for the "descendant" direction
1457 * the descendant axis contains the descendants of the context node in document
1458 * order; a descendant is a child or a child of a child and so on.
Daniel Veillardb96e6431999-08-29 21:02:19 +00001459 *
1460 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001461 */
1462xmlNodePtr
1463xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001464 if (cur == NULL) {
1465 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
1466 return(ctxt->context->doc->root);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001467 return(ctxt->context->node->childs);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001468 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001469
1470 if (cur->childs != NULL) return(cur->childs);
1471 if (cur->next != NULL) return(cur->next);
1472
1473 do {
1474 cur = cur->parent;
1475 if (cur == NULL) return(NULL);
1476 if (cur == ctxt->context->node) return(NULL);
1477 if (cur->next != NULL) {
1478 cur = cur->next;
1479 return(cur);
1480 }
1481 } while (cur != NULL);
1482 return(cur);
1483}
1484
1485/**
1486 * mlXPathNextDescendantOrSelf:
1487 * @ctxt: the XPath Parser context
1488 * @cur: the current node in the traversal
1489 *
1490 * Traversal function for the "descendant-or-self" direction
1491 * the descendant-or-self axis contains the context node and the descendants
1492 * of the context node in document order; thus the context node is the first
1493 * node on the axis, and the first child of the context node is the second node
1494 * on the axis
Daniel Veillardb96e6431999-08-29 21:02:19 +00001495 *
1496 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001497 */
1498xmlNodePtr
1499xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1500 if (cur == NULL)
1501 return(ctxt->context->node);
1502
Daniel Veillardb05deb71999-08-10 19:04:08 +00001503 if (cur == (xmlNodePtr) ctxt->context->doc)
1504 return(ctxt->context->doc->root);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001505 if (cur->childs != NULL) return(cur->childs);
1506 if (cur->next != NULL) return(cur->next);
1507
1508 do {
1509 cur = cur->parent;
1510 if (cur == NULL) return(NULL);
1511 if (cur == ctxt->context->node) return(NULL);
1512 if (cur->next != NULL) {
1513 cur = cur->next;
1514 return(cur);
1515 }
1516 } while (cur != NULL);
1517 return(cur);
1518}
1519
1520/**
1521 * xmlXPathNextParent:
1522 * @ctxt: the XPath Parser context
1523 * @cur: the current node in the traversal
1524 *
1525 * Traversal function for the "parent" direction
1526 * The parent axis contains the parent of the context node, if there is one.
Daniel Veillardb96e6431999-08-29 21:02:19 +00001527 *
1528 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001529 */
1530xmlNodePtr
1531xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1532 /*
1533 * !!!!!!!!!!!!!
1534 * the parent of an attribute or namespace node is the element
1535 * to which the attribute or namespace node is attached
1536 */
Daniel Veillardb05deb71999-08-10 19:04:08 +00001537 if (cur == NULL) {
1538 if (ctxt->context->node->parent == NULL)
1539 return((xmlNodePtr) ctxt->context->doc);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001540 return(ctxt->context->node->parent);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001541 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001542 return(NULL);
1543}
1544
1545/**
1546 * xmlXPathNextAncestor:
1547 * @ctxt: the XPath Parser context
1548 * @cur: the current node in the traversal
1549 *
1550 * Traversal function for the "ancestor" direction
1551 * the ancestor axis contains the ancestors of the context node; the ancestors
1552 * of the context node consist of the parent of context node and the parent's
1553 * parent and so on; the nodes are ordered in reverse document order; thus the
1554 * parent is the first node on the axis, and the parent's parent is the second
1555 * node on the axis
Daniel Veillardb96e6431999-08-29 21:02:19 +00001556 *
1557 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001558 */
1559xmlNodePtr
1560xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1561 /*
1562 * !!!!!!!!!!!!!
1563 * the parent of an attribute or namespace node is the element
1564 * to which the attribute or namespace node is attached
1565 */
Daniel Veillardb05deb71999-08-10 19:04:08 +00001566 if (cur == NULL) {
1567 if (ctxt->context->node->parent == NULL)
1568 return((xmlNodePtr) ctxt->context->doc);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001569 return(ctxt->context->node->parent);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001570 }
1571 if (cur == ctxt->context->doc->root)
1572 return((xmlNodePtr) ctxt->context->doc);
1573 if (cur == (xmlNodePtr) ctxt->context->doc)
1574 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001575 return(cur->parent);
1576}
1577
1578/**
1579 * xmlXPathNextAncestorOrSelf:
1580 * @ctxt: the XPath Parser context
1581 * @cur: the current node in the traversal
1582 *
1583 * Traversal function for the "ancestor-or-self" direction
Daniel Veillardb96e6431999-08-29 21:02:19 +00001584 * he ancestor-or-self axis contains the context node and ancestors of
1585 * the context node in reverse document order; thus the context node is
1586 * the first node on the axis, and the context node's parent the second;
1587 * parent here is defined the same as with the parent axis.
1588 *
1589 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001590 */
1591xmlNodePtr
1592xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1593 /*
1594 * !!!!!!!!!!!!!
1595 * the parent of an attribute or namespace node is the element
1596 * to which the attribute or namespace node is attached
1597 */
1598 if (cur == NULL)
1599 return(ctxt->context->node);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001600 if (cur == ctxt->context->doc->root)
1601 return((xmlNodePtr) ctxt->context->doc);
1602 if (cur == (xmlNodePtr) ctxt->context->doc)
1603 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001604 return(cur->parent);
1605}
1606
1607/**
1608 * xmlXPathNextFollowingSibling:
1609 * @ctxt: the XPath Parser context
1610 * @cur: the current node in the traversal
1611 *
1612 * Traversal function for the "following-sibling" direction
1613 * The following-sibling axis contains the following siblings of the context
1614 * node in document order.
Daniel Veillardb96e6431999-08-29 21:02:19 +00001615 *
1616 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001617 */
1618xmlNodePtr
1619xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001620 if (cur == (xmlNodePtr) ctxt->context->doc)
1621 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001622 if (cur == NULL)
1623 return(ctxt->context->node->next);
1624 return(cur->next);
1625}
1626
1627/**
1628 * xmlXPathNextPrecedingSibling:
1629 * @ctxt: the XPath Parser context
1630 * @cur: the current node in the traversal
1631 *
1632 * Traversal function for the "preceding-sibling" direction
1633 * The preceding-sibling axis contains the preceding siblings of the context
1634 * node in reverse document order; the first preceding sibling is first on the
1635 * axis; the sibling preceding that node is the second on the axis and so on.
Daniel Veillardb96e6431999-08-29 21:02:19 +00001636 *
1637 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001638 */
1639xmlNodePtr
1640xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001641 if (cur == (xmlNodePtr) ctxt->context->doc)
1642 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001643 if (cur == NULL)
1644 return(ctxt->context->node->prev);
1645 return(cur->prev);
1646}
1647
1648/**
1649 * xmlXPathNextFollowing:
1650 * @ctxt: the XPath Parser context
1651 * @cur: the current node in the traversal
1652 *
1653 * Traversal function for the "following" direction
1654 * The following axis contains all nodes in the same document as the context
1655 * node that are after the context node in document order, excluding any
1656 * descendants and excluding attribute nodes and namespace nodes; the nodes
1657 * are ordered in document order
Daniel Veillardb96e6431999-08-29 21:02:19 +00001658 *
1659 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001660 */
1661xmlNodePtr
1662xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001663 if (cur == (xmlNodePtr) ctxt->context->doc)
1664 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001665 if (cur == NULL)
1666 return(ctxt->context->node->next);; /* !!!!!!!!! */
1667 if (cur->childs != NULL) return(cur->childs);
1668 if (cur->next != NULL) return(cur->next);
1669
1670 do {
1671 cur = cur->parent;
1672 if (cur == NULL) return(NULL);
1673 if (cur == ctxt->context->doc->root) return(NULL);
1674 if (cur->next != NULL) {
1675 cur = cur->next;
1676 return(cur);
1677 }
1678 } while (cur != NULL);
1679 return(cur);
1680}
1681
1682/**
1683 * xmlXPathNextPreceding:
1684 * @ctxt: the XPath Parser context
1685 * @cur: the current node in the traversal
1686 *
1687 * Traversal function for the "preceding" direction
1688 * the preceding axis contains all nodes in the same document as the context
1689 * node that are before the context node in document order, excluding any
1690 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
1691 * ordered in reverse document order
Daniel Veillardb96e6431999-08-29 21:02:19 +00001692 *
1693 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001694 */
1695xmlNodePtr
1696xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001697 if (cur == (xmlNodePtr) ctxt->context->doc)
1698 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001699 if (cur == NULL)
1700 return(ctxt->context->node->prev); /* !!!!!!!!! */
1701 if (cur->last != NULL) return(cur->last);
1702 if (cur->prev != NULL) return(cur->prev);
1703
1704 do {
1705 cur = cur->parent;
1706 if (cur == NULL) return(NULL);
1707 if (cur == ctxt->context->doc->root) return(NULL);
1708 if (cur->prev != NULL) {
1709 cur = cur->prev;
1710 return(cur);
1711 }
1712 } while (cur != NULL);
1713 return(cur);
1714}
1715
1716/**
1717 * xmlXPathNextNamespace:
1718 * @ctxt: the XPath Parser context
1719 * @cur: the current attribute in the traversal
1720 *
1721 * Traversal function for the "namespace" direction
1722 * the namespace axis contains the namespace nodes of the context node;
1723 * the order of nodes on this axis is implementation-defined; the axis will
1724 * be empty unless the context node is an element
Daniel Veillardb96e6431999-08-29 21:02:19 +00001725 *
1726 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001727 */
Daniel Veillardb96e6431999-08-29 21:02:19 +00001728xmlNsPtr
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001729xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00001730 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
1731 if (ctxt->context->namespaces != NULL)
1732 free(ctxt->context->namespaces);
1733 ctxt->context->namespaces =
1734 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
1735 if (ctxt->context->namespaces == NULL) return(NULL);
1736 ctxt->context->nsNr = 0;
1737 }
1738 return(ctxt->context->namespaces[ctxt->context->nsNr++]);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001739}
1740
1741/**
1742 * xmlXPathNextAttribute:
1743 * @ctxt: the XPath Parser context
1744 * @cur: the current attribute in the traversal
1745 *
1746 * Traversal function for the "attribute" direction
Daniel Veillardb96e6431999-08-29 21:02:19 +00001747 *
1748 * Returns the next element following that axis
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001749 */
1750xmlAttrPtr
1751xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard991e63d1999-08-15 23:32:28 +00001752 if (cur == NULL) {
1753 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
1754 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001755 return(ctxt->context->node->properties);
Daniel Veillard991e63d1999-08-15 23:32:28 +00001756 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001757 return(cur->next);
1758}
1759
1760/************************************************************************
1761 * *
1762 * NodeTest Functions *
1763 * *
1764 ************************************************************************/
1765
1766#define NODE_TEST_NONE 0
1767#define NODE_TEST_TYPE 1
1768#define NODE_TEST_PI 2
1769#define NODE_TEST_ALL 3
1770#define NODE_TEST_NS 4
1771#define NODE_TEST_NAME 5
1772
1773#define NODE_TYPE_COMMENT 50
1774#define NODE_TYPE_TEXT 51
1775#define NODE_TYPE_PI 52
1776#define NODE_TYPE_NODE 53
1777
1778#define IS_FUNCTION 200
1779
1780/**
1781 * xmlXPathNodeCollectAndTest:
1782 * @ctxt: the XPath Parser context
1783 * @cur: the current node to test
1784 *
1785 * This is the function implementing a step: based on the current list
1786 * of nodes, it builds up a new list, looking at all nodes under that
1787 * axis and selecting them.
1788 *
1789 * Returns the new NodeSet resulting from the search.
1790 */
1791xmlNodeSetPtr
1792xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis,
1793 int test, int type, const CHAR *prefix, const CHAR *name) {
1794#ifdef DEBUG_STEP
1795 int n = 0, t = 0;
1796#endif
1797 int i;
1798 xmlNodeSetPtr ret;
1799 xmlXPathTraversalFunction next = NULL;
1800 xmlNodePtr cur = NULL;
1801
1802 if (ctxt->context->nodelist == NULL) {
1803 if (ctxt->context->node == NULL) {
1804 fprintf(xmlXPathDebug,
1805 "xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n",
1806 __FILE__, __LINE__);
1807 return(NULL);
1808 }
1809 STRANGE
1810 return(NULL);
1811 }
1812#ifdef DEBUG_STEP
1813 fprintf(xmlXPathDebug, "new step : ");
1814#endif
1815 switch (axis) {
1816 case AXIS_ANCESTOR:
1817#ifdef DEBUG_STEP
1818 fprintf(xmlXPathDebug, "axis 'ancestors' ");
1819#endif
1820 next = xmlXPathNextAncestor; break;
1821 case AXIS_ANCESTOR_OR_SELF:
1822#ifdef DEBUG_STEP
1823 fprintf(xmlXPathDebug, "axis 'ancestors-or-self' ");
1824#endif
1825 next = xmlXPathNextAncestorOrSelf; break;
1826 case AXIS_ATTRIBUTE:
1827#ifdef DEBUG_STEP
1828 fprintf(xmlXPathDebug, "axis 'attributes' ");
1829#endif
Daniel Veillardb96e6431999-08-29 21:02:19 +00001830 next = (xmlXPathTraversalFunction) xmlXPathNextAttribute; break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001831 break;
1832 case AXIS_CHILD:
1833#ifdef DEBUG_STEP
1834 fprintf(xmlXPathDebug, "axis 'child' ");
1835#endif
1836 next = xmlXPathNextChild; break;
1837 case AXIS_DESCENDANT:
1838#ifdef DEBUG_STEP
1839 fprintf(xmlXPathDebug, "axis 'descendant' ");
1840#endif
1841 next = xmlXPathNextDescendant; break;
1842 case AXIS_DESCENDANT_OR_SELF:
1843#ifdef DEBUG_STEP
1844 fprintf(xmlXPathDebug, "axis 'descendant-or-self' ");
1845#endif
1846 next = xmlXPathNextDescendantOrSelf; break;
1847 case AXIS_FOLLOWING:
1848#ifdef DEBUG_STEP
1849 fprintf(xmlXPathDebug, "axis 'following' ");
1850#endif
1851 next = xmlXPathNextFollowing; break;
1852 case AXIS_FOLLOWING_SIBLING:
1853#ifdef DEBUG_STEP
1854 fprintf(xmlXPathDebug, "axis 'following-siblings' ");
1855#endif
1856 next = xmlXPathNextFollowingSibling; break;
1857 case AXIS_NAMESPACE:
1858#ifdef DEBUG_STEP
1859 fprintf(xmlXPathDebug, "axis 'namespace' ");
1860#endif
Daniel Veillardb96e6431999-08-29 21:02:19 +00001861 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001862 break;
1863 case AXIS_PARENT:
1864#ifdef DEBUG_STEP
1865 fprintf(xmlXPathDebug, "axis 'parent' ");
1866#endif
1867 next = xmlXPathNextParent; break;
1868 case AXIS_PRECEDING:
1869#ifdef DEBUG_STEP
1870 fprintf(xmlXPathDebug, "axis 'preceding' ");
1871#endif
1872 next = xmlXPathNextPreceding; break;
1873 case AXIS_PRECEDING_SIBLING:
1874#ifdef DEBUG_STEP
1875 fprintf(xmlXPathDebug, "axis 'preceding-sibling' ");
1876#endif
1877 next = xmlXPathNextPrecedingSibling; break;
1878 case AXIS_SELF:
1879#ifdef DEBUG_STEP
1880 fprintf(xmlXPathDebug, "axis 'self' ");
1881#endif
1882 next = xmlXPathNextSelf; break;
1883 }
1884 if (next == NULL) return(NULL);
1885 ret = xmlXPathNodeSetCreate(NULL);
1886#ifdef DEBUG_STEP
1887 fprintf(xmlXPathDebug, " context contains %d nodes\n",
1888 ctxt->context->nodelist->nodeNr);
1889 switch (test) {
1890 case NODE_TEST_NONE:
1891 fprintf(xmlXPathDebug, " seaching for none !!!\n");
1892 break;
1893 case NODE_TEST_TYPE:
1894 fprintf(xmlXPathDebug, " seaching for type %d\n", type);
1895 break;
1896 case NODE_TEST_PI:
1897 fprintf(xmlXPathDebug, " seaching for PI !!!\n");
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001898 break;
1899 case NODE_TEST_ALL:
1900 fprintf(xmlXPathDebug, " seaching for *\n");
1901 break;
1902 case NODE_TEST_NS:
1903 fprintf(xmlXPathDebug, " seaching for namespace %s\n",
1904 prefix);
1905 break;
1906 case NODE_TEST_NAME:
1907 fprintf(xmlXPathDebug, " seaching for name %s\n", name);
1908 if (prefix != NULL)
1909 fprintf(xmlXPathDebug, " with namespace %s\n",
1910 prefix);
1911 break;
1912 }
1913 fprintf(xmlXPathDebug, "Testing : ");
1914#endif
1915 for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) {
1916 ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
1917
1918 cur = NULL;
1919 do {
1920 cur = next(ctxt, cur);
1921 if (cur == NULL) break;
1922#ifdef DEBUG_STEP
1923 t++;
1924 fprintf(xmlXPathDebug, " %s", cur->name);
1925#endif
1926 switch (test) {
1927 case NODE_TEST_NONE:
1928 STRANGE
1929 return(NULL);
1930 case NODE_TEST_TYPE:
1931 if (cur->type == type) {
1932#ifdef DEBUG_STEP
1933 n++;
1934#endif
1935 xmlXPathNodeSetAdd(ret, cur);
1936 }
1937 break;
1938 case NODE_TEST_PI:
Daniel Veillardb96e6431999-08-29 21:02:19 +00001939 if (cur->type == XML_PI_NODE) {
1940 if ((name != NULL) &&
1941 (xmlStrcmp(name, cur->name)))
1942 break;
1943#ifdef DEBUG_STEP
1944 n++;
1945#endif
1946 xmlXPathNodeSetAdd(ret, cur);
1947 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001948 break;
1949 case NODE_TEST_ALL:
Daniel Veillardb96e6431999-08-29 21:02:19 +00001950 if ((cur->type == XML_ELEMENT_NODE) ||
1951 (cur->type == XML_ATTRIBUTE_NODE)) {
1952 /* !!! || (cur->type == XML_TEXT_NODE)) { */
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001953#ifdef DEBUG_STEP
1954 n++;
1955#endif
1956 xmlXPathNodeSetAdd(ret, cur);
1957 }
1958 break;
Daniel Veillardb96e6431999-08-29 21:02:19 +00001959 case NODE_TEST_NS: {
1960 TODO /* namespace search */
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001961 break;
Daniel Veillardb96e6431999-08-29 21:02:19 +00001962 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001963 case NODE_TEST_NAME:
Daniel Veillardb05deb71999-08-10 19:04:08 +00001964 switch (cur->type) {
1965 case XML_ELEMENT_NODE:
1966 if (!xmlStrcmp(name, cur->name) &&
1967 (((prefix == NULL) ||
1968 ((cur->ns != NULL) &&
1969 (!xmlStrcmp(prefix, cur->ns->href)))))) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001970#ifdef DEBUG_STEP
Daniel Veillardb05deb71999-08-10 19:04:08 +00001971 n++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001972#endif
Daniel Veillardb05deb71999-08-10 19:04:08 +00001973 xmlXPathNodeSetAdd(ret, cur);
1974 }
Daniel Veillardb96e6431999-08-29 21:02:19 +00001975 break;
1976 case XML_ATTRIBUTE_NODE: {
1977 xmlAttrPtr attr = (xmlAttrPtr) cur;
1978 if (!xmlStrcmp(name, attr->name)) {
1979#ifdef DEBUG_STEP
1980 n++;
1981#endif
1982 xmlXPathNodeSetAdd(ret, cur);
1983 }
1984 break;
1985 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00001986 default:
1987 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001988 }
1989 break;
1990
1991 }
1992 } while (cur != NULL);
1993 }
1994#ifdef DEBUG_STEP
1995 fprintf(xmlXPathDebug,
1996 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
1997#endif
1998 return(ret);
1999}
2000
2001
2002/************************************************************************
2003 * *
2004 * Implicit tree core function library *
2005 * *
2006 ************************************************************************/
2007
2008/**
2009 * xmlXPathRoot:
2010 * @ctxt: the XPath Parser context
2011 *
2012 * Initialize the context to the root of the document
2013 */
2014void
2015xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
2016 if (ctxt->context->nodelist != NULL)
2017 xmlXPathFreeNodeSet(ctxt->context->nodelist);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002018 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
2019 ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->node);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002020}
2021
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002022/************************************************************************
2023 * *
2024 * The explicit core function library *
2025 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
2026 * *
2027 ************************************************************************/
2028
2029
2030/**
2031 * xmlXPathLastFunction:
2032 * @ctxt: the XPath Parser context
2033 *
2034 * Implement the last() XPath function
2035 * The last function returns the number of nodes in the context node list.
2036 */
2037void
2038xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2039 CHECK_ARITY(0);
2040 if ((ctxt->context->nodelist == NULL) ||
2041 (ctxt->context->node == NULL) ||
2042 (ctxt->context->nodelist->nodeNr == 0)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002043 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002044 } else {
2045 valuePush(ctxt,
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002046 xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002047 }
2048}
2049
2050/**
2051 * xmlXPathPositionFunction:
2052 * @ctxt: the XPath Parser context
2053 *
2054 * Implement the position() XPath function
2055 * The position function returns the position of the context node in the
2056 * context node list. The first position is 1, and so the last positionr
2057 * will be equal to last().
2058 */
2059void
2060xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2061 int i;
2062
2063 CHECK_ARITY(0);
2064 if ((ctxt->context->nodelist == NULL) ||
2065 (ctxt->context->node == NULL) ||
2066 (ctxt->context->nodelist->nodeNr == 0)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002067 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002068 }
2069 for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) {
2070 if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002071 valuePush(ctxt, xmlXPathNewFloat((double) i + 1));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002072 return;
2073 }
2074 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002075 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002076}
2077
2078/**
2079 * xmlXPathCountFunction:
2080 * @ctxt: the XPath Parser context
2081 *
2082 * Implement the count() XPath function
2083 */
2084void
2085xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2086 xmlXPathObjectPtr cur;
2087
2088 CHECK_ARITY(1);
2089 CHECK_TYPE(XPATH_NODESET);
2090 cur = valuePop(ctxt);
2091
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002092 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002093 xmlXPathFreeObject(cur);
2094}
2095
2096/**
2097 * xmlXPathIdFunction:
2098 * @ctxt: the XPath Parser context
2099 *
2100 * Implement the id() XPath function
2101 * The id function selects elements by their unique ID
2102 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
2103 * then the result is the union of the result of applying id to the
2104 * string value of each of the nodes in the argument node-set. When the
2105 * argument to id is of any other type, the argument is converted to a
2106 * string as if by a call to the string function; the string is split
2107 * into a whitespace-separated list of tokens (whitespace is any sequence
2108 * of characters matching the production S); the result is a node-set
2109 * containing the elements in the same document as the context node that
2110 * have a unique ID equal to any of the tokens in the list.
2111 */
2112void
2113xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002114 const CHAR *tokens;
2115 const CHAR *cur;
2116 CHAR *ID;
2117 xmlAttrPtr attr;
2118 xmlNodePtr elem = NULL;
2119 xmlXPathObjectPtr ret, obj;
2120
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002121 CHECK_ARITY(1);
Daniel Veillardb96e6431999-08-29 21:02:19 +00002122 obj = valuePop(ctxt);
2123 if (obj == NULL) ERROR(XPATH_INVALID_OPERAND);
2124 if (obj->type == XPATH_NODESET) {
2125 TODO /* ID function in case of NodeSet */
2126 }
2127 if (obj->type != XPATH_STRING) {
2128 valuePush(ctxt, obj);
2129 xmlXPathStringFunction(ctxt, 1);
2130 obj = valuePop(ctxt);
2131 if (obj->type != XPATH_STRING) {
2132 xmlXPathFreeObject(obj);
2133 return;
2134 }
2135 }
2136 tokens = obj->stringval;
2137
2138 ret = xmlXPathNewNodeSet(NULL);
2139 valuePush(ctxt, ret);
2140 if (tokens == NULL) {
2141 xmlXPathFreeObject(obj);
2142 return;
2143 }
2144
2145 cur = tokens;
2146
2147 while (IS_BLANK(*cur)) cur++;
2148 while (*cur != 0) {
2149 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2150 (*cur == '.') || (*cur == '-') ||
2151 (*cur == '_') || (*cur == ':') ||
2152 (IS_COMBINING(*cur)) ||
2153 (IS_EXTENDER(*cur)))
2154 cur++;
2155
2156 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
2157
2158 ID = xmlStrndup(tokens, cur - tokens);
2159 attr = xmlGetID(ctxt->context->doc, ID);
2160 if (attr != NULL) {
2161 elem = attr->node;
2162 xmlXPathNodeSetAdd(ret->nodesetval, elem);
2163 }
2164 if (ID != NULL)
2165 free(ID);
2166
2167 while (IS_BLANK(*cur)) cur++;
2168 tokens = cur;
2169 }
2170 xmlXPathFreeObject(obj);
2171 return;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002172}
2173
2174/**
2175 * xmlXPathLocalPartFunction:
2176 * @ctxt: the XPath Parser context
2177 *
2178 * Implement the local-part() XPath function
2179 * The local-part function returns a string containing the local part
2180 * of the name of the node in the argument node-set that is first in
2181 * document order. If the node-set is empty or the first node has no
2182 * name, an empty string is returned. If the argument is omitted it
2183 * defaults to the context node.
2184 */
2185void
2186xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2187 xmlXPathObjectPtr cur;
2188
2189 CHECK_ARITY(1);
2190 CHECK_TYPE(XPATH_NODESET);
2191 cur = valuePop(ctxt);
2192
2193 if (cur->nodesetval->nodeNr == 0) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002194 valuePush(ctxt, xmlXPathNewCString(""));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002195 } else {
2196 int i = 0; /* Should be first in document order !!!!! */
2197 valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
2198 }
2199 xmlXPathFreeObject(cur);
2200}
2201
2202/**
2203 * xmlXPathNamespaceFunction:
2204 * @ctxt: the XPath Parser context
2205 *
2206 * Implement the namespace() XPath function
2207 * The namespace function returns a string containing the namespace URI
2208 * of the expanded name of the node in the argument node-set that is
2209 * first in document order. If the node-set is empty, the first node has
2210 * no name, or the expanded name has no namespace URI, an empty string
2211 * is returned. If the argument is omitted it defaults to the context node.
2212 */
2213void
2214xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2215 xmlXPathObjectPtr cur;
2216
Daniel Veillardb96e6431999-08-29 21:02:19 +00002217 if (nargs == 0) {
2218 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
2219 nargs = 1;
2220 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002221 CHECK_ARITY(1);
2222 CHECK_TYPE(XPATH_NODESET);
2223 cur = valuePop(ctxt);
2224
2225 if (cur->nodesetval->nodeNr == 0) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002226 valuePush(ctxt, xmlXPathNewCString(""));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002227 } else {
2228 int i = 0; /* Should be first in document order !!!!! */
2229
2230 if (cur->nodesetval->nodeTab[i]->ns == NULL)
Daniel Veillardb96e6431999-08-29 21:02:19 +00002231 valuePush(ctxt, xmlXPathNewCString(""));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002232 else
2233 valuePush(ctxt, xmlXPathNewString(
2234 cur->nodesetval->nodeTab[i]->ns->href));
2235 }
2236 xmlXPathFreeObject(cur);
2237}
2238
2239/**
2240 * xmlXPathNameFunction:
2241 * @ctxt: the XPath Parser context
2242 *
2243 * Implement the name() XPath function
2244 * The name function returns a string containing a QName representing
2245 * the name of the node in the argument node-set that is first in documenti
2246 * order. The QName must represent the name with respect to the namespace
2247 * declarations in effect on the node whose name is being represented.
2248 * Typically, this will be the form in which the name occurred in the XML
2249 * source. This need not be the case if there are namespace declarations
2250 * in effect on the node that associate multiple prefixes with the same
2251 * namespace. However, an implementation may include information about
2252 * the original prefix in its representation of nodes; in this case, an
2253 * implementation can ensure that the returned string is always the same
2254 * as the QName used in the XML source. If the argument it omitted it
2255 * defaults to the context node.
2256 * Libxml keep the original prefix so the "real qualified name" used is
2257 * returned.
2258 */
2259void
2260xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2261 xmlXPathObjectPtr cur;
2262
2263 CHECK_ARITY(1);
2264 CHECK_TYPE(XPATH_NODESET);
2265 cur = valuePop(ctxt);
2266
2267 if (cur->nodesetval->nodeNr == 0) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002268 valuePush(ctxt, xmlXPathNewCString(""));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002269 } else {
2270 int i = 0; /* Should be first in document order !!!!! */
2271
2272 if (cur->nodesetval->nodeTab[i]->ns == NULL)
2273 valuePush(ctxt, xmlXPathNewString(
2274 cur->nodesetval->nodeTab[i]->name));
2275
2276 else {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002277 char name[2000];
2278 sprintf(name, "%s:%s",
2279 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
2280 (char *) cur->nodesetval->nodeTab[i]->name);
2281 valuePush(ctxt, xmlXPathNewCString(name));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002282 }
2283 }
2284 xmlXPathFreeObject(cur);
2285}
2286
2287/**
2288 * xmlXPathStringFunction:
2289 * @ctxt: the XPath Parser context
2290 *
2291 * Implement the string() XPath function
2292 * he string function converts an object to a string as follows:
2293 * - A node-set is converted to a string by returning the value of
2294 * the node in the node-set that is first in document order.
2295 * If the node-set is empty, an empty string is returned.
2296 * - A number is converted to a string as follows
2297 * + NaN is converted to the string NaN
2298 * + positive zero is converted to the string 0
2299 * + negative zero is converted to the string 0
2300 * + positive infinity is converted to the string Infinity
2301 * + negative infinity is converted to the string -Infinity
2302 * + if the number is an integer, the number is represented in
2303 * decimal form as a Number with no decimal point and no leading
2304 * zeros, preceded by a minus sign (-) if the number is negative
2305 * + otherwise, the number is represented in decimal form as a
2306 * Number including a decimal point with at least one digit
2307 * before the decimal point and at least one digit after the
2308 * decimal point, preceded by a minus sign (-) if the number
2309 * is negative; there must be no leading zeros before the decimal
2310 * point apart possibly from the one required digit immediatelyi
2311 * before the decimal point; beyond the one required digit
2312 * after the decimal point there must be as many, but only as
2313 * many, more digits as are needed to uniquely distinguish the
2314 * number from all other IEEE 754 numeric values.
2315 * - The boolean false value is converted to the string false.
2316 * The boolean true value is converted to the string true.
2317 */
2318void
2319xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2320 xmlXPathObjectPtr cur;
2321
2322 CHECK_ARITY(1);
2323 cur = valuePop(ctxt);
2324 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
2325 switch (cur->type) {
2326 case XPATH_NODESET:
2327 if (cur->nodesetval->nodeNr == 0) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002328 valuePush(ctxt, xmlXPathNewCString(""));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002329 } else {
2330 CHAR *res;
2331 int i = 0; /* Should be first in document order !!!!! */
2332 res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
2333 valuePush(ctxt, xmlXPathNewString(res));
2334 free(res);
2335 }
2336 xmlXPathFreeObject(cur);
2337 return;
2338 case XPATH_STRING:
2339 valuePush(ctxt, cur);
2340 return;
2341 case XPATH_BOOLEAN:
Daniel Veillardb96e6431999-08-29 21:02:19 +00002342 if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true"));
2343 else valuePush(ctxt, xmlXPathNewCString("false"));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002344 xmlXPathFreeObject(cur);
2345 return;
2346 case XPATH_NUMBER: {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002347 char buf[100];
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002348
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002349 if (isnan(cur->floatval))
2350 sprintf(buf, "NaN");
2351 else if (isinf(cur->floatval) > 0)
2352 sprintf(buf, "+Infinity");
2353 else if (isinf(cur->floatval) < 0)
2354 sprintf(buf, "-Infinity");
2355 else
2356 sprintf(buf, "%0g", cur->floatval);
Daniel Veillardb96e6431999-08-29 21:02:19 +00002357 valuePush(ctxt, xmlXPathNewCString(buf));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002358 xmlXPathFreeObject(cur);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002359 return;
2360 }
2361 }
2362 STRANGE
2363}
2364
2365/**
2366 * xmlXPathStringLengthFunction:
2367 * @ctxt: the XPath Parser context
2368 *
2369 * Implement the string-length() XPath function
2370 * The string-length returns the number of characters in the string
2371 * (see [3.6 Strings]). If the argument is omitted, it defaults to
2372 * the context node converted to a string, in other words the value
2373 * of the context node.
2374 */
2375void
2376xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2377 xmlXPathObjectPtr cur;
2378
2379 if (nargs == 0) {
2380 if (ctxt->context->node == NULL) {
2381 valuePush(ctxt, xmlXPathNewFloat(0));
2382 } else {
2383 CHAR *content;
2384
2385 content = xmlNodeGetContent(ctxt->context->node);
2386 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
2387 free(content);
2388 }
2389 return;
2390 }
2391 CHECK_ARITY(1);
2392 CHECK_TYPE(XPATH_STRING);
2393 cur = valuePop(ctxt);
2394 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
2395 xmlXPathFreeObject(cur);
2396}
2397
2398/**
2399 * xmlXPathConcatFunction:
2400 * @ctxt: the XPath Parser context
2401 *
2402 * Implement the concat() XPath function
2403 * The concat function returns the concatenation of its arguments.
2404 */
2405void
2406xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2407 xmlXPathObjectPtr cur, new;
2408 CHAR *tmp;
2409
2410 if (nargs < 2) {
2411 CHECK_ARITY(2);
2412 }
2413
2414 cur = valuePop(ctxt);
2415 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
2416 xmlXPathFreeObject(cur);
2417 return;
2418 }
2419 nargs--;
2420
2421 while (nargs > 0) {
2422 new = valuePop(ctxt);
2423 if ((new == NULL) || (new->type != XPATH_STRING)) {
2424 xmlXPathFreeObject(new);
2425 xmlXPathFreeObject(cur);
2426 ERROR(XPATH_INVALID_TYPE);
2427 }
2428 tmp = xmlStrcat(new->stringval, cur->stringval);
2429 new->stringval = cur->stringval;
2430 cur->stringval = tmp;
2431
2432 xmlXPathFreeObject(new);
2433 nargs--;
2434 }
2435 valuePush(ctxt, cur);
2436}
2437
2438/**
2439 * xmlXPathContainsFunction:
2440 * @ctxt: the XPath Parser context
2441 *
2442 * Implement the contains() XPath function
2443 * The contains function returns true if the first argument string
2444 * contains the second argument string, and otherwise returns false.
2445 */
2446void
2447xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2448 xmlXPathObjectPtr hay, needle;
2449
2450 CHECK_ARITY(2);
2451 CHECK_TYPE(XPATH_STRING);
2452 needle = valuePop(ctxt);
2453 hay = valuePop(ctxt);
2454 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
2455 xmlXPathFreeObject(hay);
2456 xmlXPathFreeObject(needle);
2457 ERROR(XPATH_INVALID_TYPE);
2458 }
2459 if (xmlStrstr(hay->stringval, needle->stringval))
2460 valuePush(ctxt, xmlXPathNewBoolean(1));
2461 else
2462 valuePush(ctxt, xmlXPathNewBoolean(0));
2463 xmlXPathFreeObject(hay);
2464 xmlXPathFreeObject(needle);
2465}
2466
2467/**
2468 * xmlXPathStartsWithFunction:
2469 * @ctxt: the XPath Parser context
2470 *
2471 * Implement the starts-with() XPath function
2472 * The starts-with function returns true if the first argument string
2473 * starts with the second argument string, and otherwise returns false.
2474 */
2475void
2476xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2477 xmlXPathObjectPtr hay, needle;
2478 int n;
2479
2480 CHECK_ARITY(2);
2481 CHECK_TYPE(XPATH_STRING);
2482 needle = valuePop(ctxt);
2483 hay = valuePop(ctxt);
2484 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
2485 xmlXPathFreeObject(hay);
2486 xmlXPathFreeObject(needle);
2487 ERROR(XPATH_INVALID_TYPE);
2488 }
2489 n = xmlStrlen(needle->stringval);
2490 if (xmlStrncmp(hay->stringval, needle->stringval, n))
2491 valuePush(ctxt, xmlXPathNewBoolean(0));
2492 else
2493 valuePush(ctxt, xmlXPathNewBoolean(1));
2494 xmlXPathFreeObject(hay);
2495 xmlXPathFreeObject(needle);
2496}
2497
2498/**
2499 * xmlXPathSubstringFunction:
2500 * @ctxt: the XPath Parser context
2501 *
2502 * Implement the substring() XPath function
2503 * The substring function returns the substring of the first argument
2504 * starting at the position specified in the second argument with
2505 * length specified in the third argument. For example,
2506 * substring("12345",2,3) returns "234". If the third argument is not
2507 * specified, it returns the substring starting at the position specified
2508 * in the second argument and continuing to the end of the string. For
2509 * example, substring("12345",2) returns "2345". More precisely, each
2510 * character in the string (see [3.6 Strings]) is considered to have a
2511 * numeric position: the position of the first character is 1, the position
2512 * of the second character is 2 and so on. The returned substring contains
2513 * those characters for which the position of the character is greater than
2514 * or equal to the second argument and, if the third argument is specified,
2515 * less than the sum of the second and third arguments; the comparisons
2516 * and addition used for the above follow the standard IEEE 754 rules. Thus:
2517 * - substring("12345", 1.5, 2.6) returns "234"
2518 * - substring("12345", 0, 3) returns "12"
2519 * - substring("12345", 0 div 0, 3) returns ""
2520 * - substring("12345", 1, 0 div 0) returns ""
2521 * - substring("12345", -42, 1 div 0) returns "12345"
2522 * - substring("12345", -1 div 0, 1 div 0) returns ""
2523 */
2524void
2525xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2526 xmlXPathObjectPtr str, start, len;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002527 double le, in;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002528 int i, l;
2529 CHAR *ret;
2530
2531 /*
2532 * Conformance needs to be checked !!!!!
2533 */
2534 if (nargs < 2) {
2535 CHECK_ARITY(2);
2536 }
2537 if (nargs > 3) {
2538 CHECK_ARITY(3);
2539 }
2540 if (nargs == 3) {
2541 CHECK_TYPE(XPATH_NUMBER);
2542 len = valuePop(ctxt);
2543 le = len->floatval;
2544 xmlXPathFreeObject(len);
2545 } else {
2546 le = 2000000000;
2547 }
2548 CHECK_TYPE(XPATH_NUMBER);
2549 start = valuePop(ctxt);
2550 in = start->floatval;
2551 xmlXPathFreeObject(start);
2552 CHECK_TYPE(XPATH_STRING);
2553 str = valuePop(ctxt);
2554 le += in;
2555
2556 /* integer index of the first char */
2557 i = in;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002558 if (((double)i) != in) i++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002559
2560 /* integer index of the last char */
2561 l = le;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002562 if (((double)l) != le) l++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002563
2564 /* back to a zero based len */
2565 i--;
2566 l--;
2567
2568 /* check against the string len */
2569 if (l > 1024) {
2570 l = xmlStrlen(str->stringval);
2571 }
2572 if (i < 0) {
2573 i = 0;
2574 }
2575
2576 /* number of chars to copy */
2577 l -= i;
2578
2579 ret = xmlStrsub(str->stringval, i, l);
2580 if (ret == NULL)
Daniel Veillardb96e6431999-08-29 21:02:19 +00002581 valuePush(ctxt, xmlXPathNewCString(""));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002582 else {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002583 valuePush(ctxt, xmlXPathNewString(ret));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002584 free(ret);
2585 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002586 xmlXPathFreeObject(str);
2587}
2588
2589/**
2590 * xmlXPathSubstringBeforeFunction:
2591 * @ctxt: the XPath Parser context
2592 *
2593 * Implement the substring-before() XPath function
2594 * The substring-before function returns the substring of the first
2595 * argument string that precedes the first occurrence of the second
2596 * argument string in the first argument string, or the empty string
2597 * if the first argument string does not contain the second argument
2598 * string. For example, substring-before("1999/04/01","/") returns 1999.
2599 */
2600void
2601xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2602 CHECK_ARITY(2);
2603 TODO /* substring before */
2604}
2605
2606/**
2607 * xmlXPathSubstringAfterFunction:
2608 * @ctxt: the XPath Parser context
2609 *
2610 * Implement the substring-after() XPath function
2611 * The substring-after function returns the substring of the first
2612 * argument string that follows the first occurrence of the second
2613 * argument string in the first argument string, or the empty stringi
2614 * if the first argument string does not contain the second argument
2615 * string. For example, substring-after("1999/04/01","/") returns 04/01,
2616 * and substring-after("1999/04/01","19") returns 99/04/01.
2617 */
2618void
2619xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2620 CHECK_ARITY(2);
2621 TODO /* substring after */
2622}
2623
2624/**
2625 * xmlXPathNormalizeFunction:
2626 * @ctxt: the XPath Parser context
2627 *
2628 * Implement the normalize() XPath function
2629 * The normalize function returns the argument string with white
2630 * space normalized by stripping leading and trailing whitespace
2631 * and replacing sequences of whitespace characters by a single
2632 * space. Whitespace characters are the same allowed by the S production
2633 * in XML. If the argument is omitted, it defaults to the context
2634 * node converted to a string, in other words the value of the context node.
2635 */
2636void
2637xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2638 CHECK_ARITY(1);
2639 TODO /* normalize isn't as boring as translate, but pretty much */
2640}
2641
2642/**
2643 * xmlXPathTranslateFunction:
2644 * @ctxt: the XPath Parser context
2645 *
2646 * Implement the translate() XPath function
2647 * The translate function returns the first argument string with
2648 * occurrences of characters in the second argument string replaced
2649 * by the character at the corresponding position in the third argument
2650 * string. For example, translate("bar","abc","ABC") returns the string
2651 * BAr. If there is a character in the second argument string with no
2652 * character at a corresponding position in the third argument string
2653 * (because the second argument string is longer than the third argument
2654 * string), then occurrences of that character in the first argument
2655 * string are removed. For example, translate("--aaa--","abc-","ABC")
2656 * returns "AAA". If a character occurs more than once in second
2657 * argument string, then the first occurrence determines the replacement
2658 * character. If the third argument string is longer than the second
2659 * argument string, then excess characters are ignored.
2660 */
2661void
2662xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2663 CHECK_ARITY(3);
2664 TODO /* translate is boring, waiting for UTF-8 representation too */
2665}
2666
2667/**
2668 * xmlXPathBooleanFunction:
2669 * @ctxt: the XPath Parser context
2670 *
2671 * Implement the boolean() XPath function
2672 * he boolean function converts its argument to a boolean as follows:
2673 * - a number is true if and only if it is neither positive or
2674 * negative zero nor NaN
2675 * - a node-set is true if and only if it is non-empty
2676 * - a string is true if and only if its length is non-zero
2677 */
2678void
2679xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2680 xmlXPathObjectPtr cur;
2681 int res = 0;
2682
2683 CHECK_ARITY(1);
2684 cur = valuePop(ctxt);
2685 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
2686 switch (cur->type) {
2687 case XPATH_NODESET:
2688 if ((cur->nodesetval == NULL) ||
2689 (cur->nodesetval->nodeNr == 0)) res = 0;
2690 else
2691 res = 1;
2692 break;
2693 case XPATH_STRING:
2694 if ((cur->stringval == NULL) ||
2695 (cur->stringval[0] == 0)) res = 0;
2696 else
2697 res = 1;
2698 break;
2699 case XPATH_BOOLEAN:
2700 valuePush(ctxt, cur);
2701 return;
2702 case XPATH_NUMBER:
2703 if (cur->floatval) res = 1;
2704 break;
2705 default:
2706 STRANGE
2707 }
2708 xmlXPathFreeObject(cur);
2709 valuePush(ctxt, xmlXPathNewBoolean(res));
2710}
2711
2712/**
2713 * xmlXPathNotFunction:
2714 * @ctxt: the XPath Parser context
2715 *
2716 * Implement the not() XPath function
2717 * The not function returns true if its argument is false,
2718 * and false otherwise.
2719 */
2720void
2721xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2722 CHECK_ARITY(1);
2723 CHECK_TYPE(XPATH_BOOLEAN);
2724 ctxt->value->boolval = ! ctxt->value->boolval;
2725}
2726
2727/**
2728 * xmlXPathTrueFunction:
2729 * @ctxt: the XPath Parser context
2730 *
2731 * Implement the true() XPath function
2732 */
2733void
2734xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2735 CHECK_ARITY(0);
2736 valuePush(ctxt, xmlXPathNewBoolean(1));
2737}
2738
2739/**
2740 * xmlXPathFalseFunction:
2741 * @ctxt: the XPath Parser context
2742 *
2743 * Implement the false() XPath function
2744 */
2745void
2746xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2747 CHECK_ARITY(0);
2748 valuePush(ctxt, xmlXPathNewBoolean(0));
2749}
2750
2751/**
2752 * xmlXPathLangFunction:
2753 * @ctxt: the XPath Parser context
2754 *
2755 * Implement the lang() XPath function
2756 * The lang function returns true or false depending on whether the
2757 * language of the context node as specified by xml:lang attributes
2758 * is the same as or is a sublanguage of the language specified by
2759 * the argument string. The language of the context node is determined
2760 * by the value of the xml:lang attribute on the context node, or, if
2761 * the context node has no xml:lang attribute, by the value of the
2762 * xml:lang attribute on the nearest ancestor of the context node that
2763 * has an xml:lang attribute. If there is no such attribute, then lang
2764 * returns false. If there is such an attribute, then lang returns
2765 * true if the attribute value is equal to the argument ignoring case,
2766 * or if there is some suffix starting with - such that the attribute
2767 * value is equal to the argument ignoring that suffix of the attribute
2768 * value and ignoring case.
2769 */
2770void
2771xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002772 xmlXPathObjectPtr val;
2773 const CHAR *theLang;
2774 const CHAR *lang;
2775 int ret = 0;
2776 int i;
2777
2778 CHECK_ARITY(1);
2779 CHECK_TYPE(XPATH_STRING);
2780 val = valuePop(ctxt);
2781 lang = val->stringval;
2782 theLang = xmlNodeGetLang(ctxt->context->node);
2783 if ((theLang != NULL) && (lang != NULL)) {
2784 for (i = 0;lang[i] != 0;i++)
2785 if (toupper(lang[i]) != toupper(theLang[i]))
2786 goto not_equal;
2787 ret = 1;
2788 }
2789not_equal:
2790 xmlXPathFreeObject(val);
2791 valuePush(ctxt, xmlXPathNewBoolean(ret));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002792}
2793
2794/**
2795 * xmlXPathNumberFunction:
2796 * @ctxt: the XPath Parser context
2797 *
2798 * Implement the number() XPath function
2799 */
2800void
2801xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2802 xmlXPathObjectPtr cur;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002803 double res;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002804
2805 CHECK_ARITY(1);
2806 cur = valuePop(ctxt);
2807 switch (cur->type) {
2808 case XPATH_NODESET:
2809 valuePush(ctxt, cur);
2810 xmlXPathStringFunction(ctxt, 1);
2811 cur = valuePop(ctxt);
2812 case XPATH_STRING:
2813 res = xmlXPathStringEvalNumber(cur->stringval);
2814 valuePush(ctxt, xmlXPathNewFloat(res));
2815 xmlXPathFreeObject(cur);
2816 return;
2817 case XPATH_BOOLEAN:
2818 if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
2819 else valuePush(ctxt, xmlXPathNewFloat(0.0));
2820 xmlXPathFreeObject(cur);
2821 return;
2822 case XPATH_NUMBER:
2823 valuePush(ctxt, cur);
2824 return;
2825 }
2826 STRANGE
2827}
2828
2829/**
2830 * xmlXPathSumFunction:
2831 * @ctxt: the XPath Parser context
2832 *
2833 * Implement the sum() XPath function
2834 * The sum function returns the sum of the values of the nodes in
2835 * the argument node-set.
2836 */
2837void
2838xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2839 CHECK_ARITY(1);
2840 TODO /* BUG Sum : don't understand the definition */
2841}
2842
2843/**
2844 * xmlXPathFloorFunction:
2845 * @ctxt: the XPath Parser context
2846 *
2847 * Implement the floor() XPath function
2848 * The floor function returns the largest (closest to positive infinity)
2849 * number that is not greater than the argument and that is an integer.
2850 */
2851void
2852xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2853 CHECK_ARITY(1);
2854 CHECK_TYPE(XPATH_NUMBER);
2855 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002856 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002857}
2858
2859/**
2860 * xmlXPathCeilingFunction:
2861 * @ctxt: the XPath Parser context
2862 *
2863 * Implement the ceiling() XPath function
2864 * The ceiling function returns the smallest (closest to negative infinity)
2865 * number that is not less than the argument and that is an integer.
2866 */
2867void
2868xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002869 double f;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002870
2871 CHECK_ARITY(1);
2872 CHECK_TYPE(XPATH_NUMBER);
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002873 f = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002874 if (f != ctxt->value->floatval)
2875 ctxt->value->floatval = f + 1;
2876}
2877
2878/**
2879 * xmlXPathRoundFunction:
2880 * @ctxt: the XPath Parser context
2881 *
2882 * Implement the round() XPath function
2883 * The round function returns the number that is closest to the
2884 * argument and that is an integer. If there are two such numbers,
2885 * then the one that is even is returned.
2886 */
2887void
2888xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002889 double f;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002890
2891 CHECK_ARITY(1);
2892 CHECK_TYPE(XPATH_NUMBER);
2893 /* round(0.50000001) => 0 !!!!! */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002894 f = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002895 if (ctxt->value->floatval < f + 0.5)
2896 ctxt->value->floatval = f;
2897 else if (ctxt->value->floatval == f + 0.5)
2898 ctxt->value->floatval = f; /* !!!! Not following the spec here */
2899 else
2900 ctxt->value->floatval = f + 1;
2901}
2902
2903/************************************************************************
2904 * *
2905 * The Parser *
2906 * *
2907 ************************************************************************/
2908
2909/*
2910 * a couple of forward declarations since we use a recursive call based
2911 * implementation.
2912 */
2913void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
2914void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
2915void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
2916void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
2917
2918/**
2919 * xmlXPathParseNCName:
2920 * @ctxt: the XPath Parser context
2921 *
2922 * parse an XML namespace non qualified name.
2923 *
2924 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
2925 *
2926 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
2927 * CombiningChar | Extender
2928 *
2929 * Returns the namespace name or NULL
2930 */
2931
2932CHAR *
2933xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
2934 const CHAR *q;
2935 CHAR *ret = NULL;
2936
2937 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
2938 q = NEXT;
2939
2940 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
2941 (CUR == '.') || (CUR == '-') ||
2942 (CUR == '_') ||
2943 (IS_COMBINING(CUR)) ||
2944 (IS_EXTENDER(CUR)))
2945 NEXT;
2946
2947 ret = xmlStrndup(q, CUR_PTR - q);
2948
2949 return(ret);
2950}
2951
2952/**
2953 * xmlXPathParseQName:
2954 * @ctxt: the XPath Parser context
2955 * @prefix: a CHAR **
2956 *
2957 * parse an XML qualified name
2958 *
2959 * [NS 5] QName ::= (Prefix ':')? LocalPart
2960 *
2961 * [NS 6] Prefix ::= NCName
2962 *
2963 * [NS 7] LocalPart ::= NCName
2964 *
2965 * Returns the function returns the local part, and prefix is updated
2966 * to get the Prefix if any.
2967 */
2968
2969CHAR *
2970xmlXPathParseQName(xmlXPathParserContextPtr ctxt, CHAR **prefix) {
2971 CHAR *ret = NULL;
2972
2973 *prefix = NULL;
2974 ret = xmlXPathParseNCName(ctxt);
2975 if (CUR == ':') {
2976 *prefix = ret;
2977 NEXT;
2978 ret = xmlXPathParseNCName(ctxt);
2979 }
2980 return(ret);
2981}
2982
2983/**
2984 * xmlXPathStringEvalNumber:
2985 * @str: A string to scan
2986 *
2987 * [30] Number ::= Digits ('.' Digits)?
2988 * | '.' Digits
2989 * [31] Digits ::= [0-9]+
2990 *
2991 * Parse and evaluate a Number in the string
2992 *
2993 * BUG: "1.' is not valid ... James promised correction
2994 * as Digits ('.' Digits?)?
2995 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002996 * Returns the double value.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002997 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002998double
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002999xmlXPathStringEvalNumber(const CHAR *str) {
3000 const CHAR *cur = str;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003001 double ret = 0.0;
3002 double mult = 1;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003003 int ok = 0;
3004
3005 while (*cur == ' ') cur++;
3006 if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003007 return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003008 }
3009 while ((*cur >= '0') && (*cur <= '9')) {
3010 ret = ret * 10 + (*cur - '0');
3011 ok = 1;
3012 cur++;
3013 }
3014 if (*cur == '.') {
3015 cur++;
3016 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003017 return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003018 }
3019 while ((*cur >= '0') && (*cur <= '9')) {
3020 mult /= 10;
3021 ret = ret + (*cur - '0') * mult;
3022 cur++;
3023 }
3024 }
3025 while (*cur == ' ') cur++;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003026 if (*cur != 0) return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003027 return(ret);
3028}
3029
3030/**
3031 * xmlXPathEvalNumber:
3032 * @ctxt: the XPath Parser context
3033 *
3034 * [30] Number ::= Digits ('.' Digits)?
3035 * | '.' Digits
3036 * [31] Digits ::= [0-9]+
3037 *
3038 * Parse and evaluate a Number, then push it on the stack
3039 *
3040 * BUG: "1.' is not valid ... James promised correction
3041 * as Digits ('.' Digits?)?
3042 */
3043void
3044xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003045 double ret = 0.0;
3046 double mult = 1;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003047 int ok = 0;
3048
3049 CHECK_ERROR;
3050 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
3051 ERROR(XPATH_NUMBER_ERROR);
3052 }
3053 while ((CUR >= '0') && (CUR <= '9')) {
3054 ret = ret * 10 + (CUR - '0');
3055 ok = 1;
3056 NEXT;
3057 }
3058 if (CUR == '.') {
3059 NEXT;
3060 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
3061 ERROR(XPATH_NUMBER_ERROR);
3062 }
3063 while ((CUR >= '0') && (CUR <= '9')) {
3064 mult /= 10;
3065 ret = ret + (CUR - '0') * mult;
3066 NEXT;
3067 }
3068 }
3069 valuePush(ctxt, xmlXPathNewFloat(ret));
3070}
3071
3072/**
3073 * xmlXPathEvalLiteral:
3074 * @ctxt: the XPath Parser context
3075 *
3076 * Parse a Literal and push it on the stack.
3077 *
3078 * [29] Literal ::= '"' [^"]* '"'
3079 * | "'" [^']* "'"
3080 *
Daniel Veillardb96e6431999-08-29 21:02:19 +00003081 * TODO: xmlXPathEvalLiteral memory allocation could be improved.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003082 */
3083void
3084xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
3085 const CHAR *q;
3086 CHAR *ret = NULL;
3087
3088 if (CUR == '"') {
3089 NEXT;
3090 q = CUR_PTR;
3091 while ((IS_CHAR(CUR)) && (CUR != '"'))
3092 NEXT;
3093 if (!IS_CHAR(CUR)) {
3094 ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
3095 } else {
3096 ret = xmlStrndup(q, CUR_PTR - q);
3097 NEXT;
3098 }
3099 } else if (CUR == '\'') {
3100 NEXT;
3101 q = CUR_PTR;
3102 while ((IS_CHAR(CUR)) && (CUR != '\''))
3103 NEXT;
3104 if (!IS_CHAR(CUR)) {
3105 ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
3106 } else {
3107 ret = xmlStrndup(q, CUR_PTR - q);
3108 NEXT;
3109 }
3110 } else {
3111 ERROR(XPATH_START_LITERAL_ERROR);
3112 }
3113 if (ret == NULL) return;
3114 valuePush(ctxt, xmlXPathNewString(ret));
3115 free(ret);
3116}
3117
3118/**
3119 * xmlXPathEvalVariableReference:
3120 * @ctxt: the XPath Parser context
3121 *
3122 * Parse a VariableReference, evaluate it and push it on the stack.
3123 *
3124 * The variable bindings consist of a mapping from variable names
3125 * to variable values. The value of a variable is an object, which
3126 * of any of the types that are possible for the value of an expression,
3127 * and may also be of additional types not specified here.
3128 *
3129 * Early evaluation is possible since:
3130 * The variable bindings [...] used to evaluate a subexpression are
3131 * always the same as those used to evaluate the containing expression.
3132 *
3133 * [36] VariableReference ::= '$' QName
3134 */
3135void
3136xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
3137 CHAR *name;
3138 CHAR *prefix;
3139 xmlXPathObjectPtr value;
3140
3141 if (CUR != '$') {
3142 ERROR(XPATH_VARIABLE_REF_ERROR);
3143 }
3144 name = xmlXPathParseQName(ctxt, &prefix);
3145 if (name == NULL) {
3146 ERROR(XPATH_VARIABLE_REF_ERROR);
3147 }
3148 value = xmlXPathVariablelookup(ctxt, prefix, name);
3149 if (value == NULL) {
3150 ERROR(XPATH_UNDEF_VARIABLE_ERROR);
3151 }
3152 valuePush(ctxt, value);
3153 if (prefix != NULL) free(prefix);
3154 free(name);
3155}
3156
3157
3158/**
3159 * xmlXPathFunctionLookup:
3160 * @ctxt: the XPath Parser context
3161 * @name: a name string
3162 *
3163 * Search for a function of the given name
3164 *
3165 * [35] FunctionName ::= QName - NodeType
3166 *
Daniel Veillardb96e6431999-08-29 21:02:19 +00003167 * TODO: for the moment the function list is hardcoded from the spec !!!!
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003168 *
3169 * Returns the xmlXPathFunction if found, or NULL otherwise
3170 */
3171xmlXPathFunction
Daniel Veillardb96e6431999-08-29 21:02:19 +00003172xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, const CHAR *name) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003173 switch (name[0]) {
3174 case 'b':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003175 if (!xmlStrcmp(name, BAD_CAST "boolean"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003176 return(xmlXPathBooleanFunction);
3177 break;
3178 case 'c':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003179 if (!xmlStrcmp(name, BAD_CAST "ceiling"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003180 return(xmlXPathCeilingFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003181 if (!xmlStrcmp(name, BAD_CAST "count"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003182 return(xmlXPathCountFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003183 if (!xmlStrcmp(name, BAD_CAST "concat"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003184 return(xmlXPathConcatFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003185 if (!xmlStrcmp(name, BAD_CAST "contains"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003186 return(xmlXPathContainsFunction);
3187 break;
3188 case 'i':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003189 if (!xmlStrcmp(name, BAD_CAST "id"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003190 return(xmlXPathIdFunction);
3191 break;
3192 case 'f':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003193 if (!xmlStrcmp(name, BAD_CAST "false"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003194 return(xmlXPathFalseFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003195 if (!xmlStrcmp(name, BAD_CAST "floor"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003196 return(xmlXPathFloorFunction);
3197 break;
3198 case 'l':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003199 if (!xmlStrcmp(name, BAD_CAST "last"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003200 return(xmlXPathLastFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003201 if (!xmlStrcmp(name, BAD_CAST "lang"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003202 return(xmlXPathLangFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003203 if (!xmlStrcmp(name, BAD_CAST "local-part"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003204 return(xmlXPathLocalPartFunction);
3205 break;
3206 case 'n':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003207 if (!xmlStrcmp(name, BAD_CAST "not"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003208 return(xmlXPathNotFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003209 if (!xmlStrcmp(name, BAD_CAST "name"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003210 return(xmlXPathNameFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003211 if (!xmlStrcmp(name, BAD_CAST "namespace"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003212 return(xmlXPathNamespaceFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003213 if (!xmlStrcmp(name, BAD_CAST "normalize"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003214 return(xmlXPathNormalizeFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003215 if (!xmlStrcmp(name, BAD_CAST "number"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003216 return(xmlXPathNumberFunction);
3217 break;
3218 case 'p':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003219 if (!xmlStrcmp(name, BAD_CAST "position"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003220 return(xmlXPathPositionFunction);
3221 break;
3222 case 'r':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003223 if (!xmlStrcmp(name, BAD_CAST "round"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003224 return(xmlXPathRoundFunction);
3225 break;
3226 case 's':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003227 if (!xmlStrcmp(name, BAD_CAST "string"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003228 return(xmlXPathStringFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003229 if (!xmlStrcmp(name, BAD_CAST "string-length"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003230 return(xmlXPathStringLengthFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003231 if (!xmlStrcmp(name, BAD_CAST "starts-with"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003232 return(xmlXPathStartsWithFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003233 if (!xmlStrcmp(name, BAD_CAST "substring"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003234 return(xmlXPathSubstringFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003235 if (!xmlStrcmp(name, BAD_CAST "substring-before"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003236 return(xmlXPathSubstringBeforeFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003237 if (!xmlStrcmp(name, BAD_CAST "substring-after"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003238 return(xmlXPathSubstringAfterFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003239 if (!xmlStrcmp(name, BAD_CAST "sum"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003240 return(xmlXPathSumFunction);
3241 break;
3242 case 't':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003243 if (!xmlStrcmp(name, BAD_CAST "true"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003244 return(xmlXPathTrueFunction);
Daniel Veillardb96e6431999-08-29 21:02:19 +00003245 if (!xmlStrcmp(name, BAD_CAST "translate"))
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003246 return(xmlXPathTranslateFunction);
3247 break;
3248 }
3249 return(NULL);
3250}
3251
3252/**
3253 * xmlXPathEvalLocationPathName:
3254 * @ctxt: the XPath Parser context
3255 * @name: a name string
3256 *
3257 * Various names in the beginning of a LocationPath expression
3258 * indicate whether that's an Axis, a node type,
3259 *
3260 * [6] AxisName ::= 'ancestor'
3261 * | 'ancestor-or-self'
3262 * | 'attribute'
3263 * | 'child'
3264 * | 'descendant'
3265 * | 'descendant-or-self'
3266 * | 'following'
3267 * | 'following-sibling'
3268 * | 'namespace'
3269 * | 'parent'
3270 * | 'preceding'
3271 * | 'preceding-sibling'
3272 * | 'self'
3273 * [38] NodeType ::= 'comment'
3274 * | 'text'
3275 * | 'processing-instruction'
3276 * | 'node'
3277 */
3278int
Daniel Veillardb96e6431999-08-29 21:02:19 +00003279xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, const CHAR *name) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003280 switch (name[0]) {
3281 case 'a':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003282 if (!xmlStrcmp(name, BAD_CAST "ancestor")) return(AXIS_ANCESTOR);
3283 if (!xmlStrcmp(name, BAD_CAST "ancestor-or-self"))
3284 return(AXIS_ANCESTOR_OR_SELF);
3285 if (!xmlStrcmp(name, BAD_CAST "attribute")) return(AXIS_ATTRIBUTE);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003286 break;
3287 case 'c':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003288 if (!xmlStrcmp(name, BAD_CAST "child")) return(AXIS_CHILD);
3289 if (!xmlStrcmp(name, BAD_CAST "comment")) return(NODE_TYPE_COMMENT);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003290 break;
3291 case 'd':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003292 if (!xmlStrcmp(name, BAD_CAST "descendant"))
3293 return(AXIS_DESCENDANT);
3294 if (!xmlStrcmp(name, BAD_CAST "descendant-or-self"))
3295 return(AXIS_DESCENDANT_OR_SELF);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003296 break;
3297 case 'f':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003298 if (!xmlStrcmp(name, BAD_CAST "following")) return(AXIS_FOLLOWING);
3299 if (!xmlStrcmp(name, BAD_CAST "following-sibling"))
3300 return(AXIS_FOLLOWING_SIBLING);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003301 break;
3302 case 'n':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003303 if (!xmlStrcmp(name, BAD_CAST "namespace")) return(AXIS_NAMESPACE);
3304 if (!xmlStrcmp(name, BAD_CAST "node")) return(NODE_TYPE_NODE);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003305 break;
3306 case 'p':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003307 if (!xmlStrcmp(name, BAD_CAST "parent")) return(AXIS_PARENT);
3308 if (!xmlStrcmp(name, BAD_CAST "preceding")) return(AXIS_PRECEDING);
3309 if (!xmlStrcmp(name, BAD_CAST "preceding-sibling"))
3310 return(AXIS_PRECEDING_SIBLING);
3311 if (!xmlStrcmp(name, BAD_CAST "processing-instruction"))
3312 return(NODE_TYPE_PI);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003313 break;
3314 case 's':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003315 if (!xmlStrcmp(name, BAD_CAST "self")) return(AXIS_SELF);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003316 break;
3317 case 't':
Daniel Veillardb96e6431999-08-29 21:02:19 +00003318 if (!xmlStrcmp(name, BAD_CAST "text")) return(NODE_TYPE_TEXT);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003319 break;
3320 }
3321 if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION);
3322 return(0);
3323}
3324
3325/**
3326 * xmlXPathEvalFunctionCall:
3327 * @ctxt: the XPath Parser context
3328 *
3329 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
3330 * [17] Argument ::= Expr
3331 *
3332 * Parse and evaluate a function call, the evaluation of all arguments are
3333 * pushed on the stack
3334 */
3335void
3336xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
3337 CHAR *name;
3338 CHAR *prefix;
3339 xmlXPathFunction func;
3340 int nbargs = 0;
3341
3342 name = xmlXPathParseQName(ctxt, &prefix);
3343 if (name == NULL) {
3344 ERROR(XPATH_EXPR_ERROR);
3345 }
3346 func = xmlXPathIsFunction(ctxt, name);
3347 if (func == NULL) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003348 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003349 ERROR(XPATH_UNKNOWN_FUNC_ERROR);
3350 }
3351#ifdef DEBUG_EXPR
3352 fprintf(xmlXPathDebug, "Calling function %s\n", name);
3353#endif
3354
3355 if (CUR != '(') {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003356 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003357 ERROR(XPATH_EXPR_ERROR);
3358 }
3359 NEXT;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003360
3361 while (CUR != ')') {
3362 xmlXPathEvalExpr(ctxt);
3363 nbargs++;
3364 if (CUR == ')') break;
3365 if (CUR != ',') {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003366 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003367 ERROR(XPATH_EXPR_ERROR);
3368 }
3369 NEXT;
3370 }
3371 NEXT;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003372 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003373 func(ctxt, nbargs);
3374}
3375
3376/**
3377 * xmlXPathEvalPrimaryExpr:
3378 * @ctxt: the XPath Parser context
3379 *
3380 * [15] PrimaryExpr ::= VariableReference
3381 * | '(' Expr ')'
3382 * | Literal
3383 * | Number
3384 * | FunctionCall
3385 *
3386 * Parse and evaluate a primary expression, then push the result on the stack
3387 */
3388void
3389xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
3390 if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
3391 else if (CUR == '(') {
3392 NEXT;
3393 xmlXPathEvalExpr(ctxt);
3394 if (CUR != ')') {
3395 ERROR(XPATH_EXPR_ERROR);
3396 }
3397 NEXT;
3398 } else if (IS_DIGIT(CUR)) {
3399 xmlXPathEvalNumber(ctxt);
3400 } else if ((CUR == '\'') || (CUR == '"')) {
3401 xmlXPathEvalLiteral(ctxt);
3402 } else {
3403 xmlXPathEvalFunctionCall(ctxt);
3404 }
3405}
3406
3407/**
3408 * xmlXPathEvalFilterExpr:
3409 * @ctxt: the XPath Parser context
3410 *
3411 * [20] FilterExpr ::= PrimaryExpr
3412 * | FilterExpr Predicate
3413 *
3414 * Parse and evaluate a filter expression, then push the result on the stack
3415 * Square brackets are used to filter expressions in the same way that
3416 * they are used in location paths. It is an error if the expression to
3417 * be filtered does not evaluate to a node-set. The context node list
3418 * used for evaluating the expression in square brackets is the node-set
3419 * to be filtered listed in document order.
3420 */
3421
3422void
3423xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
3424 /****
3425 xmlNodeSetPtr oldset = NULL;
3426 xmlXPathObjectPtr arg;
3427 ****/
3428
3429 xmlXPathEvalPrimaryExpr(ctxt);
3430 CHECK_ERROR;
3431
3432 if (CUR != '[') return;
3433
3434 CHECK_TYPE(XPATH_NODESET);
3435
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003436 while (CUR == '[') {
3437 xmlXPathEvalPredicate(ctxt);
3438 }
3439
3440
3441}
3442
3443/**
Daniel Veillardb96e6431999-08-29 21:02:19 +00003444 * xmlXPathScanName:
3445 * @ctxt: the XPath Parser context
3446 *
3447 * Trickery: parse an XML name but without consuming the input flow
3448 * Needed for rollback cases.
3449 *
3450 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
3451 * CombiningChar | Extender
3452 *
3453 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
3454 *
3455 * [6] Names ::= Name (S Name)*
3456 *
3457 * Returns the Name parsed or NULL
3458 */
3459
3460CHAR *
3461xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
3462 CHAR buf[XML_MAX_NAMELEN];
3463 int len = 0;
3464
3465 if (!IS_LETTER(CUR) && (CUR != '_') &&
3466 (CUR != ':')) {
3467 return(NULL);
3468 }
3469
3470 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
3471 (NXT(len) == '.') || (NXT(len) == '-') ||
3472 (NXT(len) == '_') || (NXT(len) == ':') ||
3473 (IS_COMBINING(NXT(len))) ||
3474 (IS_EXTENDER(NXT(len)))) {
3475 buf[len] = NXT(len);
3476 len++;
3477 if (len >= XML_MAX_NAMELEN) {
3478 fprintf(stderr,
3479 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
3480 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
3481 (NXT(len) == '.') || (NXT(len) == '-') ||
3482 (NXT(len) == '_') || (NXT(len) == ':') ||
3483 (IS_COMBINING(NXT(len))) ||
3484 (IS_EXTENDER(NXT(len))))
3485 len++;
3486 break;
3487 }
3488 }
3489 return(xmlStrndup(buf, len));
3490}
3491
3492/**
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003493 * xmlXPathEvalPathExpr:
3494 * @ctxt: the XPath Parser context
3495 *
3496 * [19] PathExpr ::= LocationPath
3497 * | FilterExpr
3498 * | FilterExpr '/' RelativeLocationPath
3499 * | FilterExpr '//' RelativeLocationPath
3500 *
3501 * Parse and evaluate a path expression, then push the result on the stack
3502 * The / operator and // operators combine an arbitrary expression
3503 * and a relative location path. It is an error if the expression
3504 * does not evaluate to a node-set.
3505 * The / operator does composition in the same way as when / is
3506 * used in a location path. As in location paths, // is short for
3507 * /descendant-or-self::node()/.
3508 */
3509
3510void
3511xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
3512 xmlNodeSetPtr newset = NULL;
3513
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003514 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
3515 (CUR == '\'') || (CUR == '"')) {
3516 xmlXPathEvalFilterExpr(ctxt);
3517 CHECK_ERROR;
3518 if ((CUR == '/') && (NXT(1) == '/')) {
3519 SKIP(2);
3520 if (ctxt->context->nodelist == NULL) {
3521 STRANGE
3522 xmlXPathRoot(ctxt);
3523 }
3524 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
3525 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3526 if (ctxt->context->nodelist != NULL)
3527 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3528 ctxt->context->nodelist = newset;
3529 ctxt->context->node = NULL;
3530 xmlXPathEvalRelativeLocationPath(ctxt);
3531 } else if (CUR == '/') {
3532 xmlXPathEvalRelativeLocationPath(ctxt);
3533 }
3534 } else {
Daniel Veillardb96e6431999-08-29 21:02:19 +00003535 CHAR *name;
3536
3537 name = xmlXPathScanName(ctxt);
3538 if ((name == NULL) || (!xmlXPathIsFunction(ctxt, name)))
3539 xmlXPathEvalLocationPath(ctxt);
3540 else
3541 xmlXPathEvalFilterExpr(ctxt);
3542 if (name != NULL)
3543 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003544 }
3545}
3546
3547/**
3548 * xmlXPathEvalUnionExpr:
3549 * @ctxt: the XPath Parser context
3550 *
3551 * [18] UnionExpr ::= PathExpr
3552 * | UnionExpr '|' PathExpr
3553 *
3554 * Parse and evaluate an union expression, then push the result on the stack
3555 */
3556
3557void
3558xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
3559 xmlXPathEvalPathExpr(ctxt);
3560 CHECK_ERROR;
3561 if (CUR == '|') {
3562 xmlNodeSetPtr old = ctxt->context->nodelist;
3563
3564 xmlXPathEvalPathExpr(ctxt);
3565
3566 if (ctxt->context->nodelist == NULL)
3567 ctxt->context->nodelist = old;
3568 else {
3569 ctxt->context->nodelist =
3570 xmlXPathNodeSetMerge(ctxt->context->nodelist, old);
3571 xmlXPathFreeNodeSet(old);
3572 }
3573 }
3574}
3575
3576/**
3577 * xmlXPathEvalUnaryExpr:
3578 * @ctxt: the XPath Parser context
3579 *
3580 * [27] UnaryExpr ::= UnionExpr
3581 * | '-' UnaryExpr
3582 *
3583 * Parse and evaluate an unary expression, then push the result on the stack
3584 */
3585
3586void
3587xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
3588 int minus = 0;
3589
3590 if (CUR == '-') {
3591 minus = 1;
3592 NEXT;
3593 }
Daniel Veillardb96e6431999-08-29 21:02:19 +00003594 xmlXPathEvalUnionExpr(ctxt);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003595 CHECK_ERROR;
3596 if (minus) {
3597 xmlXPathValueFlipSign(ctxt);
3598 }
3599}
3600
3601/**
3602 * xmlXPathEvalMultiplicativeExpr:
3603 * @ctxt: the XPath Parser context
3604 *
3605 * [26] MultiplicativeExpr ::= UnaryExpr
3606 * | MultiplicativeExpr MultiplyOperator UnaryExpr
3607 * | MultiplicativeExpr 'div' UnaryExpr
3608 * | MultiplicativeExpr 'mod' UnaryExpr
3609 * [34] MultiplyOperator ::= '*'
3610 *
3611 * Parse and evaluate an Additive expression, then push the result on the stack
3612 */
3613
3614void
3615xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
3616 xmlXPathEvalUnaryExpr(ctxt);
3617 CHECK_ERROR;
3618 while ((CUR == '*') ||
3619 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
3620 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
3621 int op = -1;
3622
3623 if (CUR == '*') {
3624 op = 0;
3625 NEXT;
3626 } else if (CUR == 'd') {
3627 op = 1;
3628 SKIP(3);
3629 } else if (CUR == 'm') {
3630 op = 2;
3631 SKIP(3);
3632 }
3633 xmlXPathEvalUnaryExpr(ctxt);
3634 CHECK_ERROR;
3635 switch (op) {
3636 case 0:
3637 xmlXPathMultValues(ctxt);
3638 break;
3639 case 1:
3640 xmlXPathDivValues(ctxt);
3641 break;
3642 case 2:
3643 xmlXPathModValues(ctxt);
3644 break;
3645 }
3646 }
3647}
3648
3649/**
3650 * xmlXPathEvalAdditiveExpr:
3651 * @ctxt: the XPath Parser context
3652 *
3653 * [25] AdditiveExpr ::= MultiplicativeExpr
3654 * | AdditiveExpr '+' MultiplicativeExpr
3655 * | AdditiveExpr '-' MultiplicativeExpr
3656 *
3657 * Parse and evaluate an Additive expression, then push the result on the stack
3658 */
3659
3660void
3661xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
3662 xmlXPathEvalMultiplicativeExpr(ctxt);
3663 CHECK_ERROR;
3664 while ((CUR == '+') || (CUR == '-')) {
3665 int plus;
3666
3667 if (CUR == '+') plus = 1;
3668 else plus = 0;
3669 NEXT;
3670 xmlXPathEvalMultiplicativeExpr(ctxt);
3671 CHECK_ERROR;
3672 if (plus) xmlXPathAddValues(ctxt);
3673 else xmlXPathSubValues(ctxt);
3674 }
3675}
3676
3677/**
3678 * xmlXPathEvalRelationalExpr:
3679 * @ctxt: the XPath Parser context
3680 *
3681 * [24] RelationalExpr ::= AdditiveExpr
3682 * | RelationalExpr '<' AdditiveExpr
3683 * | RelationalExpr '>' AdditiveExpr
3684 * | RelationalExpr '<=' AdditiveExpr
3685 * | RelationalExpr '>=' AdditiveExpr
3686 *
3687 * A <= B > C is allowed ? Answer from James, yes with
3688 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
3689 * which is basically what got implemented.
3690 *
3691 * Parse and evaluate a Relational expression, then push the result
3692 * on the stack
3693 */
3694
3695void
3696xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
3697 xmlXPathEvalAdditiveExpr(ctxt);
3698 CHECK_ERROR;
3699 while ((CUR == '<') ||
3700 (CUR == '>') ||
3701 ((CUR == '<') && (NXT(1) == '=')) ||
3702 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard991e63d1999-08-15 23:32:28 +00003703 int inf, strict, ret;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003704
3705 if (CUR == '<') inf = 1;
3706 else inf = 0;
3707 if (NXT(1) == '=') strict = 0;
3708 else strict = 1;
3709 NEXT;
3710 if (!strict) NEXT;
3711 xmlXPathEvalAdditiveExpr(ctxt);
3712 CHECK_ERROR;
Daniel Veillard991e63d1999-08-15 23:32:28 +00003713 ret = xmlXPathCompareValues(ctxt, inf, strict);
3714 valuePush(ctxt, xmlXPathNewBoolean(ret));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003715 }
3716}
3717
3718/**
3719 * xmlXPathEvalEqualityExpr:
3720 * @ctxt: the XPath Parser context
3721 *
3722 * [23] EqualityExpr ::= RelationalExpr
3723 * | EqualityExpr '=' RelationalExpr
3724 * | EqualityExpr '!=' RelationalExpr
3725 *
3726 * A != B != C is allowed ? Answer from James, yes with
3727 * (RelationalExpr = RelationalExpr) = RelationalExpr
3728 * (RelationalExpr != RelationalExpr) != RelationalExpr
3729 * which is basically what got implemented.
3730 *
3731 * Parse and evaluate an Equality expression, then push the result on the stack
3732 *
3733 */
3734void
3735xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
3736 xmlXPathEvalRelationalExpr(ctxt);
3737 CHECK_ERROR;
3738 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard991e63d1999-08-15 23:32:28 +00003739 xmlXPathObjectPtr res;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003740 int eq, equal;
3741
3742 if (CUR == '=') eq = 1;
3743 else eq = 0;
3744 NEXT;
3745 if (!eq) NEXT;
3746 xmlXPathEvalRelationalExpr(ctxt);
3747 CHECK_ERROR;
Daniel Veillard991e63d1999-08-15 23:32:28 +00003748 equal = xmlXPathEqualValues(ctxt);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003749 if (eq) res = xmlXPathNewBoolean(equal);
3750 else res = xmlXPathNewBoolean(!equal);
3751 valuePush(ctxt, res);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003752 }
3753}
3754
3755/**
3756 * xmlXPathEvalAndExpr:
3757 * @ctxt: the XPath Parser context
3758 *
3759 * [22] AndExpr ::= EqualityExpr
3760 * | AndExpr 'and' EqualityExpr
3761 *
3762 * Parse and evaluate an AND expression, then push the result on the stack
3763 *
3764 */
3765void
3766xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
3767 xmlXPathEvalEqualityExpr(ctxt);
3768 CHECK_ERROR;
3769 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) {
3770 xmlXPathObjectPtr arg1, arg2;
3771
3772 SKIP(3);
3773 xmlXPathEvalEqualityExpr(ctxt);
3774 CHECK_ERROR;
3775 arg2 = valuePop(ctxt);
3776 arg1 = valuePop(ctxt);
3777 arg1->boolval &= arg2->boolval;
3778 valuePush(ctxt, arg1);
3779 xmlXPathFreeObject(arg2);
3780 }
3781}
3782
3783/**
3784 * xmlXPathEvalExpr:
3785 * @ctxt: the XPath Parser context
3786 *
3787 * [14] Expr ::= OrExpr
3788 * [21] OrExpr ::= AndExpr
3789 * | OrExpr 'or' AndExpr
3790 *
3791 * Parse and evaluate an expression, then push the result on the stack
3792 *
3793 */
3794void
3795xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
3796 xmlXPathEvalAndExpr(ctxt);
3797 CHECK_ERROR;
3798 while ((CUR == 'o') && (NXT(1) == 'r')) {
3799 xmlXPathObjectPtr arg1, arg2;
3800
3801 SKIP(2);
3802 xmlXPathEvalAndExpr(ctxt);
3803 CHECK_ERROR;
3804 arg2 = valuePop(ctxt);
3805 arg1 = valuePop(ctxt);
3806 arg1->boolval |= arg2->boolval;
3807 valuePush(ctxt, arg1);
3808 xmlXPathFreeObject(arg2);
3809 }
3810}
3811
3812/**
3813 * xmlXPathEvaluatePredicateResult:
3814 * @ctxt: the XPath Parser context
3815 * @res: the Predicate Expression evaluation result
3816 * @index: index of the current node in the current list
3817 *
3818 * Evaluate a predicate result for the current node.
3819 * A PredicateExpr is evaluated by evaluating the Expr and converting
3820 * the result to a boolean. If the result is a number, the result will
3821 * be converted to true if the number is equal to the position of the
3822 * context node in the context node list (as returned by the position
3823 * function) and will be converted to false otherwise; if the result
3824 * is not a number, then the result will be converted as if by a call
3825 * to the boolean function.
3826 */
3827int
3828xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
3829 xmlXPathObjectPtr res, int index) {
3830 if (res == NULL) return(0);
3831 switch (res->type) {
3832 case XPATH_BOOLEAN:
3833 return(res->boolval);
3834 case XPATH_NUMBER:
3835 return(res->floatval == index);
3836 case XPATH_NODESET:
3837 return(res->nodesetval->nodeNr != 0);
3838 case XPATH_STRING:
3839 return((res->stringval != NULL) &&
3840 (xmlStrlen(res->stringval) != 0));
3841 default:
3842 STRANGE
3843 }
3844 return(0);
3845}
3846
3847/**
3848 * xmlXPathEvalPredicate:
3849 * @ctxt: the XPath Parser context
3850 *
3851 * [8] Predicate ::= '[' PredicateExpr ']'
3852 * [9] PredicateExpr ::= Expr
3853 *
3854 * Parse and evaluate a predicate for all the elements of the
3855 * current node list. Then refine the list by removing all
3856 * nodes where the predicate is false.
3857 */
3858void
3859xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
3860 const CHAR *cur;
3861 xmlXPathObjectPtr res;
3862 xmlNodeSetPtr newset = NULL;
3863 int i;
3864
3865 if (CUR != '[') {
3866 ERROR(XPATH_INVALID_PREDICATE_ERROR);
3867 }
3868 NEXT;
3869 if ((ctxt->context->nodelist == NULL) ||
3870 (ctxt->context->nodelist->nodeNr == 0)) {
3871 ctxt->context->node = NULL;
3872 xmlXPathEvalExpr(ctxt);
3873 CHECK_ERROR;
3874 res = valuePop(ctxt);
3875 if (res != NULL)
3876 xmlXPathFreeObject(res);
3877 } else {
3878 cur = ctxt->cur;
3879 newset = xmlXPathNodeSetCreate(NULL);
3880 for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) {
3881 ctxt->cur = cur;
3882 ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
3883 xmlXPathEvalExpr(ctxt);
3884 CHECK_ERROR;
3885 res = valuePop(ctxt);
3886 if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1))
3887 xmlXPathNodeSetAdd(newset,
3888 ctxt->context->nodelist->nodeTab[i]);
3889 if (res != NULL)
3890 xmlXPathFreeObject(res);
3891 }
3892 if (ctxt->context->nodelist != NULL)
3893 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3894 ctxt->context->nodelist = newset;
3895 ctxt->context->node = NULL;
3896 }
3897 if (CUR != ']') {
3898 ERROR(XPATH_INVALID_PREDICATE_ERROR);
3899 }
3900 NEXT;
3901#ifdef DEBUG_STEP
3902 fprintf(xmlXPathDebug, "After predicate : ");
3903 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
3904#endif
3905}
3906
3907/**
3908 * xmlXPathEvalBasis:
3909 * @ctxt: the XPath Parser context
3910 *
3911 * [5] Basis ::= AxisName '::' NodeTest
3912 * | AbbreviatedBasis
3913 * [13] AbbreviatedBasis ::= NodeTest
3914 * | '@' NodeTest
3915 * [7] NodeTest ::= WildcardName
3916 * | NodeType '(' ')'
3917 * | 'processing-instruction' '(' Literal ')'
3918 * [37] WildcardName ::= '*'
3919 * | NCName ':' '*'
3920 * | QName
3921 *
3922 * Evaluate one step in a Location Path
3923 */
3924void
3925xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) {
3926 CHAR *name = NULL;
3927 CHAR *prefix = NULL;
3928 int type = 0;
3929 int axis = AXIS_CHILD; /* the default on abbreviated syntax */
3930 int nodetest = NODE_TEST_NONE;
3931 int nodetype = 0;
3932 xmlNodeSetPtr newset = NULL;
Daniel Veillardb96e6431999-08-29 21:02:19 +00003933 int attribute = 0;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003934
3935 if (CUR == '@') {
Daniel Veillardb96e6431999-08-29 21:02:19 +00003936 NEXT;
3937 attribute = 1;
3938 axis = AXIS_ATTRIBUTE;
3939 goto parse_NodeTest;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003940 } else if (CUR == '*') {
3941 NEXT;
3942 nodetest = NODE_TEST_ALL;
3943 } else {
3944 name = xmlXPathParseNCName(ctxt);
3945 if (name == NULL) {
3946 ERROR(XPATH_EXPR_ERROR);
3947 }
3948 type = xmlXPathGetNameType(ctxt, name);
3949 switch (type) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00003950 case IS_FUNCTION: {
3951 xmlXPathFunction func;
3952 int nbargs = 0;
3953 xmlXPathObjectPtr top;
3954
3955 top = ctxt->value;
3956 func = xmlXPathIsFunction(ctxt, name);
3957 if (func == NULL) {
3958 free(name);
3959 ERROR(XPATH_UNKNOWN_FUNC_ERROR);
3960 }
3961#ifdef DEBUG_EXPR
3962 fprintf(xmlXPathDebug, "Calling function %s\n", name);
3963#endif
3964
3965 if (CUR != '(') {
3966 free(name);
3967 ERROR(XPATH_EXPR_ERROR);
3968 }
3969 NEXT;
3970
3971 while (CUR != ')') {
3972 xmlXPathEvalExpr(ctxt);
3973 nbargs++;
3974 if (CUR == ')') break;
3975 if (CUR != ',') {
3976 free(name);
3977 ERROR(XPATH_EXPR_ERROR);
3978 }
3979 NEXT;
3980 }
3981 NEXT;
3982 free(name);
3983 func(ctxt, nbargs);
3984 if ((ctxt->value != top) &&
3985 (ctxt->value != NULL) &&
3986 (ctxt->value->type == XPATH_NODESET)) {
3987 xmlXPathObjectPtr cur;
3988
3989 cur = valuePop(ctxt);
3990 ctxt->context->nodelist = cur->nodesetval;
3991 ctxt->context->node = NULL;
3992 cur->nodesetval = NULL;
3993 xmlXPathFreeObject(cur);
3994 }
3995 return;
3996 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003997 /*
3998 * Simple case: no axis seach all given node types.
3999 */
4000 case NODE_TYPE_COMMENT:
4001 if ((CUR != '(') || (NXT(1) != ')')) break;
4002 SKIP(2);
4003 nodetest = NODE_TEST_TYPE;
4004 nodetype = XML_COMMENT_NODE;
4005 goto search_nodes;
4006 case NODE_TYPE_TEXT:
4007 if ((CUR != '(') || (NXT(1) != ')')) break;
4008 SKIP(2);
4009 nodetest = NODE_TEST_TYPE;
4010 nodetype = XML_TEXT_NODE;
4011 goto search_nodes;
4012 case NODE_TYPE_NODE:
4013 if ((CUR != '(') || (NXT(1) != ')')) {
4014 nodetest = NODE_TEST_NAME;
4015 break;
4016 }
4017 SKIP(2);
4018 nodetest = NODE_TEST_TYPE;
4019 nodetype = XML_ELEMENT_NODE;
4020 goto search_nodes;
4021 case NODE_TYPE_PI:
4022 if (CUR != '(') break;
Daniel Veillardb96e6431999-08-29 21:02:19 +00004023 if (name != NULL) free(name);
4024 name = NULL;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004025 if (NXT(1) != ')') {
Daniel Veillardb96e6431999-08-29 21:02:19 +00004026 xmlXPathObjectPtr cur;
4027
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004028 /*
4029 * Specific case: search a PI by name.
4030 */
Daniel Veillardb96e6431999-08-29 21:02:19 +00004031 NEXT;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004032 nodetest = NODE_TEST_PI;
4033 xmlXPathEvalLiteral(ctxt);
4034 CHECK_ERROR;
4035 if (CUR != ')')
4036 ERROR(XPATH_UNCLOSED_ERROR);
Daniel Veillardb96e6431999-08-29 21:02:19 +00004037 NEXT;
4038 xmlXPathStringFunction(ctxt, 1);
4039 CHECK_ERROR;
4040 cur = valuePop(ctxt);
4041 name = xmlStrdup(cur->stringval);
4042 xmlXPathFreeObject(cur);
4043 } else
4044 SKIP(2);
4045 nodetest = NODE_TEST_PI;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004046 goto search_nodes;
4047
4048 /*
4049 * Handling of the compund form: got the axis.
4050 */
4051 case AXIS_ANCESTOR:
4052 case AXIS_ANCESTOR_OR_SELF:
4053 case AXIS_ATTRIBUTE:
4054 case AXIS_CHILD:
4055 case AXIS_DESCENDANT:
4056 case AXIS_DESCENDANT_OR_SELF:
4057 case AXIS_FOLLOWING:
4058 case AXIS_FOLLOWING_SIBLING:
4059 case AXIS_NAMESPACE:
4060 case AXIS_PARENT:
4061 case AXIS_PRECEDING:
4062 case AXIS_PRECEDING_SIBLING:
4063 case AXIS_SELF:
4064 if ((CUR != ':') || (NXT(1) != ':')) {
4065 nodetest = NODE_TEST_NAME;
4066 break;
4067 }
4068 SKIP(2);
4069 axis = type;
4070 break;
4071
4072 /*
4073 * Default: abbreviated syntax the axis is AXIS_CHILD
4074 */
4075 default:
4076 nodetest = NODE_TEST_NAME;
4077 }
Daniel Veillardb96e6431999-08-29 21:02:19 +00004078parse_NodeTest:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004079 if (nodetest == NODE_TEST_NONE) {
4080 if (CUR == '*') {
4081 NEXT;
4082 nodetest = NODE_TEST_ALL;
4083 } else {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004084 if (name != NULL)
4085 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004086 name = xmlXPathParseQName(ctxt, &prefix);
Daniel Veillardb05deb71999-08-10 19:04:08 +00004087 if (name == NULL) {
4088 ERROR(XPATH_EXPR_ERROR);
4089 }
4090 type = xmlXPathGetNameType(ctxt, name);
4091 switch (type) {
4092 /*
4093 * Simple case: no axis seach all given node types.
4094 */
4095 case NODE_TYPE_COMMENT:
4096 if ((CUR != '(') || (NXT(1) != ')')) break;
4097 SKIP(2);
4098 nodetest = NODE_TEST_TYPE;
4099 nodetype = XML_COMMENT_NODE;
4100 goto search_nodes;
4101 case NODE_TYPE_TEXT:
4102 if ((CUR != '(') || (NXT(1) != ')')) break;
4103 SKIP(2);
4104 nodetest = NODE_TEST_TYPE;
4105 nodetype = XML_TEXT_NODE;
4106 goto search_nodes;
4107 case NODE_TYPE_NODE:
4108 if ((CUR != '(') || (NXT(1) != ')')) {
4109 nodetest = NODE_TEST_NAME;
4110 break;
4111 }
4112 SKIP(2);
4113 nodetest = NODE_TEST_TYPE;
4114 nodetype = XML_ELEMENT_NODE;
4115 goto search_nodes;
4116 case NODE_TYPE_PI:
4117 if (CUR != '(') break;
Daniel Veillardb96e6431999-08-29 21:02:19 +00004118 if (name != NULL) free(name);
4119 name = NULL;
Daniel Veillardb05deb71999-08-10 19:04:08 +00004120 if (NXT(1) != ')') {
Daniel Veillardb96e6431999-08-29 21:02:19 +00004121 xmlXPathObjectPtr cur;
4122
Daniel Veillardb05deb71999-08-10 19:04:08 +00004123 /*
4124 * Specific case: search a PI by name.
4125 */
Daniel Veillardb96e6431999-08-29 21:02:19 +00004126 NEXT;
Daniel Veillardb05deb71999-08-10 19:04:08 +00004127 nodetest = NODE_TEST_PI;
4128 xmlXPathEvalLiteral(ctxt);
4129 CHECK_ERROR;
4130 if (CUR != ')')
4131 ERROR(XPATH_UNCLOSED_ERROR);
Daniel Veillardb96e6431999-08-29 21:02:19 +00004132 NEXT;
4133 xmlXPathStringFunction(ctxt, 1);
4134 CHECK_ERROR;
4135 cur = valuePop(ctxt);
4136 name = xmlStrdup(cur->stringval);
4137 xmlXPathFreeObject(cur);
4138 } else
4139 SKIP(2);
4140 nodetest = NODE_TEST_PI;
Daniel Veillardb05deb71999-08-10 19:04:08 +00004141 goto search_nodes;
4142 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004143 nodetest = NODE_TEST_NAME;
4144 }
4145 } else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) {
4146 NEXT;
4147 prefix = name;
4148 if (CUR == '*') {
4149 NEXT;
4150 nodetest = NODE_TEST_ALL;
4151 } else
4152 name = xmlXPathParseNCName(ctxt);
4153 } else if (name == NULL)
4154 ERROR(XPATH_EXPR_ERROR);
4155 }
4156
4157search_nodes:
4158
4159#ifdef DEBUG_STEP
4160 fprintf(xmlXPathDebug, "Basis : computing new set\n");
4161#endif
4162 newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype,
4163 prefix, name);
4164 if (ctxt->context->nodelist != NULL)
4165 xmlXPathFreeNodeSet(ctxt->context->nodelist);
4166 ctxt->context->nodelist = newset;
4167 ctxt->context->node = NULL;
4168#ifdef DEBUG_STEP
4169 fprintf(xmlXPathDebug, "Basis : ");
4170 xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist);
4171#endif
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004172 if (name != NULL) free(name);
4173 if (prefix != NULL) free(prefix);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004174}
4175
4176/**
4177 * xmlXPathEvalStep:
4178 * @ctxt: the XPath Parser context
4179 *
4180 * [4] Step ::= Basis Predicate*
4181 * | AbbreviatedStep
4182 * [12] AbbreviatedStep ::= '.'
4183 * | '..'
4184 *
4185 * Evaluate one step in a Location Path
4186 * A location step of . is short for self::node(). This is
4187 * particularly useful in conjunction with //. For example, the
4188 * location path .//para is short for
4189 * self::node()/descendant-or-self::node()/child::para
4190 * and so will select all para descendant elements of the context
4191 * node.
4192 * Similarly, a location step of .. is short for parent::node().
4193 * For example, ../title is short for parent::node()/child::title
4194 * and so will select the title children of the parent of the context
4195 * node.
4196 */
4197void
4198xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
4199 xmlNodeSetPtr newset = NULL;
4200
4201 if ((CUR == '.') && (NXT(1) == '.')) {
4202 SKIP(2);
4203 if (ctxt->context->nodelist == NULL) {
4204 STRANGE
4205 xmlXPathRoot(ctxt);
4206 }
4207 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
4208 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
4209 if (ctxt->context->nodelist != NULL)
4210 xmlXPathFreeNodeSet(ctxt->context->nodelist);
4211 ctxt->context->nodelist = newset;
4212 ctxt->context->node = NULL;
4213 } else if (CUR == '.') {
4214 NEXT;
4215 } else {
4216 xmlXPathEvalBasis(ctxt);
4217 while (CUR == '[') {
4218 xmlXPathEvalPredicate(ctxt);
4219 }
4220 }
4221#ifdef DEBUG_STEP
4222 fprintf(xmlXPathDebug, "Step : ");
4223 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
4224#endif
4225}
4226
4227/**
4228 * xmlXPathEvalRelativeLocationPath:
4229 * @ctxt: the XPath Parser context
4230 *
4231 * [3] RelativeLocationPath ::= Step
4232 * | RelativeLocationPath '/' Step
4233 * | AbbreviatedRelativeLocationPath
4234 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
4235 *
4236 */
4237void
4238xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) {
4239 xmlNodeSetPtr newset = NULL;
4240
4241 xmlXPathEvalStep(ctxt);
4242 while (CUR == '/') {
4243 if ((CUR == '/') && (NXT(1) == '/')) {
4244 SKIP(2);
4245 if (ctxt->context->nodelist == NULL) {
4246 STRANGE
4247 xmlXPathRoot(ctxt);
4248 }
4249 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
4250 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
4251 if (ctxt->context->nodelist != NULL)
4252 xmlXPathFreeNodeSet(ctxt->context->nodelist);
4253 ctxt->context->nodelist = newset;
4254 ctxt->context->node = NULL;
4255 xmlXPathEvalStep(ctxt);
4256 } else if (CUR == '/') {
4257 NEXT;
4258 xmlXPathEvalStep(ctxt);
4259 }
4260 }
4261}
4262
4263/**
4264 * xmlXPathEvalLocationPath:
4265 * @ctxt: the XPath Parser context
4266 *
4267 * [1] LocationPath ::= RelativeLocationPath
4268 * | AbsoluteLocationPath
4269 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
4270 * | AbbreviatedAbsoluteLocationPath
4271 * [10] AbbreviatedAbsoluteLocationPath ::=
4272 * '//' RelativeLocationPath
4273 *
4274 * // is short for /descendant-or-self::node()/. For example,
4275 * //para is short for /descendant-or-self::node()/child::para and
4276 * so will select any para element in the document (even a para element
4277 * that is a document element will be selected by //para since the
4278 * document element node is a child of the root node); div//para is
4279 * short for div/descendant-or-self::node()/child::para and so will
4280 * select all para descendants of div children.
4281 */
4282void
4283xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
4284 xmlNodeSetPtr newset = NULL;
4285
4286 while (CUR == '/') {
4287 if ((CUR == '/') && (NXT(1) == '/')) {
4288 SKIP(2);
4289 if (ctxt->context->nodelist == NULL)
4290 xmlXPathRoot(ctxt);
4291 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
4292 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
4293 if (ctxt->context->nodelist != NULL)
4294 xmlXPathFreeNodeSet(ctxt->context->nodelist);
4295 ctxt->context->nodelist = newset;
4296 ctxt->context->node = NULL;
4297 xmlXPathEvalRelativeLocationPath(ctxt);
4298 } else if (CUR == '/') {
4299 NEXT;
4300 xmlXPathRoot(ctxt);
Daniel Veillardb05deb71999-08-10 19:04:08 +00004301 if (CUR != 0)
4302 xmlXPathEvalRelativeLocationPath(ctxt);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004303 } else {
4304 xmlXPathEvalRelativeLocationPath(ctxt);
4305 }
4306 }
4307}
4308
4309/*
4310 * TODO * extra spaces *
4311 * more tokenization rules ... Not used currently, especially allowing
4312 * spaces before and after ExprToken !!!!!!!!!!!!!
4313 *
4314 * [32] Operator ::= OperatorName
4315 * | MultiplyOperator
4316 * | '/' | '//' | '|' | '+' | '-' | '=' | '!='
4317 * | '<'| '<=' | '>' | '>='
4318 * [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
4319 * [39] ExprWhitespace ::= S
4320 *
4321 * BUG: ExprToken is never referenced.
4322 *
4323 * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
4324 * | WildcardName
4325 * | NodeType
4326 * | Operator
4327 * | FunctionName
4328 * | AxisName
4329 * | Literal
4330 * | Number
4331 * | VariableReference
4332 */
4333
4334/**
4335 * xmlXPathEval:
4336 * @str: the XPath expression
4337 * @ctxt: the XPath context
4338 *
4339 * Evaluate the XPath Location Path in the given context.
4340 *
4341 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
4342 * the caller has to free the object.
4343 */
4344xmlXPathObjectPtr
4345xmlXPathEval(const CHAR *str, xmlXPathContextPtr ctxt) {
4346 xmlXPathParserContextPtr pctxt;
Daniel Veillardb96e6431999-08-29 21:02:19 +00004347 xmlXPathObjectPtr res = NULL, tmp;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004348
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004349 xmlXPathInit();
4350
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004351 CHECK_CONTEXT
4352
4353 if (xmlXPathDebug == NULL)
4354 xmlXPathDebug = stderr;
4355 pctxt = xmlXPathNewParserContext(str, ctxt);
4356 xmlXPathEvalLocationPath(pctxt);
4357
Daniel Veillardb96e6431999-08-29 21:02:19 +00004358 /* TODO: cleanup nodelist, res = valuePop(pctxt); */
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004359 do {
Daniel Veillardb96e6431999-08-29 21:02:19 +00004360 tmp = valuePop(pctxt);
4361 if (tmp != NULL);
4362 xmlXPathFreeObject(tmp);
4363 } while (tmp != NULL);
4364 if (res == NULL)
4365 res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004366 xmlXPathFreeParserContext(pctxt);
4367 return(res);
4368}
4369
4370/**
4371 * xmlXPathEvalExpression:
4372 * @str: the XPath expression
4373 * @ctxt: the XPath context
4374 *
4375 * Evaluate the XPath expression in the given context.
4376 *
Daniel Veillardb96e6431999-08-29 21:02:19 +00004377 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004378 * the caller has to free the object.
4379 */
4380xmlXPathObjectPtr
4381xmlXPathEvalExpression(const CHAR *str, xmlXPathContextPtr ctxt) {
4382 xmlXPathParserContextPtr pctxt;
4383 xmlXPathObjectPtr res, tmp;
4384
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004385 xmlXPathInit();
4386
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004387 CHECK_CONTEXT
4388
4389 if (xmlXPathDebug == NULL)
4390 xmlXPathDebug = stderr;
4391 pctxt = xmlXPathNewParserContext(str, ctxt);
4392 xmlXPathEvalExpr(pctxt);
4393
4394 res = valuePop(pctxt);
4395 do {
4396 tmp = valuePop(pctxt);
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004397 if (tmp != NULL);
4398 xmlXPathFreeObject(tmp);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004399 } while (tmp != NULL);
4400 xmlXPathFreeParserContext(pctxt);
4401 return(res);
4402}
4403