blob: cff0e82be4eca026a644e83d84849a16ee802aee [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>
31#include "tree.h"
32#include "xpath.h"
33#include "parserInternals.h"
34
35/*
Daniel Veillarde2d034d1999-07-27 19:52:06 +000036 * Setup stuff for floating point
Daniel Veillard991e63d1999-08-15 23:32:28 +000037 * The lack of portability of this section of the libc is annoying !
Daniel Veillard1566d3a1999-07-15 14:24:29 +000038 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +000039double xmlXPathNAN = 0;
40double xmlXPathPINF = 1;
41double xmlXPathMINF = -1;
42
Daniel Veillardb05deb71999-08-10 19:04:08 +000043#ifndef isinf
44#ifndef HAVE_ISINF
45
46#if HAVE_FPCLASS
47
48int isinf(double d) {
49 fpclass_t type = fpclass(d);
50 switch (type) {
51 case FP_NINF:
52 return(-1);
53 case FP_PINF:
54 return(1);
55 default:
56 return(0);
57 }
58 return(0);
59}
60
61#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
62
63#if HAVE_FP_CLASS_H
64#include <fp_class.h>
65#endif
66
67int isinf(double d) {
68#if HAVE_FP_CLASS
69 int fpclass = fp_class(d);
70#else
71 int fpclass = fp_class_d(d);
72#endif
73 if (fpclass == FP_POS_INF)
74 return(1);
75 if (fpclass == FP_NEG_INF)
76 return(-1);
77 return(0);
78}
79
80#elif defined(HAVE_CLASS)
81
82int isinf(double d) {
83 int fpclass = class(d);
84 if (fpclass == FP_PLUS_INF)
85 return(1);
86 if (fpclass == FP_MINUS_INF)
87 return(-1);
88 return(0);
89}
90#elif defined(finite) || defined(HAVE_FINITE)
91int isinf(double x) { return !finite(x) && x==x; }
Daniel Veillard991e63d1999-08-15 23:32:28 +000092#elif defined(HUGE_VAL)
93static int isinf(double x)
94{
95 if (x == HUGE_VAL)
96 return(1);
97 if (x == -HUGE_VAL)
98 return(-1);
99 return(0);
100}
101#endif
Daniel Veillardb05deb71999-08-10 19:04:08 +0000102
103#endif /* ! HAVE_ISINF */
104#endif /* ! defined(isinf) */
105
106#ifndef isnan
107#ifndef HAVE_ISNAN
108
109#ifdef HAVE_ISNAND
110#define isnan(f) isnand(f)
111#endif /* HAVE_iSNAND */
112
113#endif /* ! HAVE_iSNAN */
114#endif /* ! defined(isnan) */
115
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000116/**
117 * xmlXPathInit:
118 *
119 * Initialize the XPath environment
120 */
121void
122xmlXPathInit(void) {
123 static int initialized = 0;
124
125 if (initialized) return;
126
127 xmlXPathNAN = 0;
128 xmlXPathNAN /= 0;
129
130 xmlXPathPINF = 1;
131 xmlXPathPINF /= 0;
132
133 xmlXPathMINF = -1;
134 xmlXPathMINF /= 0;
135
136 initialized = 1;
137}
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000138
139/* #define DEBUG */
140/* #define DEBUG_STEP */
141/* #define DEBUG_EXPR */
142
143FILE *xmlXPathDebug = NULL;
144
145#define TODO \
146 fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \
147 __FILE__, __LINE__);
148
149#define STRANGE \
150 fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \
151 __FILE__, __LINE__);
152
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000153double xmlXPathStringEvalNumber(const CHAR *str);
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000154
155/************************************************************************
156 * *
157 * Parser stacks related functions and macros *
158 * *
159 ************************************************************************/
160
161/*
162 * Generic function for accessing stacks in the Parser Context
163 */
164
165#define PUSH_AND_POP(type, name) \
166extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
167 if (ctxt->name##Nr >= ctxt->name##Max) { \
168 ctxt->name##Max *= 2; \
169 ctxt->name##Tab = (void *) realloc(ctxt->name##Tab, \
170 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
171 if (ctxt->name##Tab == NULL) { \
Daniel Veillardb05deb71999-08-10 19:04:08 +0000172 fprintf(xmlXPathDebug, "realloc failed !\n"); \
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000173 exit(1); \
174 } \
175 } \
176 ctxt->name##Tab[ctxt->name##Nr] = value; \
177 ctxt->name = value; \
178 return(ctxt->name##Nr++); \
179} \
180extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
181 type ret; \
182 if (ctxt->name##Nr <= 0) return(0); \
183 ctxt->name##Nr--; \
184 if (ctxt->name##Nr > 0) \
185 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
186 else \
187 ctxt->name = NULL; \
188 ret = ctxt->name##Tab[ctxt->name##Nr]; \
189 ctxt->name##Tab[ctxt->name##Nr] = 0; \
190 return(ret); \
191} \
192
193PUSH_AND_POP(xmlXPathObjectPtr, value)
194
195/*
196 * Macros for accessing the content. Those should be used only by the parser,
197 * and not exported.
198 *
199 * Dirty macros, i.e. one need to make assumption on the context to use them
200 *
201 * CUR_PTR return the current pointer to the CHAR to be parsed.
202 * CUR returns the current CHAR value, i.e. a 8 bit value if compiled
203 * in ISO-Latin or UTF-8, and the current 16 bit value if compiled
204 * in UNICODE mode. This should be used internally by the parser
205 * only to compare to ASCII values otherwise it would break when
206 * running with UTF-8 encoding.
207 * NXT(n) returns the n'th next CHAR. Same as CUR is should be used only
208 * to compare on ASCII based substring.
209 * SKIP(n) Skip n CHAR, and must also be used only to skip ASCII defined
210 * strings within the parser.
211 * CURRENT Returns the current char value, with the full decoding of
212 * UTF-8 if we are using this mode. It returns an int.
213 * NEXT Skip to the next character, this does the proper decoding
214 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
215 * It returns the pointer to the current CHAR.
216 */
217
218#define CUR (*ctxt->cur)
219#define SKIP(val) ctxt->cur += (val)
220#define NXT(val) ctxt->cur[(val)]
221#define CUR_PTR ctxt->cur
222
223#define SKIP_BLANKS \
224 while (IS_BLANK(*(ctxt->cur))) NEXT
225
226#ifndef USE_UTF_8
227#define CURRENT (*ctxt->cur)
228#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
229#else
230#endif
231
232/************************************************************************
233 * *
234 * Error handling routines *
235 * *
236 ************************************************************************/
237
238#define XPATH_EXPRESSION_OK 0
239#define XPATH_NUMBER_ERROR 1
240#define XPATH_UNFINISHED_LITERAL_ERROR 2
241#define XPATH_START_LITERAL_ERROR 3
242#define XPATH_VARIABLE_REF_ERROR 4
243#define XPATH_UNDEF_VARIABLE_ERROR 5
244#define XPATH_INVALID_PREDICATE_ERROR 6
245#define XPATH_EXPR_ERROR 7
246#define XPATH_UNCLOSED_ERROR 8
247#define XPATH_UNKNOWN_FUNC_ERROR 9
248#define XPATH_INVALID_OPERAND 10
249#define XPATH_INVALID_TYPE 11
250#define XPATH_INVALID_ARITY 12
251
252const char *xmlXPathErrorMessages[] = {
253 "Ok",
254 "Number encoding",
255 "Unfinished litteral",
256 "Start of litteral",
257 "Expected $ for variable reference",
258 "Undefined variable",
259 "Invalid predicate",
260 "Invalid expression",
261 "Missing closing curly brace",
262 "Unregistered function",
263 "Invalid operand",
264 "Invalid type",
265 "Invalid number of arguments",
266};
267
268/**
269 * xmlXPathError:
270 * @ctxt: the XPath Parser context
271 * @file: the file name
272 * @line: the line number
273 * @no: the error number
274 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000275 * Create a new xmlNodeSetPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000276 *
277 * Returns the newly created object.
278 */
279void
280xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
281 int line, int no) {
282 int n;
283 const char *cur;
284 const char *base;
285
286 fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line,
287 xmlXPathErrorMessages[no]);
288
289 cur = ctxt->cur;
290 base = ctxt->base;
291 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
292 cur--;
293 }
294 n = 0;
295 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
296 cur--;
297 if ((*cur == '\n') || (*cur == '\r')) cur++;
298 base = cur;
299 n = 0;
300 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
301 fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++);
302 n++;
303 }
304 fprintf(xmlXPathDebug, "\n");
305 cur = ctxt->cur;
306 while ((*cur == '\n') || (*cur == '\r'))
307 cur--;
308 n = 0;
309 while ((cur != base) && (n++ < 80)) {
310 fprintf(xmlXPathDebug, " ");
311 base++;
312 }
313 fprintf(xmlXPathDebug,"^\n");
314}
315
316#define CHECK_ERROR \
317 if (ctxt->error != XPATH_EXPRESSION_OK) return
318
319#define ERROR(X) \
320 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
321 ctxt->error = (X); return; }
322
Daniel Veillard991e63d1999-08-15 23:32:28 +0000323#define ERROR0(X) \
324 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
325 ctxt->error = (X); return(0); }
326
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000327#define CHECK_TYPE(typeval) \
328 if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \
329 ERROR(XPATH_INVALID_TYPE) \
330
331
332/************************************************************************
333 * *
334 * Routines to handle NodeSets *
335 * *
336 ************************************************************************/
337
338#define XML_NODESET_DEFAULT 10
339/**
340 * xmlXPathNodeSetCreate:
341 * @val: an initial xmlNodePtr, or NULL
342 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000343 * Create a new xmlNodeSetPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000344 *
345 * Returns the newly created object.
346 */
347xmlNodeSetPtr
348xmlXPathNodeSetCreate(xmlNodePtr val) {
349 xmlNodeSetPtr ret;
350
351 ret = (xmlNodeSetPtr) malloc(sizeof(xmlNodeSet));
352 if (ret == NULL) {
353 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
354 return(NULL);
355 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000356 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000357 if (val != NULL) {
358 ret->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
359 sizeof(xmlNodePtr));
360 if (ret->nodeTab == NULL) {
361 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
362 return(NULL);
363 }
364 memset(ret->nodeTab, 0 ,
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000365 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000366 ret->nodeMax = XML_NODESET_DEFAULT;
367 ret->nodeTab[ret->nodeNr++] = val;
368 }
369 return(ret);
370}
371
372/**
373 * xmlXPathNodeSetAdd:
374 * @cur: the initial node set
375 * @val: a new xmlNodePtr
376 *
377 * add a new xmlNodePtr ot an existing NodeSet
378 */
379void
380xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
381 int i;
382
383 if (val == NULL) return;
384
385 /*
386 * check against doublons
387 */
388 for (i = 0;i < cur->nodeNr;i++)
389 if (cur->nodeTab[i] == val) return;
390
391 /*
392 * grow the nodeTab if needed
393 */
394 if (cur->nodeMax == 0) {
395 cur->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
396 sizeof(xmlNodePtr));
397 if (cur->nodeTab == NULL) {
398 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
399 return;
400 }
401 memset(cur->nodeTab, 0 ,
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000402 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000403 cur->nodeMax = XML_NODESET_DEFAULT;
404 } else if (cur->nodeNr == cur->nodeMax) {
405 xmlNodePtr *temp;
406
407 cur->nodeMax *= 2;
408 temp = (xmlNodePtr *) realloc(cur->nodeTab, cur->nodeMax *
409 sizeof(xmlNodePtr));
410 if (temp == NULL) {
411 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
412 return;
413 }
414 }
415 cur->nodeTab[cur->nodeNr++] = val;
416}
417
418/**
419 * xmlXPathNodeSetMerge:
420 * @val1: the first NodeSet
421 * @val2: the second NodeSet
422 *
423 * Merges two nodesets, all nodes from @val2 are added to @val1
424 *
425 * Returns val1 once extended or NULL in case of error.
426 */
427xmlNodeSetPtr
428xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
429 int i;
430
431 if (val1 == NULL) return(NULL);
432 if (val2 == NULL) return(val1);
433
434 /*
435 * !!!!! this can be optimized a lot, knowing that both
436 * val1 and val2 already have unicity of their values.
437 */
438
439 for (i = 0;i < val2->nodeNr;i++)
440 xmlXPathNodeSetAdd(val1, val2->nodeTab[i]);
441
442 return(val1);
443}
444
445/**
446 * xmlXPathNodeSetDel:
447 * @cur: the initial node set
448 * @val: an xmlNodePtr
449 *
450 * Removes an xmlNodePtr from an existing NodeSet
451 */
452void
453xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
454 int i;
455
456 if (cur == NULL) return;
457 if (val == NULL) return;
458
459 /*
460 * check against doublons
461 */
462 for (i = 0;i < cur->nodeNr;i++)
463 if (cur->nodeTab[i] == val) break;
464
465 if (i >= cur->nodeNr) {
466#ifdef DEBUG
467 fprintf(xmlXPathDebug,
468 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
469 val->name);
470#endif
471 return;
472 }
473 cur->nodeNr--;
474 for (;i < cur->nodeNr;i++)
475 cur->nodeTab[i] = cur->nodeTab[i + 1];
476 cur->nodeTab[cur->nodeNr] = NULL;
477}
478
479/**
480 * xmlXPathNodeSetRemove:
481 * @cur: the initial node set
482 * @val: the index to remove
483 *
484 * Removes an entry from an existing NodeSet list.
485 */
486void
487xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
488 if (cur == NULL) return;
489 if (val >= cur->nodeNr) return;
490 cur->nodeNr--;
491 for (;val < cur->nodeNr;val++)
492 cur->nodeTab[val] = cur->nodeTab[val + 1];
493 cur->nodeTab[cur->nodeNr] = NULL;
494}
495
496/**
497 * xmlXPathFreeNodeSet:
498 * @obj: the xmlNodeSetPtr to free
499 *
500 * Free the NodeSet compound (not the actual nodes !).
501 */
502void
503xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
504 if (obj == NULL) return;
505 if (obj->nodeTab != NULL) {
506#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000507 memset(obj->nodeTab, 0xB , (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000508#endif
509 free(obj->nodeTab);
510 }
511#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000512 memset(obj, 0xB , (size_t) sizeof(xmlNodeSet));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000513#endif
514 free(obj);
515}
516
517#ifdef DEBUG
518/**
519 * xmlXPathDebugNodeSet:
520 * @output: a FILE * for the output
521 * @obj: the xmlNodeSetPtr to free
522 *
523 * Quick display of a NodeSet
524 */
525void
526xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) {
527 int i;
528
529 if (output == NULL) output = xmlXPathDebug;
530 if (obj == NULL) {
531 fprintf(output, "NodeSet == NULL !\n");
532 return;
533 }
534 if (obj->nodeNr == 0) {
535 fprintf(output, "NodeSet is empty\n");
536 return;
537 }
538 if (obj->nodeTab == NULL) {
539 fprintf(output, " nodeTab == NULL !\n");
540 return;
541 }
542 for (i = 0; i < obj->nodeNr; i++) {
543 if (obj->nodeTab[i] == NULL) {
544 fprintf(output, " NULL !\n");
545 return;
546 }
Daniel Veillardb05deb71999-08-10 19:04:08 +0000547 if (obj->nodeTab[i]->type == XML_DOCUMENT_NODE)
548 fprintf(output, " /");
549 else if (obj->nodeTab[i]->name == NULL)
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000550 fprintf(output, " noname!");
551 else fprintf(output, " %s", obj->nodeTab[i]->name);
552 }
553 fprintf(output, "\n");
554}
555#endif
556
557/************************************************************************
558 * *
559 * Routines to handle Variable *
560 * *
561 * UNIMPLEMENTED CURRENTLY *
562 * *
563 ************************************************************************/
564
565/**
566 * xmlXPathVariablelookup:
567 * @ctxt: the XPath Parser context
568 * @prefix: the variable name namespace if any
569 * @name: the variable name
570 *
571 * Search in the Variable array of the context for the given
572 * variable value.
573 *
574 * UNIMPLEMENTED: always return NULL.
575 *
576 * Returns the value or NULL if not found
577 */
578xmlXPathObjectPtr
579xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt,
580 const CHAR *prefix, const CHAR *name) {
581 return(NULL);
582}
583
584/************************************************************************
585 * *
586 * Routines to handle Values *
587 * *
588 ************************************************************************/
589
590/* Allocations are terrible, one need to optimize all this !!! */
591
592/**
593 * xmlXPathNewFloat:
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000594 * @val: the double value
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000595 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000596 * Create a new xmlXPathObjectPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000597 *
598 * Returns the newly created object.
599 */
600xmlXPathObjectPtr
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000601xmlXPathNewFloat(double val) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000602 xmlXPathObjectPtr ret;
603
604 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
605 if (ret == NULL) {
606 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
607 return(NULL);
608 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000609 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000610 ret->type = XPATH_NUMBER;
611 ret->floatval = val;
612 return(ret);
613}
614
615/**
616 * xmlXPathNewBoolean:
617 * @val: the boolean value
618 *
619 * Create a new xmlXPathObjectPtr of type boolean and of value @val
620 *
621 * Returns the newly created object.
622 */
623xmlXPathObjectPtr
624xmlXPathNewBoolean(int val) {
625 xmlXPathObjectPtr ret;
626
627 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
628 if (ret == NULL) {
629 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
630 return(NULL);
631 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000632 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000633 ret->type = XPATH_BOOLEAN;
634 ret->boolval = (val != 0);
635 return(ret);
636}
637
638/**
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000639 * xmlXPathNewBoolean:
640 * @val: the CHAR * value
641 *
642 * Create a new xmlXPathObjectPtr of type string and of value @val
643 *
644 * Returns the newly created object.
645 */
646xmlXPathObjectPtr
647xmlXPathNewString(const CHAR *val) {
648 xmlXPathObjectPtr ret;
649
650 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
651 if (ret == NULL) {
652 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
653 return(NULL);
654 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000655 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000656 ret->type = XPATH_STRING;
657 ret->stringval = xmlStrdup(val);
658 return(ret);
659}
660
661/**
662 * xmlXPathNewNodeSet:
663 * @val: the NodePtr value
664 *
665 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
666 * it with the single Node @val
667 *
668 * Returns the newly created object.
669 */
670xmlXPathObjectPtr
671xmlXPathNewNodeSet(xmlNodePtr val) {
672 xmlXPathObjectPtr ret;
673
674 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
675 if (ret == NULL) {
676 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
677 return(NULL);
678 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000679 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000680 ret->type = XPATH_NODESET;
681 ret->nodesetval = xmlXPathNodeSetCreate(val);
682 return(ret);
683}
684
685/**
686 * xmlXPathNewNodeSetList:
687 * @val: an existing NodeSet
688 *
689 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
690 * it with the Nodeset @val
691 *
692 * Returns the newly created object.
693 */
694xmlXPathObjectPtr
695xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
696 xmlXPathObjectPtr ret;
697
698 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
699 if (ret == NULL) {
700 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
701 return(NULL);
702 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000703 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000704 ret->type = XPATH_NODESET;
705 ret->nodesetval = val;
706 return(ret);
707}
708
709/**
710 * xmlXPathFreeObject:
711 * @obj: the object to free
712 *
713 * Free up an xmlXPathObjectPtr object.
714 */
715void
716xmlXPathFreeObject(xmlXPathObjectPtr obj) {
717 if (obj == NULL) return;
718 if (obj->nodesetval != NULL)
719 xmlXPathFreeNodeSet(obj->nodesetval);
720 if (obj->stringval != NULL)
721 free(obj->stringval);
722#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000723 memset(obj, 0xB , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000724#endif
725 free(obj);
726}
727
728/************************************************************************
729 * *
730 * Routines to handle XPath contexts *
731 * *
732 ************************************************************************/
733
734/**
735 * xmlXPathNewContext:
736 * @doc: the XML document
737 * @variables: the variable list
738 * @functions: the function list
739 * @namespaces: the namespace list
740 *
741 * Create a new xmlXPathContext
742 *
743 * Returns the xmlXPathContext just allocated.
744 */
745xmlXPathContextPtr
746xmlXPathNewContext(xmlDocPtr doc, void *variables, void *functions,
747 void *namespaces) {
748 xmlXPathContextPtr ret;
749
750 ret = (xmlXPathContextPtr) malloc(sizeof(xmlXPathContext));
751 if (ret == NULL) {
752 fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n");
753 return(NULL);
754 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000755 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000756 ret->doc = doc;
757 ret->variables = variables;
758 ret->functions = functions;
759 ret->namespaces = namespaces;
760 return(ret);
761}
762
763/**
764 * xmlXPathFreeContext:
765 * @ctxt: the context to free
766 *
767 * Free up an xmlXPathContext
768 */
769void
770xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
771#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000772 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000773#endif
774 free(ctxt);
775}
776
777/************************************************************************
778 * *
779 * Routines to handle XPath parser contexts *
780 * *
781 ************************************************************************/
782
783#define CHECK_CTXT \
784 if (ctxt == NULL) { \
785 fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \
786 __FILE__, __LINE__); \
787 } \
788
789
790#define CHECK_CONTEXT \
791 if (ctxt == NULL) { \
792 fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \
793 __FILE__, __LINE__); \
794 } \
795 if (ctxt->doc == NULL) { \
796 fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \
797 __FILE__, __LINE__); \
798 } \
799 if (ctxt->doc->root == NULL) { \
800 fprintf(xmlXPathDebug, \
801 "%s:%d Internal error: document without root\n", \
802 __FILE__, __LINE__); \
803 } \
804
805
806/**
807 * xmlXPathNewParserContext:
808 * @str: the XPath expression
809 * @ctxt: the XPath context
810 *
811 * Create a new xmlXPathParserContext
812 *
813 * Returns the xmlXPathParserContext just allocated.
814 */
815xmlXPathParserContextPtr
816xmlXPathNewParserContext(const CHAR *str, xmlXPathContextPtr ctxt) {
817 xmlXPathParserContextPtr ret;
818
819 ret = (xmlXPathParserContextPtr) malloc(sizeof(xmlXPathParserContext));
820 if (ret == NULL) {
821 fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n");
822 return(NULL);
823 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000824 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000825 ret->cur = ret->base = str;
826 ret->context = ctxt;
827
828 /* Allocate the value stack */
829 ret->valueTab = (xmlXPathObjectPtr *)
830 malloc(10 * sizeof(xmlXPathObjectPtr));
831 ret->valueNr = 0;
832 ret->valueMax = 10;
833 ret->value = NULL;
834 return(ret);
835}
836
837/**
838 * xmlXPathFreeParserContext:
839 * @ctxt: the context to free
840 *
841 * Free up an xmlXPathParserContext
842 */
843void
844xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
845 if (ctxt->valueTab != NULL) {
846#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000847 memset(ctxt->valueTab, 0xB , 10 * (size_t) sizeof(xmlXPathObjectPtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000848#endif
849 free(ctxt->valueTab);
850 }
851#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000852 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathParserContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000853#endif
854 free(ctxt);
855}
856
857/************************************************************************
858 * *
859 * The implicit core function library *
860 * *
861 ************************************************************************/
862
863/*
864 * TODO: check the semantic for all these operations in case of operands
865 * with different types, Cast function probably need to be provided
866 * to simplify the coding.
867 */
868
869/*
870 * Auto-pop and cast to a number
871 */
872void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
873
874#define CHECK_ARITY(x) \
875 if (nargs != (x)) { \
876 ERROR(XPATH_INVALID_ARITY); \
877 } \
878
879
880#define POP_FLOAT \
881 arg = valuePop(ctxt); \
882 if (arg == NULL) { \
883 ERROR(XPATH_INVALID_OPERAND); \
884 } \
885 if (arg->type != XPATH_NUMBER) { \
886 valuePush(ctxt, arg); \
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000887 xmlXPathNumberFunction(ctxt, 1); \
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000888 arg = valuePop(ctxt); \
889 }
890
891/**
Daniel Veillard991e63d1999-08-15 23:32:28 +0000892 * xmlXPathEqualNodeSetString
893 * @arg: the nodeset object argument
894 * @str: the string to compare to.
895 *
896 * Implement the equal operation on XPath objects content: @arg1 == @arg2
897 * If one object to be compared is a node-set and the other is a string,
898 * then the comparison will be true if and only if there is a node in
899 * the node-set such that the result of performing the comparison on the
900 * string-value of the node and the other string is true.
901 *
902 * Returns 0 or 1 depending on the results of the test.
903 */
904int
905xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const CHAR *str) {
906 int i;
907 xmlNodeSetPtr ns;
908 CHAR *str2;
909
910 if ((str == NULL) || (arg == NULL) || (arg->type != XPATH_NODESET))
911 return(0);
912 ns = arg->nodesetval;
913 for (i = 0;i < ns->nodeNr;i++) {
914 str2 = xmlNodeGetContent(ns->nodeTab[i]);
915 if ((str2 != NULL) && (!xmlStrcmp(str, str2))) {
916 free(str2);
917 return(1);
918 }
919 free(str2);
920 }
921 return(0);
922}
923
924/**
925 * xmlXPathEqualNodeSetFloat
926 * @arg: the nodeset object argument
927 * @f: the float to compare to
928 *
929 * Implement the equal operation on XPath objects content: @arg1 == @arg2
930 * If one object to be compared is a node-set and the other is a number,
931 * then the comparison will be true if and only if there is a node in
932 * the node-set such that the result of performing the comparison on the
933 * number to be compared and on the result of converting the string-value
934 * of that node to a number using the number function is true.
935 *
936 * Returns 0 or 1 depending on the results of the test.
937 */
938int
939xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, float f) {
940 CHAR buf[100] = "";
941
942 if ((arg == NULL) || (arg->type != XPATH_NODESET))
943 return(0);
944
945 if (isnan(f))
946 sprintf(buf, "NaN");
947 else if (isinf(f) > 0)
948 sprintf(buf, "+Infinity");
949 else if (isinf(f) < 0)
950 sprintf(buf, "-Infinity");
951 else
952 sprintf(buf, "%0g", f);
953
954 return(xmlXPathEqualNodeSetString(arg, buf));
955}
956
957
958/**
959 * xmlXPathEqualNodeSets
960 * @arg1: first nodeset object argument
961 * @arg2: second nodeset object argument
962 *
963 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
964 * If both objects to be compared are node-sets, then the comparison
965 * will be true if and only if there is a node in the first node-set and
966 * a node in the second node-set such that the result of performing the
967 * comparison on the string-values of the two nodes is true.
968 *
969 * (needless to say, this is a costly operation)
970 *
971 * Returns 0 or 1 depending on the results of the test.
972 */
973int
974xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
975 int i;
976 xmlNodeSetPtr ns;
977 CHAR *str;
978
979 if ((arg1 == NULL) || (arg1->type != XPATH_NODESET))
980 return(0);
981 if ((arg2 == NULL) || (arg2->type != XPATH_NODESET))
982 return(0);
983
984 ns = arg1->nodesetval;
985 for (i = 0;i < ns->nodeNr;i++) {
986 str = xmlNodeGetContent(ns->nodeTab[i]);
987 if ((str != NULL) && (xmlXPathEqualNodeSetString(arg2, str))) {
988 free(str);
989 return(1);
990 }
991 free(str);
992 }
993 return(0);
994}
995
996/**
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000997 * xmlXPathEqualValues:
Daniel Veillard991e63d1999-08-15 23:32:28 +0000998 * @ctxt: the XPath Parser context
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000999 *
1000 * Implement the equal operation on XPath objects content: @arg1 == @arg2
1001 *
1002 * Returns 0 or 1 depending on the results of the test.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001003 */
1004int
Daniel Veillard991e63d1999-08-15 23:32:28 +00001005xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
1006 xmlXPathObjectPtr arg1, arg2;
1007 int ret = 0;
1008
1009 arg1 = valuePop(ctxt);
1010 if (arg1 == NULL)
1011 ERROR0(XPATH_INVALID_OPERAND);
1012
1013 arg2 = valuePop(ctxt);
1014 if (arg2 == NULL) {
1015 xmlXPathFreeObject(arg1);
1016 ERROR0(XPATH_INVALID_OPERAND);
1017 }
1018
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001019 if (arg1 == arg2) {
1020#ifdef DEBUG_EXPR
1021 fprintf(xmlXPathDebug, "Equal: by pointer\n");
1022#endif
1023 return(1);
1024 }
Daniel Veillard991e63d1999-08-15 23:32:28 +00001025
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001026 switch (arg1->type) {
1027 case XPATH_UNDEFINED:
1028#ifdef DEBUG_EXPR
1029 fprintf(xmlXPathDebug, "Equal: undefined\n");
1030#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001031 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001032 case XPATH_NODESET:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001033 switch (arg2->type) {
1034 case XPATH_UNDEFINED:
1035#ifdef DEBUG_EXPR
1036 fprintf(xmlXPathDebug, "Equal: undefined\n");
1037#endif
1038 break;
1039 case XPATH_NODESET:
1040 ret = xmlXPathEqualNodeSets(arg1, arg2);
1041 break;
1042 case XPATH_BOOLEAN:
1043 if ((arg1->nodesetval == NULL) ||
1044 (arg1->nodesetval->nodeNr == 0)) ret = 0;
1045 else
1046 ret = 1;
1047 ret = (ret == arg2->boolval);
1048 break;
1049 case XPATH_NUMBER:
1050 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
1051 break;
1052 case XPATH_STRING:
1053 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
1054 break;
1055 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001056 break;
1057 case XPATH_BOOLEAN:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001058 switch (arg2->type) {
1059 case XPATH_UNDEFINED:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001060#ifdef DEBUG_EXPR
Daniel Veillard991e63d1999-08-15 23:32:28 +00001061 fprintf(xmlXPathDebug, "Equal: undefined\n");
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001062#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001063 break;
1064 case XPATH_NODESET:
1065 if ((arg2->nodesetval == NULL) ||
1066 (arg2->nodesetval->nodeNr == 0)) ret = 0;
1067 else
1068 ret = 1;
1069 break;
1070 case XPATH_BOOLEAN:
1071#ifdef DEBUG_EXPR
1072 fprintf(xmlXPathDebug, "Equal: %d boolean %d \n",
1073 arg1->boolval, arg2->boolval);
1074#endif
1075 ret = (arg1->boolval == arg2->boolval);
1076 break;
1077 case XPATH_NUMBER:
1078 if (arg2->floatval) ret = 1;
1079 else ret = 0;
1080 ret = (arg1->boolval == ret);
1081 break;
1082 case XPATH_STRING:
1083 if ((arg2->stringval == NULL) ||
1084 (arg2->stringval[0] == 0)) ret = 0;
1085 else
1086 ret = 1;
1087 ret = (arg1->boolval == ret);
1088 break;
1089 }
1090 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001091 case XPATH_NUMBER:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001092 switch (arg2->type) {
1093 case XPATH_UNDEFINED:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001094#ifdef DEBUG_EXPR
Daniel Veillard991e63d1999-08-15 23:32:28 +00001095 fprintf(xmlXPathDebug, "Equal: undefined\n");
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001096#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001097 break;
1098 case XPATH_NODESET:
1099 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
1100 break;
1101 case XPATH_BOOLEAN:
1102 if (arg1->floatval) ret = 1;
1103 else ret = 0;
1104 ret = (arg2->boolval == ret);
1105 break;
1106 case XPATH_STRING:
1107 valuePush(ctxt, arg2);
1108 xmlXPathNumberFunction(ctxt, 1);
1109 arg2 = valuePop(ctxt);
1110 /* no break on purpose */
1111 case XPATH_NUMBER:
1112 ret = (arg1->floatval == arg2->floatval);
1113 break;
1114 }
1115 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001116 case XPATH_STRING:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001117 switch (arg2->type) {
1118 case XPATH_UNDEFINED:
1119#ifdef DEBUG_EXPR
1120 fprintf(xmlXPathDebug, "Equal: undefined\n");
1121#endif
1122 break;
1123 case XPATH_NODESET:
1124 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
1125 break;
1126 case XPATH_BOOLEAN:
1127 if ((arg1->stringval == NULL) ||
1128 (arg1->stringval[0] == 0)) ret = 0;
1129 else
1130 ret = 1;
1131 ret = (arg2->boolval == ret);
1132 break;
1133 case XPATH_STRING:
1134 ret = !xmlStrcmp(arg1->stringval, arg2->stringval);
1135 break;
1136 case XPATH_NUMBER:
1137 valuePush(ctxt, arg1);
1138 xmlXPathNumberFunction(ctxt, 1);
1139 arg1 = valuePop(ctxt);
1140 ret = (arg1->floatval == arg2->floatval);
1141 break;
1142 }
1143 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001144#ifdef DEBUG_EXPR
1145 fprintf(xmlXPathDebug, "Equal: %s string %s \n",
1146 arg1->stringval, arg2->stringval);
1147#endif
Daniel Veillard991e63d1999-08-15 23:32:28 +00001148 ret = !xmlStrcmp(arg1->stringval, arg2->stringval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001149 }
Daniel Veillard991e63d1999-08-15 23:32:28 +00001150 xmlXPathFreeObject(arg1);
1151 xmlXPathFreeObject(arg2);
1152 return(ret);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001153}
1154
1155/**
1156 * xmlXPathCompareValues:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001157 * @ctxt: the XPath Parser context
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001158 * @inf: less than (1) or greater than (2)
1159 * @strict: is the comparison strict
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001160 *
1161 * Implement the compare operation on XPath objects:
1162 * @arg1 < @arg2 (1, 1, ...
1163 * @arg1 <= @arg2 (1, 0, ...
1164 * @arg1 > @arg2 (0, 1, ...
1165 * @arg1 >= @arg2 (0, 0, ...
1166 *
Daniel Veillard991e63d1999-08-15 23:32:28 +00001167 * When neither object to be compared is a node-set and the operator is
1168 * <=, <, >=, >, then the objects are compared by converted both objects
1169 * to numbers and comparing the numbers according to IEEE 754. The <
1170 * comparison will be true if and only if the first number is less than the
1171 * second number. The <= comparison will be true if and only if the first
1172 * number is less than or equal to the second number. The > comparison
1173 * will be true if and only if the first number is greater than the second
1174 * number. The >= comparison will be true if and only if the first number
1175 * is greater than or equal to the second number.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001176 */
1177int
Daniel Veillard991e63d1999-08-15 23:32:28 +00001178xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
1179 int ret = 0;
1180 xmlXPathObjectPtr arg1, arg2;
1181
1182 arg2 = valuePop(ctxt);
1183 if ((arg2 == NULL) || (arg2->type == XPATH_NODESET)) {
1184 if (arg2 != NULL)
1185 xmlXPathFreeObject(arg2);
1186 ERROR0(XPATH_INVALID_OPERAND);
1187 }
1188
1189 arg1 = valuePop(ctxt);
1190 if ((arg1 == NULL) || (arg1->type == XPATH_NODESET)) {
1191 if (arg1 != NULL)
1192 xmlXPathFreeObject(arg1);
1193 xmlXPathFreeObject(arg2);
1194 ERROR0(XPATH_INVALID_OPERAND);
1195 }
1196
1197 if (arg1->type != XPATH_NUMBER) {
1198 valuePush(ctxt, arg1);
1199 xmlXPathNumberFunction(ctxt, 1);
1200 arg1 = valuePop(ctxt);
1201 }
1202 if (arg1->type != XPATH_NUMBER) {
1203 xmlXPathFreeObject(arg1);
1204 xmlXPathFreeObject(arg2);
1205 ERROR0(XPATH_INVALID_OPERAND);
1206 }
1207 if (arg2->type != XPATH_NUMBER) {
1208 valuePush(ctxt, arg2);
1209 xmlXPathNumberFunction(ctxt, 1);
1210 arg2 = valuePop(ctxt);
1211 }
1212 if (arg2->type != XPATH_NUMBER) {
1213 xmlXPathFreeObject(arg1);
1214 xmlXPathFreeObject(arg2);
1215 ERROR0(XPATH_INVALID_OPERAND);
1216 }
1217 /*
1218 * Add tests for infinity and nan
1219 * => feedback on 3.4 for Inf and NaN
1220 */
1221 if (inf && strict)
1222 ret = (arg1->floatval < arg2->floatval);
1223 else if (inf && !strict)
1224 ret = (arg1->floatval <= arg2->floatval);
1225 else if (!inf && strict)
1226 ret = (arg1->floatval > arg2->floatval);
1227 else if (!inf && !strict)
1228 ret = (arg1->floatval >= arg2->floatval);
1229 xmlXPathFreeObject(arg1);
1230 xmlXPathFreeObject(arg2);
1231 return(ret);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001232}
1233
1234/**
1235 * xmlXPathValueFlipSign:
1236 * @ctxt: the XPath Parser context
1237 *
1238 * Implement the unary - operation on an XPath object
1239 * The numeric operators convert their operands to numbers as if
1240 * by calling the number function.
1241 */
1242void
1243xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
1244 xmlXPathObjectPtr arg;
1245
1246 POP_FLOAT
1247 arg->floatval = -arg->floatval;
1248 valuePush(ctxt, arg);
1249}
1250
1251/**
1252 * xmlXPathAddValues:
1253 * @ctxt: the XPath Parser context
1254 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001255 * Implement the add operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001256 * The numeric operators convert their operands to numbers as if
1257 * by calling the number function.
1258 */
1259void
1260xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
1261 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001262 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001263
1264 POP_FLOAT
1265 val = arg->floatval;
1266 xmlXPathFreeObject(arg);
1267
1268 POP_FLOAT
1269 arg->floatval += val;
1270 valuePush(ctxt, arg);
1271}
1272
1273/**
1274 * xmlXPathSubValues:
1275 * @ctxt: the XPath Parser context
1276 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001277 * Implement the substraction operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001278 * The numeric operators convert their operands to numbers as if
1279 * by calling the number function.
1280 */
1281void
1282xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
1283 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001284 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001285
1286 POP_FLOAT
1287 val = arg->floatval;
1288 xmlXPathFreeObject(arg);
1289
1290 POP_FLOAT
1291 arg->floatval -= val;
1292 valuePush(ctxt, arg);
1293}
1294
1295/**
1296 * xmlXPathMultValues:
1297 * @ctxt: the XPath Parser context
1298 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001299 * Implement the multiply operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001300 * The numeric operators convert their operands to numbers as if
1301 * by calling the number function.
1302 */
1303void
1304xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
1305 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001306 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001307
1308 POP_FLOAT
1309 val = arg->floatval;
1310 xmlXPathFreeObject(arg);
1311
1312 POP_FLOAT
1313 arg->floatval *= val;
1314 valuePush(ctxt, arg);
1315}
1316
1317/**
1318 * xmlXPathDivValues:
1319 * @ctxt: the XPath Parser context
1320 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001321 * Implement the div operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001322 * The numeric operators convert their operands to numbers as if
1323 * by calling the number function.
1324 */
1325void
1326xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
1327 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001328 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001329
1330 POP_FLOAT
1331 val = arg->floatval;
1332 xmlXPathFreeObject(arg);
1333
1334 POP_FLOAT
1335 arg->floatval /= val;
1336 valuePush(ctxt, arg);
1337}
1338
1339/**
1340 * xmlXPathModValues:
1341 * @ctxt: the XPath Parser context
1342 *
1343 * Implement the div operation on XPath objects: @arg1 / @arg2
1344 * The numeric operators convert their operands to numbers as if
1345 * by calling the number function.
1346 */
1347void
1348xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
1349 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001350 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001351
1352 POP_FLOAT
1353 val = arg->floatval;
1354 xmlXPathFreeObject(arg);
1355
1356 POP_FLOAT
1357 arg->floatval /= val;
1358 valuePush(ctxt, arg);
1359}
1360
1361/************************************************************************
1362 * *
1363 * The traversal functions *
1364 * *
1365 ************************************************************************/
1366
1367#define AXIS_ANCESTOR 1
1368#define AXIS_ANCESTOR_OR_SELF 2
1369#define AXIS_ATTRIBUTE 3
1370#define AXIS_CHILD 4
1371#define AXIS_DESCENDANT 5
1372#define AXIS_DESCENDANT_OR_SELF 6
1373#define AXIS_FOLLOWING 7
1374#define AXIS_FOLLOWING_SIBLING 8
1375#define AXIS_NAMESPACE 9
1376#define AXIS_PARENT 10
1377#define AXIS_PRECEDING 11
1378#define AXIS_PRECEDING_SIBLING 12
1379#define AXIS_SELF 13
1380
1381/*
1382 * A traversal function enumerates nodes along an axis.
1383 * Initially it must be called with NULL, and it indicates
1384 * termination on the axis by returning NULL.
1385 */
1386typedef xmlNodePtr (*xmlXPathTraversalFunction)
1387 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
1388
1389/**
1390 * mlXPathNextSelf:
1391 * @ctxt: the XPath Parser context
1392 * @cur: the current node in the traversal
1393 *
1394 * Traversal function for the "self" direction
1395 * he self axis contains just the context node itself
1396 */
1397xmlNodePtr
1398xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1399 if (cur == NULL)
1400 return(ctxt->context->node);
1401 return(NULL);
1402}
1403
1404/**
1405 * mlXPathNextChild:
1406 * @ctxt: the XPath Parser context
1407 * @cur: the current node in the traversal
1408 *
1409 * Traversal function for the "child" direction
1410 * The child axis contains the children of the context node in document order.
1411 */
1412xmlNodePtr
1413xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001414 if (cur == NULL) {
1415 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
1416 return(ctxt->context->doc->root);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001417 return(ctxt->context->node->childs);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001418 }
1419 if (ctxt->context->node->type == XML_DOCUMENT_NODE)
1420 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001421 return(cur->next);
1422}
1423
1424/**
1425 * mlXPathNextDescendant:
1426 * @ctxt: the XPath Parser context
1427 * @cur: the current node in the traversal
1428 *
1429 * Traversal function for the "descendant" direction
1430 * the descendant axis contains the descendants of the context node in document
1431 * order; a descendant is a child or a child of a child and so on.
1432 */
1433xmlNodePtr
1434xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001435 if (cur == NULL) {
1436 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
1437 return(ctxt->context->doc->root);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001438 return(ctxt->context->node->childs);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001439 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001440
1441 if (cur->childs != NULL) return(cur->childs);
1442 if (cur->next != NULL) return(cur->next);
1443
1444 do {
1445 cur = cur->parent;
1446 if (cur == NULL) return(NULL);
1447 if (cur == ctxt->context->node) return(NULL);
1448 if (cur->next != NULL) {
1449 cur = cur->next;
1450 return(cur);
1451 }
1452 } while (cur != NULL);
1453 return(cur);
1454}
1455
1456/**
1457 * mlXPathNextDescendantOrSelf:
1458 * @ctxt: the XPath Parser context
1459 * @cur: the current node in the traversal
1460 *
1461 * Traversal function for the "descendant-or-self" direction
1462 * the descendant-or-self axis contains the context node and the descendants
1463 * of the context node in document order; thus the context node is the first
1464 * node on the axis, and the first child of the context node is the second node
1465 * on the axis
1466 */
1467xmlNodePtr
1468xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1469 if (cur == NULL)
1470 return(ctxt->context->node);
1471
Daniel Veillardb05deb71999-08-10 19:04:08 +00001472 if (cur == (xmlNodePtr) ctxt->context->doc)
1473 return(ctxt->context->doc->root);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001474 if (cur->childs != NULL) return(cur->childs);
1475 if (cur->next != NULL) return(cur->next);
1476
1477 do {
1478 cur = cur->parent;
1479 if (cur == NULL) return(NULL);
1480 if (cur == ctxt->context->node) return(NULL);
1481 if (cur->next != NULL) {
1482 cur = cur->next;
1483 return(cur);
1484 }
1485 } while (cur != NULL);
1486 return(cur);
1487}
1488
1489/**
1490 * xmlXPathNextParent:
1491 * @ctxt: the XPath Parser context
1492 * @cur: the current node in the traversal
1493 *
1494 * Traversal function for the "parent" direction
1495 * The parent axis contains the parent of the context node, if there is one.
1496 */
1497xmlNodePtr
1498xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1499 /*
1500 * !!!!!!!!!!!!!
1501 * the parent of an attribute or namespace node is the element
1502 * to which the attribute or namespace node is attached
1503 */
Daniel Veillardb05deb71999-08-10 19:04:08 +00001504 if (cur == NULL) {
1505 if (ctxt->context->node->parent == NULL)
1506 return((xmlNodePtr) ctxt->context->doc);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001507 return(ctxt->context->node->parent);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001508 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001509 return(NULL);
1510}
1511
1512/**
1513 * xmlXPathNextAncestor:
1514 * @ctxt: the XPath Parser context
1515 * @cur: the current node in the traversal
1516 *
1517 * Traversal function for the "ancestor" direction
1518 * the ancestor axis contains the ancestors of the context node; the ancestors
1519 * of the context node consist of the parent of context node and the parent's
1520 * parent and so on; the nodes are ordered in reverse document order; thus the
1521 * parent is the first node on the axis, and the parent's parent is the second
1522 * node on the axis
1523 */
1524xmlNodePtr
1525xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1526 /*
1527 * !!!!!!!!!!!!!
1528 * the parent of an attribute or namespace node is the element
1529 * to which the attribute or namespace node is attached
1530 */
Daniel Veillardb05deb71999-08-10 19:04:08 +00001531 if (cur == NULL) {
1532 if (ctxt->context->node->parent == NULL)
1533 return((xmlNodePtr) ctxt->context->doc);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001534 return(ctxt->context->node->parent);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001535 }
1536 if (cur == ctxt->context->doc->root)
1537 return((xmlNodePtr) ctxt->context->doc);
1538 if (cur == (xmlNodePtr) ctxt->context->doc)
1539 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001540 return(cur->parent);
1541}
1542
1543/**
1544 * xmlXPathNextAncestorOrSelf:
1545 * @ctxt: the XPath Parser context
1546 * @cur: the current node in the traversal
1547 *
1548 * Traversal function for the "ancestor-or-self" direction
1549 * he ancestor-or-self axis contains the context node and ancestors of the context
1550 * node in reverse document order; thus the context node is the first node on the
1551 * axis, and the context node's parent the second; parent here is defined the same
1552 * as with the parent axis.
1553 */
1554xmlNodePtr
1555xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1556 /*
1557 * !!!!!!!!!!!!!
1558 * the parent of an attribute or namespace node is the element
1559 * to which the attribute or namespace node is attached
1560 */
1561 if (cur == NULL)
1562 return(ctxt->context->node);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001563 if (cur == ctxt->context->doc->root)
1564 return((xmlNodePtr) ctxt->context->doc);
1565 if (cur == (xmlNodePtr) ctxt->context->doc)
1566 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001567 return(cur->parent);
1568}
1569
1570/**
1571 * xmlXPathNextFollowingSibling:
1572 * @ctxt: the XPath Parser context
1573 * @cur: the current node in the traversal
1574 *
1575 * Traversal function for the "following-sibling" direction
1576 * The following-sibling axis contains the following siblings of the context
1577 * node in document order.
1578 */
1579xmlNodePtr
1580xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001581 if (cur == (xmlNodePtr) ctxt->context->doc)
1582 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001583 if (cur == NULL)
1584 return(ctxt->context->node->next);
1585 return(cur->next);
1586}
1587
1588/**
1589 * xmlXPathNextPrecedingSibling:
1590 * @ctxt: the XPath Parser context
1591 * @cur: the current node in the traversal
1592 *
1593 * Traversal function for the "preceding-sibling" direction
1594 * The preceding-sibling axis contains the preceding siblings of the context
1595 * node in reverse document order; the first preceding sibling is first on the
1596 * axis; the sibling preceding that node is the second on the axis and so on.
1597 */
1598xmlNodePtr
1599xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001600 if (cur == (xmlNodePtr) ctxt->context->doc)
1601 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001602 if (cur == NULL)
1603 return(ctxt->context->node->prev);
1604 return(cur->prev);
1605}
1606
1607/**
1608 * xmlXPathNextFollowing:
1609 * @ctxt: the XPath Parser context
1610 * @cur: the current node in the traversal
1611 *
1612 * Traversal function for the "following" direction
1613 * The following axis contains all nodes in the same document as the context
1614 * node that are after the context node in document order, excluding any
1615 * descendants and excluding attribute nodes and namespace nodes; the nodes
1616 * are ordered in document order
1617 */
1618xmlNodePtr
1619xmlXPathNextFollowing(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 if (cur->childs != NULL) return(cur->childs);
1625 if (cur->next != NULL) return(cur->next);
1626
1627 do {
1628 cur = cur->parent;
1629 if (cur == NULL) return(NULL);
1630 if (cur == ctxt->context->doc->root) return(NULL);
1631 if (cur->next != NULL) {
1632 cur = cur->next;
1633 return(cur);
1634 }
1635 } while (cur != NULL);
1636 return(cur);
1637}
1638
1639/**
1640 * xmlXPathNextPreceding:
1641 * @ctxt: the XPath Parser context
1642 * @cur: the current node in the traversal
1643 *
1644 * Traversal function for the "preceding" direction
1645 * the preceding axis contains all nodes in the same document as the context
1646 * node that are before the context node in document order, excluding any
1647 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
1648 * ordered in reverse document order
1649 */
1650xmlNodePtr
1651xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001652 if (cur == (xmlNodePtr) ctxt->context->doc)
1653 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001654 if (cur == NULL)
1655 return(ctxt->context->node->prev); /* !!!!!!!!! */
1656 if (cur->last != NULL) return(cur->last);
1657 if (cur->prev != NULL) return(cur->prev);
1658
1659 do {
1660 cur = cur->parent;
1661 if (cur == NULL) return(NULL);
1662 if (cur == ctxt->context->doc->root) return(NULL);
1663 if (cur->prev != NULL) {
1664 cur = cur->prev;
1665 return(cur);
1666 }
1667 } while (cur != NULL);
1668 return(cur);
1669}
1670
1671/**
1672 * xmlXPathNextNamespace:
1673 * @ctxt: the XPath Parser context
1674 * @cur: the current attribute in the traversal
1675 *
1676 * Traversal function for the "namespace" direction
1677 * the namespace axis contains the namespace nodes of the context node;
1678 * the order of nodes on this axis is implementation-defined; the axis will
1679 * be empty unless the context node is an element
1680 */
1681xmlAttrPtr
1682xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
1683 TODO /* namespace traversal */
1684 return(NULL);
1685}
1686
1687/**
1688 * xmlXPathNextAttribute:
1689 * @ctxt: the XPath Parser context
1690 * @cur: the current attribute in the traversal
1691 *
1692 * Traversal function for the "attribute" direction
1693 */
1694xmlAttrPtr
1695xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
Daniel Veillard991e63d1999-08-15 23:32:28 +00001696 if (cur == NULL) {
1697 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
1698 return(NULL);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001699 return(ctxt->context->node->properties);
Daniel Veillard991e63d1999-08-15 23:32:28 +00001700 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001701 return(cur->next);
1702}
1703
1704/************************************************************************
1705 * *
1706 * NodeTest Functions *
1707 * *
1708 ************************************************************************/
1709
1710#define NODE_TEST_NONE 0
1711#define NODE_TEST_TYPE 1
1712#define NODE_TEST_PI 2
1713#define NODE_TEST_ALL 3
1714#define NODE_TEST_NS 4
1715#define NODE_TEST_NAME 5
1716
1717#define NODE_TYPE_COMMENT 50
1718#define NODE_TYPE_TEXT 51
1719#define NODE_TYPE_PI 52
1720#define NODE_TYPE_NODE 53
1721
1722#define IS_FUNCTION 200
1723
1724/**
1725 * xmlXPathNodeCollectAndTest:
1726 * @ctxt: the XPath Parser context
1727 * @cur: the current node to test
1728 *
1729 * This is the function implementing a step: based on the current list
1730 * of nodes, it builds up a new list, looking at all nodes under that
1731 * axis and selecting them.
1732 *
1733 * Returns the new NodeSet resulting from the search.
1734 */
1735xmlNodeSetPtr
1736xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis,
1737 int test, int type, const CHAR *prefix, const CHAR *name) {
1738#ifdef DEBUG_STEP
1739 int n = 0, t = 0;
1740#endif
1741 int i;
1742 xmlNodeSetPtr ret;
1743 xmlXPathTraversalFunction next = NULL;
1744 xmlNodePtr cur = NULL;
1745
1746 if (ctxt->context->nodelist == NULL) {
1747 if (ctxt->context->node == NULL) {
1748 fprintf(xmlXPathDebug,
1749 "xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n",
1750 __FILE__, __LINE__);
1751 return(NULL);
1752 }
1753 STRANGE
1754 return(NULL);
1755 }
1756#ifdef DEBUG_STEP
1757 fprintf(xmlXPathDebug, "new step : ");
1758#endif
1759 switch (axis) {
1760 case AXIS_ANCESTOR:
1761#ifdef DEBUG_STEP
1762 fprintf(xmlXPathDebug, "axis 'ancestors' ");
1763#endif
1764 next = xmlXPathNextAncestor; break;
1765 case AXIS_ANCESTOR_OR_SELF:
1766#ifdef DEBUG_STEP
1767 fprintf(xmlXPathDebug, "axis 'ancestors-or-self' ");
1768#endif
1769 next = xmlXPathNextAncestorOrSelf; break;
1770 case AXIS_ATTRIBUTE:
1771#ifdef DEBUG_STEP
1772 fprintf(xmlXPathDebug, "axis 'attributes' ");
1773#endif
1774 TODO /* attribute axis */
1775 break;
1776 case AXIS_CHILD:
1777#ifdef DEBUG_STEP
1778 fprintf(xmlXPathDebug, "axis 'child' ");
1779#endif
1780 next = xmlXPathNextChild; break;
1781 case AXIS_DESCENDANT:
1782#ifdef DEBUG_STEP
1783 fprintf(xmlXPathDebug, "axis 'descendant' ");
1784#endif
1785 next = xmlXPathNextDescendant; break;
1786 case AXIS_DESCENDANT_OR_SELF:
1787#ifdef DEBUG_STEP
1788 fprintf(xmlXPathDebug, "axis 'descendant-or-self' ");
1789#endif
1790 next = xmlXPathNextDescendantOrSelf; break;
1791 case AXIS_FOLLOWING:
1792#ifdef DEBUG_STEP
1793 fprintf(xmlXPathDebug, "axis 'following' ");
1794#endif
1795 next = xmlXPathNextFollowing; break;
1796 case AXIS_FOLLOWING_SIBLING:
1797#ifdef DEBUG_STEP
1798 fprintf(xmlXPathDebug, "axis 'following-siblings' ");
1799#endif
1800 next = xmlXPathNextFollowingSibling; break;
1801 case AXIS_NAMESPACE:
1802#ifdef DEBUG_STEP
1803 fprintf(xmlXPathDebug, "axis 'namespace' ");
1804#endif
1805 TODO /* namespace axis */
1806 break;
1807 case AXIS_PARENT:
1808#ifdef DEBUG_STEP
1809 fprintf(xmlXPathDebug, "axis 'parent' ");
1810#endif
1811 next = xmlXPathNextParent; break;
1812 case AXIS_PRECEDING:
1813#ifdef DEBUG_STEP
1814 fprintf(xmlXPathDebug, "axis 'preceding' ");
1815#endif
1816 next = xmlXPathNextPreceding; break;
1817 case AXIS_PRECEDING_SIBLING:
1818#ifdef DEBUG_STEP
1819 fprintf(xmlXPathDebug, "axis 'preceding-sibling' ");
1820#endif
1821 next = xmlXPathNextPrecedingSibling; break;
1822 case AXIS_SELF:
1823#ifdef DEBUG_STEP
1824 fprintf(xmlXPathDebug, "axis 'self' ");
1825#endif
1826 next = xmlXPathNextSelf; break;
1827 }
1828 if (next == NULL) return(NULL);
1829 ret = xmlXPathNodeSetCreate(NULL);
1830#ifdef DEBUG_STEP
1831 fprintf(xmlXPathDebug, " context contains %d nodes\n",
1832 ctxt->context->nodelist->nodeNr);
1833 switch (test) {
1834 case NODE_TEST_NONE:
1835 fprintf(xmlXPathDebug, " seaching for none !!!\n");
1836 break;
1837 case NODE_TEST_TYPE:
1838 fprintf(xmlXPathDebug, " seaching for type %d\n", type);
1839 break;
1840 case NODE_TEST_PI:
1841 fprintf(xmlXPathDebug, " seaching for PI !!!\n");
1842 TODO /* PI search */
1843 break;
1844 case NODE_TEST_ALL:
1845 fprintf(xmlXPathDebug, " seaching for *\n");
1846 break;
1847 case NODE_TEST_NS:
1848 fprintf(xmlXPathDebug, " seaching for namespace %s\n",
1849 prefix);
1850 break;
1851 case NODE_TEST_NAME:
1852 fprintf(xmlXPathDebug, " seaching for name %s\n", name);
1853 if (prefix != NULL)
1854 fprintf(xmlXPathDebug, " with namespace %s\n",
1855 prefix);
1856 break;
1857 }
1858 fprintf(xmlXPathDebug, "Testing : ");
1859#endif
1860 for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) {
1861 ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
1862
1863 cur = NULL;
1864 do {
1865 cur = next(ctxt, cur);
1866 if (cur == NULL) break;
1867#ifdef DEBUG_STEP
1868 t++;
1869 fprintf(xmlXPathDebug, " %s", cur->name);
1870#endif
1871 switch (test) {
1872 case NODE_TEST_NONE:
1873 STRANGE
1874 return(NULL);
1875 case NODE_TEST_TYPE:
1876 if (cur->type == type) {
1877#ifdef DEBUG_STEP
1878 n++;
1879#endif
1880 xmlXPathNodeSetAdd(ret, cur);
1881 }
1882 break;
1883 case NODE_TEST_PI:
1884 TODO /* PI search */
1885 break;
1886 case NODE_TEST_ALL:
1887 if (cur->type == XML_ELEMENT_NODE) {
1888#ifdef DEBUG_STEP
1889 n++;
1890#endif
1891 xmlXPathNodeSetAdd(ret, cur);
1892 }
1893 break;
1894 case NODE_TEST_NS:
1895 TODO /* NS search */
1896 break;
1897 case NODE_TEST_NAME:
Daniel Veillardb05deb71999-08-10 19:04:08 +00001898 switch (cur->type) {
1899 case XML_ELEMENT_NODE:
1900 if (!xmlStrcmp(name, cur->name) &&
1901 (((prefix == NULL) ||
1902 ((cur->ns != NULL) &&
1903 (!xmlStrcmp(prefix, cur->ns->href)))))) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001904#ifdef DEBUG_STEP
Daniel Veillardb05deb71999-08-10 19:04:08 +00001905 n++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001906#endif
Daniel Veillardb05deb71999-08-10 19:04:08 +00001907 xmlXPathNodeSetAdd(ret, cur);
1908 }
1909 default:
1910 break;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001911 }
1912 break;
1913
1914 }
1915 } while (cur != NULL);
1916 }
1917#ifdef DEBUG_STEP
1918 fprintf(xmlXPathDebug,
1919 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
1920#endif
1921 return(ret);
1922}
1923
1924
1925/************************************************************************
1926 * *
1927 * Implicit tree core function library *
1928 * *
1929 ************************************************************************/
1930
1931/**
1932 * xmlXPathRoot:
1933 * @ctxt: the XPath Parser context
1934 *
1935 * Initialize the context to the root of the document
1936 */
1937void
1938xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
1939 if (ctxt->context->nodelist != NULL)
1940 xmlXPathFreeNodeSet(ctxt->context->nodelist);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001941 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
1942 ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->node);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001943}
1944
1945/**
1946 * xmlXPathSearchPI:
1947 * @ctxt: the XPath Parser context
1948 *
1949 * Search a processing instruction by name. The name is
1950 * in the object on the stack.
1951 */
1952void
1953xmlXPathSearchPI(xmlXPathParserContextPtr ctxt, int nargs) {
1954 TODO /* Search PI */
1955 CHECK_ARITY(0);
1956 CHECK_TYPE(XPATH_NUMBER)
1957}
1958
1959/************************************************************************
1960 * *
1961 * The explicit core function library *
1962 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
1963 * *
1964 ************************************************************************/
1965
1966
1967/**
1968 * xmlXPathLastFunction:
1969 * @ctxt: the XPath Parser context
1970 *
1971 * Implement the last() XPath function
1972 * The last function returns the number of nodes in the context node list.
1973 */
1974void
1975xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1976 CHECK_ARITY(0);
1977 if ((ctxt->context->nodelist == NULL) ||
1978 (ctxt->context->node == NULL) ||
1979 (ctxt->context->nodelist->nodeNr == 0)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001980 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001981 } else {
1982 valuePush(ctxt,
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001983 xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001984 }
1985}
1986
1987/**
1988 * xmlXPathPositionFunction:
1989 * @ctxt: the XPath Parser context
1990 *
1991 * Implement the position() XPath function
1992 * The position function returns the position of the context node in the
1993 * context node list. The first position is 1, and so the last positionr
1994 * will be equal to last().
1995 */
1996void
1997xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1998 int i;
1999
2000 CHECK_ARITY(0);
2001 if ((ctxt->context->nodelist == NULL) ||
2002 (ctxt->context->node == NULL) ||
2003 (ctxt->context->nodelist->nodeNr == 0)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002004 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002005 }
2006 for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) {
2007 if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002008 valuePush(ctxt, xmlXPathNewFloat((double) i + 1));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002009 return;
2010 }
2011 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002012 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002013}
2014
2015/**
2016 * xmlXPathCountFunction:
2017 * @ctxt: the XPath Parser context
2018 *
2019 * Implement the count() XPath function
2020 */
2021void
2022xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2023 xmlXPathObjectPtr cur;
2024
2025 CHECK_ARITY(1);
2026 CHECK_TYPE(XPATH_NODESET);
2027 cur = valuePop(ctxt);
2028
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002029 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002030 xmlXPathFreeObject(cur);
2031}
2032
2033/**
2034 * xmlXPathIdFunction:
2035 * @ctxt: the XPath Parser context
2036 *
2037 * Implement the id() XPath function
2038 * The id function selects elements by their unique ID
2039 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
2040 * then the result is the union of the result of applying id to the
2041 * string value of each of the nodes in the argument node-set. When the
2042 * argument to id is of any other type, the argument is converted to a
2043 * string as if by a call to the string function; the string is split
2044 * into a whitespace-separated list of tokens (whitespace is any sequence
2045 * of characters matching the production S); the result is a node-set
2046 * containing the elements in the same document as the context node that
2047 * have a unique ID equal to any of the tokens in the list.
2048 */
2049void
2050xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2051 CHECK_ARITY(1);
2052 TODO /* Write ID/IDREF support in libxml first */
2053}
2054
2055/**
2056 * xmlXPathLocalPartFunction:
2057 * @ctxt: the XPath Parser context
2058 *
2059 * Implement the local-part() XPath function
2060 * The local-part function returns a string containing the local part
2061 * of the name of the node in the argument node-set that is first in
2062 * document order. If the node-set is empty or the first node has no
2063 * name, an empty string is returned. If the argument is omitted it
2064 * defaults to the context node.
2065 */
2066void
2067xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2068 xmlXPathObjectPtr cur;
2069
2070 CHECK_ARITY(1);
2071 CHECK_TYPE(XPATH_NODESET);
2072 cur = valuePop(ctxt);
2073
2074 if (cur->nodesetval->nodeNr == 0) {
2075 valuePush(ctxt, xmlXPathNewString(""));
2076 } else {
2077 int i = 0; /* Should be first in document order !!!!! */
2078 valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
2079 }
2080 xmlXPathFreeObject(cur);
2081}
2082
2083/**
2084 * xmlXPathNamespaceFunction:
2085 * @ctxt: the XPath Parser context
2086 *
2087 * Implement the namespace() XPath function
2088 * The namespace function returns a string containing the namespace URI
2089 * of the expanded name of the node in the argument node-set that is
2090 * first in document order. If the node-set is empty, the first node has
2091 * no name, or the expanded name has no namespace URI, an empty string
2092 * is returned. If the argument is omitted it defaults to the context node.
2093 */
2094void
2095xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2096 xmlXPathObjectPtr cur;
2097
2098 CHECK_ARITY(1);
2099 CHECK_TYPE(XPATH_NODESET);
2100 cur = valuePop(ctxt);
2101
2102 if (cur->nodesetval->nodeNr == 0) {
2103 valuePush(ctxt, xmlXPathNewString(""));
2104 } else {
2105 int i = 0; /* Should be first in document order !!!!! */
2106
2107 if (cur->nodesetval->nodeTab[i]->ns == NULL)
2108 valuePush(ctxt, xmlXPathNewString(""));
2109 else
2110 valuePush(ctxt, xmlXPathNewString(
2111 cur->nodesetval->nodeTab[i]->ns->href));
2112 }
2113 xmlXPathFreeObject(cur);
2114}
2115
2116/**
2117 * xmlXPathNameFunction:
2118 * @ctxt: the XPath Parser context
2119 *
2120 * Implement the name() XPath function
2121 * The name function returns a string containing a QName representing
2122 * the name of the node in the argument node-set that is first in documenti
2123 * order. The QName must represent the name with respect to the namespace
2124 * declarations in effect on the node whose name is being represented.
2125 * Typically, this will be the form in which the name occurred in the XML
2126 * source. This need not be the case if there are namespace declarations
2127 * in effect on the node that associate multiple prefixes with the same
2128 * namespace. However, an implementation may include information about
2129 * the original prefix in its representation of nodes; in this case, an
2130 * implementation can ensure that the returned string is always the same
2131 * as the QName used in the XML source. If the argument it omitted it
2132 * defaults to the context node.
2133 * Libxml keep the original prefix so the "real qualified name" used is
2134 * returned.
2135 */
2136void
2137xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2138 xmlXPathObjectPtr cur;
2139
2140 CHECK_ARITY(1);
2141 CHECK_TYPE(XPATH_NODESET);
2142 cur = valuePop(ctxt);
2143
2144 if (cur->nodesetval->nodeNr == 0) {
2145 valuePush(ctxt, xmlXPathNewString(""));
2146 } else {
2147 int i = 0; /* Should be first in document order !!!!! */
2148
2149 if (cur->nodesetval->nodeTab[i]->ns == NULL)
2150 valuePush(ctxt, xmlXPathNewString(
2151 cur->nodesetval->nodeTab[i]->name));
2152
2153 else {
2154 CHAR name[2000];
2155 sprintf(name, "%s:%s", cur->nodesetval->nodeTab[i]->ns->prefix,
2156 cur->nodesetval->nodeTab[i]->name);
2157 valuePush(ctxt, xmlXPathNewString(name));
2158 }
2159 }
2160 xmlXPathFreeObject(cur);
2161}
2162
2163/**
2164 * xmlXPathStringFunction:
2165 * @ctxt: the XPath Parser context
2166 *
2167 * Implement the string() XPath function
2168 * he string function converts an object to a string as follows:
2169 * - A node-set is converted to a string by returning the value of
2170 * the node in the node-set that is first in document order.
2171 * If the node-set is empty, an empty string is returned.
2172 * - A number is converted to a string as follows
2173 * + NaN is converted to the string NaN
2174 * + positive zero is converted to the string 0
2175 * + negative zero is converted to the string 0
2176 * + positive infinity is converted to the string Infinity
2177 * + negative infinity is converted to the string -Infinity
2178 * + if the number is an integer, the number is represented in
2179 * decimal form as a Number with no decimal point and no leading
2180 * zeros, preceded by a minus sign (-) if the number is negative
2181 * + otherwise, the number is represented in decimal form as a
2182 * Number including a decimal point with at least one digit
2183 * before the decimal point and at least one digit after the
2184 * decimal point, preceded by a minus sign (-) if the number
2185 * is negative; there must be no leading zeros before the decimal
2186 * point apart possibly from the one required digit immediatelyi
2187 * before the decimal point; beyond the one required digit
2188 * after the decimal point there must be as many, but only as
2189 * many, more digits as are needed to uniquely distinguish the
2190 * number from all other IEEE 754 numeric values.
2191 * - The boolean false value is converted to the string false.
2192 * The boolean true value is converted to the string true.
2193 */
2194void
2195xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2196 xmlXPathObjectPtr cur;
2197
2198 CHECK_ARITY(1);
2199 cur = valuePop(ctxt);
2200 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
2201 switch (cur->type) {
2202 case XPATH_NODESET:
2203 if (cur->nodesetval->nodeNr == 0) {
2204 valuePush(ctxt, xmlXPathNewString(""));
2205 } else {
2206 CHAR *res;
2207 int i = 0; /* Should be first in document order !!!!! */
2208 res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
2209 valuePush(ctxt, xmlXPathNewString(res));
2210 free(res);
2211 }
2212 xmlXPathFreeObject(cur);
2213 return;
2214 case XPATH_STRING:
2215 valuePush(ctxt, cur);
2216 return;
2217 case XPATH_BOOLEAN:
2218 if (cur->boolval) valuePush(ctxt, xmlXPathNewString("true"));
2219 else valuePush(ctxt, xmlXPathNewString("false"));
2220 xmlXPathFreeObject(cur);
2221 return;
2222 case XPATH_NUMBER: {
2223 CHAR buf[100];
2224
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002225 if (isnan(cur->floatval))
2226 sprintf(buf, "NaN");
2227 else if (isinf(cur->floatval) > 0)
2228 sprintf(buf, "+Infinity");
2229 else if (isinf(cur->floatval) < 0)
2230 sprintf(buf, "-Infinity");
2231 else
2232 sprintf(buf, "%0g", cur->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002233 valuePush(ctxt, xmlXPathNewString(buf));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002234 xmlXPathFreeObject(cur);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002235 return;
2236 }
2237 }
2238 STRANGE
2239}
2240
2241/**
2242 * xmlXPathStringLengthFunction:
2243 * @ctxt: the XPath Parser context
2244 *
2245 * Implement the string-length() XPath function
2246 * The string-length returns the number of characters in the string
2247 * (see [3.6 Strings]). If the argument is omitted, it defaults to
2248 * the context node converted to a string, in other words the value
2249 * of the context node.
2250 */
2251void
2252xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2253 xmlXPathObjectPtr cur;
2254
2255 if (nargs == 0) {
2256 if (ctxt->context->node == NULL) {
2257 valuePush(ctxt, xmlXPathNewFloat(0));
2258 } else {
2259 CHAR *content;
2260
2261 content = xmlNodeGetContent(ctxt->context->node);
2262 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
2263 free(content);
2264 }
2265 return;
2266 }
2267 CHECK_ARITY(1);
2268 CHECK_TYPE(XPATH_STRING);
2269 cur = valuePop(ctxt);
2270 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
2271 xmlXPathFreeObject(cur);
2272}
2273
2274/**
2275 * xmlXPathConcatFunction:
2276 * @ctxt: the XPath Parser context
2277 *
2278 * Implement the concat() XPath function
2279 * The concat function returns the concatenation of its arguments.
2280 */
2281void
2282xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2283 xmlXPathObjectPtr cur, new;
2284 CHAR *tmp;
2285
2286 if (nargs < 2) {
2287 CHECK_ARITY(2);
2288 }
2289
2290 cur = valuePop(ctxt);
2291 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
2292 xmlXPathFreeObject(cur);
2293 return;
2294 }
2295 nargs--;
2296
2297 while (nargs > 0) {
2298 new = valuePop(ctxt);
2299 if ((new == NULL) || (new->type != XPATH_STRING)) {
2300 xmlXPathFreeObject(new);
2301 xmlXPathFreeObject(cur);
2302 ERROR(XPATH_INVALID_TYPE);
2303 }
2304 tmp = xmlStrcat(new->stringval, cur->stringval);
2305 new->stringval = cur->stringval;
2306 cur->stringval = tmp;
2307
2308 xmlXPathFreeObject(new);
2309 nargs--;
2310 }
2311 valuePush(ctxt, cur);
2312}
2313
2314/**
2315 * xmlXPathContainsFunction:
2316 * @ctxt: the XPath Parser context
2317 *
2318 * Implement the contains() XPath function
2319 * The contains function returns true if the first argument string
2320 * contains the second argument string, and otherwise returns false.
2321 */
2322void
2323xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2324 xmlXPathObjectPtr hay, needle;
2325
2326 CHECK_ARITY(2);
2327 CHECK_TYPE(XPATH_STRING);
2328 needle = valuePop(ctxt);
2329 hay = valuePop(ctxt);
2330 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
2331 xmlXPathFreeObject(hay);
2332 xmlXPathFreeObject(needle);
2333 ERROR(XPATH_INVALID_TYPE);
2334 }
2335 if (xmlStrstr(hay->stringval, needle->stringval))
2336 valuePush(ctxt, xmlXPathNewBoolean(1));
2337 else
2338 valuePush(ctxt, xmlXPathNewBoolean(0));
2339 xmlXPathFreeObject(hay);
2340 xmlXPathFreeObject(needle);
2341}
2342
2343/**
2344 * xmlXPathStartsWithFunction:
2345 * @ctxt: the XPath Parser context
2346 *
2347 * Implement the starts-with() XPath function
2348 * The starts-with function returns true if the first argument string
2349 * starts with the second argument string, and otherwise returns false.
2350 */
2351void
2352xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2353 xmlXPathObjectPtr hay, needle;
2354 int n;
2355
2356 CHECK_ARITY(2);
2357 CHECK_TYPE(XPATH_STRING);
2358 needle = valuePop(ctxt);
2359 hay = valuePop(ctxt);
2360 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
2361 xmlXPathFreeObject(hay);
2362 xmlXPathFreeObject(needle);
2363 ERROR(XPATH_INVALID_TYPE);
2364 }
2365 n = xmlStrlen(needle->stringval);
2366 if (xmlStrncmp(hay->stringval, needle->stringval, n))
2367 valuePush(ctxt, xmlXPathNewBoolean(0));
2368 else
2369 valuePush(ctxt, xmlXPathNewBoolean(1));
2370 xmlXPathFreeObject(hay);
2371 xmlXPathFreeObject(needle);
2372}
2373
2374/**
2375 * xmlXPathSubstringFunction:
2376 * @ctxt: the XPath Parser context
2377 *
2378 * Implement the substring() XPath function
2379 * The substring function returns the substring of the first argument
2380 * starting at the position specified in the second argument with
2381 * length specified in the third argument. For example,
2382 * substring("12345",2,3) returns "234". If the third argument is not
2383 * specified, it returns the substring starting at the position specified
2384 * in the second argument and continuing to the end of the string. For
2385 * example, substring("12345",2) returns "2345". More precisely, each
2386 * character in the string (see [3.6 Strings]) is considered to have a
2387 * numeric position: the position of the first character is 1, the position
2388 * of the second character is 2 and so on. The returned substring contains
2389 * those characters for which the position of the character is greater than
2390 * or equal to the second argument and, if the third argument is specified,
2391 * less than the sum of the second and third arguments; the comparisons
2392 * and addition used for the above follow the standard IEEE 754 rules. Thus:
2393 * - substring("12345", 1.5, 2.6) returns "234"
2394 * - substring("12345", 0, 3) returns "12"
2395 * - substring("12345", 0 div 0, 3) returns ""
2396 * - substring("12345", 1, 0 div 0) returns ""
2397 * - substring("12345", -42, 1 div 0) returns "12345"
2398 * - substring("12345", -1 div 0, 1 div 0) returns ""
2399 */
2400void
2401xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2402 xmlXPathObjectPtr str, start, len;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002403 double le, in;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002404 int i, l;
2405 CHAR *ret;
2406
2407 /*
2408 * Conformance needs to be checked !!!!!
2409 */
2410 if (nargs < 2) {
2411 CHECK_ARITY(2);
2412 }
2413 if (nargs > 3) {
2414 CHECK_ARITY(3);
2415 }
2416 if (nargs == 3) {
2417 CHECK_TYPE(XPATH_NUMBER);
2418 len = valuePop(ctxt);
2419 le = len->floatval;
2420 xmlXPathFreeObject(len);
2421 } else {
2422 le = 2000000000;
2423 }
2424 CHECK_TYPE(XPATH_NUMBER);
2425 start = valuePop(ctxt);
2426 in = start->floatval;
2427 xmlXPathFreeObject(start);
2428 CHECK_TYPE(XPATH_STRING);
2429 str = valuePop(ctxt);
2430 le += in;
2431
2432 /* integer index of the first char */
2433 i = in;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002434 if (((double)i) != in) i++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002435
2436 /* integer index of the last char */
2437 l = le;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002438 if (((double)l) != le) l++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002439
2440 /* back to a zero based len */
2441 i--;
2442 l--;
2443
2444 /* check against the string len */
2445 if (l > 1024) {
2446 l = xmlStrlen(str->stringval);
2447 }
2448 if (i < 0) {
2449 i = 0;
2450 }
2451
2452 /* number of chars to copy */
2453 l -= i;
2454
2455 ret = xmlStrsub(str->stringval, i, l);
2456 if (ret == NULL)
2457 valuePush(ctxt, xmlXPathNewString(""));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002458 else {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002459 valuePush(ctxt, xmlXPathNewString(ret));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002460 free(ret);
2461 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002462 xmlXPathFreeObject(str);
2463}
2464
2465/**
2466 * xmlXPathSubstringBeforeFunction:
2467 * @ctxt: the XPath Parser context
2468 *
2469 * Implement the substring-before() XPath function
2470 * The substring-before function returns the substring of the first
2471 * argument string that precedes the first occurrence of the second
2472 * argument string in the first argument string, or the empty string
2473 * if the first argument string does not contain the second argument
2474 * string. For example, substring-before("1999/04/01","/") returns 1999.
2475 */
2476void
2477xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2478 CHECK_ARITY(2);
2479 TODO /* substring before */
2480}
2481
2482/**
2483 * xmlXPathSubstringAfterFunction:
2484 * @ctxt: the XPath Parser context
2485 *
2486 * Implement the substring-after() XPath function
2487 * The substring-after function returns the substring of the first
2488 * argument string that follows the first occurrence of the second
2489 * argument string in the first argument string, or the empty stringi
2490 * if the first argument string does not contain the second argument
2491 * string. For example, substring-after("1999/04/01","/") returns 04/01,
2492 * and substring-after("1999/04/01","19") returns 99/04/01.
2493 */
2494void
2495xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2496 CHECK_ARITY(2);
2497 TODO /* substring after */
2498}
2499
2500/**
2501 * xmlXPathNormalizeFunction:
2502 * @ctxt: the XPath Parser context
2503 *
2504 * Implement the normalize() XPath function
2505 * The normalize function returns the argument string with white
2506 * space normalized by stripping leading and trailing whitespace
2507 * and replacing sequences of whitespace characters by a single
2508 * space. Whitespace characters are the same allowed by the S production
2509 * in XML. If the argument is omitted, it defaults to the context
2510 * node converted to a string, in other words the value of the context node.
2511 */
2512void
2513xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2514 CHECK_ARITY(1);
2515 TODO /* normalize isn't as boring as translate, but pretty much */
2516}
2517
2518/**
2519 * xmlXPathTranslateFunction:
2520 * @ctxt: the XPath Parser context
2521 *
2522 * Implement the translate() XPath function
2523 * The translate function returns the first argument string with
2524 * occurrences of characters in the second argument string replaced
2525 * by the character at the corresponding position in the third argument
2526 * string. For example, translate("bar","abc","ABC") returns the string
2527 * BAr. If there is a character in the second argument string with no
2528 * character at a corresponding position in the third argument string
2529 * (because the second argument string is longer than the third argument
2530 * string), then occurrences of that character in the first argument
2531 * string are removed. For example, translate("--aaa--","abc-","ABC")
2532 * returns "AAA". If a character occurs more than once in second
2533 * argument string, then the first occurrence determines the replacement
2534 * character. If the third argument string is longer than the second
2535 * argument string, then excess characters are ignored.
2536 */
2537void
2538xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2539 CHECK_ARITY(3);
2540 TODO /* translate is boring, waiting for UTF-8 representation too */
2541}
2542
2543/**
2544 * xmlXPathBooleanFunction:
2545 * @ctxt: the XPath Parser context
2546 *
2547 * Implement the boolean() XPath function
2548 * he boolean function converts its argument to a boolean as follows:
2549 * - a number is true if and only if it is neither positive or
2550 * negative zero nor NaN
2551 * - a node-set is true if and only if it is non-empty
2552 * - a string is true if and only if its length is non-zero
2553 */
2554void
2555xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2556 xmlXPathObjectPtr cur;
2557 int res = 0;
2558
2559 CHECK_ARITY(1);
2560 cur = valuePop(ctxt);
2561 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
2562 switch (cur->type) {
2563 case XPATH_NODESET:
2564 if ((cur->nodesetval == NULL) ||
2565 (cur->nodesetval->nodeNr == 0)) res = 0;
2566 else
2567 res = 1;
2568 break;
2569 case XPATH_STRING:
2570 if ((cur->stringval == NULL) ||
2571 (cur->stringval[0] == 0)) res = 0;
2572 else
2573 res = 1;
2574 break;
2575 case XPATH_BOOLEAN:
2576 valuePush(ctxt, cur);
2577 return;
2578 case XPATH_NUMBER:
2579 if (cur->floatval) res = 1;
2580 break;
2581 default:
2582 STRANGE
2583 }
2584 xmlXPathFreeObject(cur);
2585 valuePush(ctxt, xmlXPathNewBoolean(res));
2586}
2587
2588/**
2589 * xmlXPathNotFunction:
2590 * @ctxt: the XPath Parser context
2591 *
2592 * Implement the not() XPath function
2593 * The not function returns true if its argument is false,
2594 * and false otherwise.
2595 */
2596void
2597xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2598 CHECK_ARITY(1);
2599 CHECK_TYPE(XPATH_BOOLEAN);
2600 ctxt->value->boolval = ! ctxt->value->boolval;
2601}
2602
2603/**
2604 * xmlXPathTrueFunction:
2605 * @ctxt: the XPath Parser context
2606 *
2607 * Implement the true() XPath function
2608 */
2609void
2610xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2611 CHECK_ARITY(0);
2612 valuePush(ctxt, xmlXPathNewBoolean(1));
2613}
2614
2615/**
2616 * xmlXPathFalseFunction:
2617 * @ctxt: the XPath Parser context
2618 *
2619 * Implement the false() XPath function
2620 */
2621void
2622xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2623 CHECK_ARITY(0);
2624 valuePush(ctxt, xmlXPathNewBoolean(0));
2625}
2626
2627/**
2628 * xmlXPathLangFunction:
2629 * @ctxt: the XPath Parser context
2630 *
2631 * Implement the lang() XPath function
2632 * The lang function returns true or false depending on whether the
2633 * language of the context node as specified by xml:lang attributes
2634 * is the same as or is a sublanguage of the language specified by
2635 * the argument string. The language of the context node is determined
2636 * by the value of the xml:lang attribute on the context node, or, if
2637 * the context node has no xml:lang attribute, by the value of the
2638 * xml:lang attribute on the nearest ancestor of the context node that
2639 * has an xml:lang attribute. If there is no such attribute, then lang
2640 * returns false. If there is such an attribute, then lang returns
2641 * true if the attribute value is equal to the argument ignoring case,
2642 * or if there is some suffix starting with - such that the attribute
2643 * value is equal to the argument ignoring that suffix of the attribute
2644 * value and ignoring case.
2645 */
2646void
2647xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2648 CHECK_ARITY(0);
2649 TODO /* Write down xml:lang support in libxml first */
2650}
2651
2652/**
2653 * xmlXPathNumberFunction:
2654 * @ctxt: the XPath Parser context
2655 *
2656 * Implement the number() XPath function
2657 */
2658void
2659xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2660 xmlXPathObjectPtr cur;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002661 double res;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002662
2663 CHECK_ARITY(1);
2664 cur = valuePop(ctxt);
2665 switch (cur->type) {
2666 case XPATH_NODESET:
2667 valuePush(ctxt, cur);
2668 xmlXPathStringFunction(ctxt, 1);
2669 cur = valuePop(ctxt);
2670 case XPATH_STRING:
2671 res = xmlXPathStringEvalNumber(cur->stringval);
2672 valuePush(ctxt, xmlXPathNewFloat(res));
2673 xmlXPathFreeObject(cur);
2674 return;
2675 case XPATH_BOOLEAN:
2676 if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
2677 else valuePush(ctxt, xmlXPathNewFloat(0.0));
2678 xmlXPathFreeObject(cur);
2679 return;
2680 case XPATH_NUMBER:
2681 valuePush(ctxt, cur);
2682 return;
2683 }
2684 STRANGE
2685}
2686
2687/**
2688 * xmlXPathSumFunction:
2689 * @ctxt: the XPath Parser context
2690 *
2691 * Implement the sum() XPath function
2692 * The sum function returns the sum of the values of the nodes in
2693 * the argument node-set.
2694 */
2695void
2696xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2697 CHECK_ARITY(1);
2698 TODO /* BUG Sum : don't understand the definition */
2699}
2700
2701/**
2702 * xmlXPathFloorFunction:
2703 * @ctxt: the XPath Parser context
2704 *
2705 * Implement the floor() XPath function
2706 * The floor function returns the largest (closest to positive infinity)
2707 * number that is not greater than the argument and that is an integer.
2708 */
2709void
2710xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2711 CHECK_ARITY(1);
2712 CHECK_TYPE(XPATH_NUMBER);
2713 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002714 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002715}
2716
2717/**
2718 * xmlXPathCeilingFunction:
2719 * @ctxt: the XPath Parser context
2720 *
2721 * Implement the ceiling() XPath function
2722 * The ceiling function returns the smallest (closest to negative infinity)
2723 * number that is not less than the argument and that is an integer.
2724 */
2725void
2726xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002727 double f;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002728
2729 CHECK_ARITY(1);
2730 CHECK_TYPE(XPATH_NUMBER);
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002731 f = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002732 if (f != ctxt->value->floatval)
2733 ctxt->value->floatval = f + 1;
2734}
2735
2736/**
2737 * xmlXPathRoundFunction:
2738 * @ctxt: the XPath Parser context
2739 *
2740 * Implement the round() XPath function
2741 * The round function returns the number that is closest to the
2742 * argument and that is an integer. If there are two such numbers,
2743 * then the one that is even is returned.
2744 */
2745void
2746xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002747 double f;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002748
2749 CHECK_ARITY(1);
2750 CHECK_TYPE(XPATH_NUMBER);
2751 /* round(0.50000001) => 0 !!!!! */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002752 f = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002753 if (ctxt->value->floatval < f + 0.5)
2754 ctxt->value->floatval = f;
2755 else if (ctxt->value->floatval == f + 0.5)
2756 ctxt->value->floatval = f; /* !!!! Not following the spec here */
2757 else
2758 ctxt->value->floatval = f + 1;
2759}
2760
2761/************************************************************************
2762 * *
2763 * The Parser *
2764 * *
2765 ************************************************************************/
2766
2767/*
2768 * a couple of forward declarations since we use a recursive call based
2769 * implementation.
2770 */
2771void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
2772void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
2773void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
2774void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
2775
2776/**
2777 * xmlXPathParseNCName:
2778 * @ctxt: the XPath Parser context
2779 *
2780 * parse an XML namespace non qualified name.
2781 *
2782 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
2783 *
2784 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
2785 * CombiningChar | Extender
2786 *
2787 * Returns the namespace name or NULL
2788 */
2789
2790CHAR *
2791xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
2792 const CHAR *q;
2793 CHAR *ret = NULL;
2794
2795 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
2796 q = NEXT;
2797
2798 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
2799 (CUR == '.') || (CUR == '-') ||
2800 (CUR == '_') ||
2801 (IS_COMBINING(CUR)) ||
2802 (IS_EXTENDER(CUR)))
2803 NEXT;
2804
2805 ret = xmlStrndup(q, CUR_PTR - q);
2806
2807 return(ret);
2808}
2809
2810/**
2811 * xmlXPathParseQName:
2812 * @ctxt: the XPath Parser context
2813 * @prefix: a CHAR **
2814 *
2815 * parse an XML qualified name
2816 *
2817 * [NS 5] QName ::= (Prefix ':')? LocalPart
2818 *
2819 * [NS 6] Prefix ::= NCName
2820 *
2821 * [NS 7] LocalPart ::= NCName
2822 *
2823 * Returns the function returns the local part, and prefix is updated
2824 * to get the Prefix if any.
2825 */
2826
2827CHAR *
2828xmlXPathParseQName(xmlXPathParserContextPtr ctxt, CHAR **prefix) {
2829 CHAR *ret = NULL;
2830
2831 *prefix = NULL;
2832 ret = xmlXPathParseNCName(ctxt);
2833 if (CUR == ':') {
2834 *prefix = ret;
2835 NEXT;
2836 ret = xmlXPathParseNCName(ctxt);
2837 }
2838 return(ret);
2839}
2840
2841/**
2842 * xmlXPathStringEvalNumber:
2843 * @str: A string to scan
2844 *
2845 * [30] Number ::= Digits ('.' Digits)?
2846 * | '.' Digits
2847 * [31] Digits ::= [0-9]+
2848 *
2849 * Parse and evaluate a Number in the string
2850 *
2851 * BUG: "1.' is not valid ... James promised correction
2852 * as Digits ('.' Digits?)?
2853 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002854 * Returns the double value.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002855 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002856double
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002857xmlXPathStringEvalNumber(const CHAR *str) {
2858 const CHAR *cur = str;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002859 double ret = 0.0;
2860 double mult = 1;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002861 int ok = 0;
2862
2863 while (*cur == ' ') cur++;
2864 if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002865 return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002866 }
2867 while ((*cur >= '0') && (*cur <= '9')) {
2868 ret = ret * 10 + (*cur - '0');
2869 ok = 1;
2870 cur++;
2871 }
2872 if (*cur == '.') {
2873 cur++;
2874 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002875 return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002876 }
2877 while ((*cur >= '0') && (*cur <= '9')) {
2878 mult /= 10;
2879 ret = ret + (*cur - '0') * mult;
2880 cur++;
2881 }
2882 }
2883 while (*cur == ' ') cur++;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002884 if (*cur != 0) return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002885 return(ret);
2886}
2887
2888/**
2889 * xmlXPathEvalNumber:
2890 * @ctxt: the XPath Parser context
2891 *
2892 * [30] Number ::= Digits ('.' Digits)?
2893 * | '.' Digits
2894 * [31] Digits ::= [0-9]+
2895 *
2896 * Parse and evaluate a Number, then push it on the stack
2897 *
2898 * BUG: "1.' is not valid ... James promised correction
2899 * as Digits ('.' Digits?)?
2900 */
2901void
2902xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002903 double ret = 0.0;
2904 double mult = 1;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002905 int ok = 0;
2906
2907 CHECK_ERROR;
2908 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
2909 ERROR(XPATH_NUMBER_ERROR);
2910 }
2911 while ((CUR >= '0') && (CUR <= '9')) {
2912 ret = ret * 10 + (CUR - '0');
2913 ok = 1;
2914 NEXT;
2915 }
2916 if (CUR == '.') {
2917 NEXT;
2918 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
2919 ERROR(XPATH_NUMBER_ERROR);
2920 }
2921 while ((CUR >= '0') && (CUR <= '9')) {
2922 mult /= 10;
2923 ret = ret + (CUR - '0') * mult;
2924 NEXT;
2925 }
2926 }
2927 valuePush(ctxt, xmlXPathNewFloat(ret));
2928}
2929
2930/**
2931 * xmlXPathEvalLiteral:
2932 * @ctxt: the XPath Parser context
2933 *
2934 * Parse a Literal and push it on the stack.
2935 *
2936 * [29] Literal ::= '"' [^"]* '"'
2937 * | "'" [^']* "'"
2938 *
2939 * TODO: memory allocation could be improved.
2940 */
2941void
2942xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
2943 const CHAR *q;
2944 CHAR *ret = NULL;
2945
2946 if (CUR == '"') {
2947 NEXT;
2948 q = CUR_PTR;
2949 while ((IS_CHAR(CUR)) && (CUR != '"'))
2950 NEXT;
2951 if (!IS_CHAR(CUR)) {
2952 ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
2953 } else {
2954 ret = xmlStrndup(q, CUR_PTR - q);
2955 NEXT;
2956 }
2957 } else if (CUR == '\'') {
2958 NEXT;
2959 q = CUR_PTR;
2960 while ((IS_CHAR(CUR)) && (CUR != '\''))
2961 NEXT;
2962 if (!IS_CHAR(CUR)) {
2963 ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
2964 } else {
2965 ret = xmlStrndup(q, CUR_PTR - q);
2966 NEXT;
2967 }
2968 } else {
2969 ERROR(XPATH_START_LITERAL_ERROR);
2970 }
2971 if (ret == NULL) return;
2972 valuePush(ctxt, xmlXPathNewString(ret));
2973 free(ret);
2974}
2975
2976/**
2977 * xmlXPathEvalVariableReference:
2978 * @ctxt: the XPath Parser context
2979 *
2980 * Parse a VariableReference, evaluate it and push it on the stack.
2981 *
2982 * The variable bindings consist of a mapping from variable names
2983 * to variable values. The value of a variable is an object, which
2984 * of any of the types that are possible for the value of an expression,
2985 * and may also be of additional types not specified here.
2986 *
2987 * Early evaluation is possible since:
2988 * The variable bindings [...] used to evaluate a subexpression are
2989 * always the same as those used to evaluate the containing expression.
2990 *
2991 * [36] VariableReference ::= '$' QName
2992 */
2993void
2994xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
2995 CHAR *name;
2996 CHAR *prefix;
2997 xmlXPathObjectPtr value;
2998
2999 if (CUR != '$') {
3000 ERROR(XPATH_VARIABLE_REF_ERROR);
3001 }
3002 name = xmlXPathParseQName(ctxt, &prefix);
3003 if (name == NULL) {
3004 ERROR(XPATH_VARIABLE_REF_ERROR);
3005 }
3006 value = xmlXPathVariablelookup(ctxt, prefix, name);
3007 if (value == NULL) {
3008 ERROR(XPATH_UNDEF_VARIABLE_ERROR);
3009 }
3010 valuePush(ctxt, value);
3011 if (prefix != NULL) free(prefix);
3012 free(name);
3013}
3014
3015
3016/**
3017 * xmlXPathFunctionLookup:
3018 * @ctxt: the XPath Parser context
3019 * @name: a name string
3020 *
3021 * Search for a function of the given name
3022 *
3023 * [35] FunctionName ::= QName - NodeType
3024 *
3025 * TODO: for the moment the list is hardcoded from the spec !!!!
3026 *
3027 * Returns the xmlXPathFunction if found, or NULL otherwise
3028 */
3029xmlXPathFunction
3030xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, CHAR *name) {
3031 switch (name[0]) {
3032 case 'b':
3033 if (!xmlStrcmp(name, "boolean"))
3034 return(xmlXPathBooleanFunction);
3035 break;
3036 case 'c':
3037 if (!xmlStrcmp(name, "ceiling"))
3038 return(xmlXPathCeilingFunction);
3039 if (!xmlStrcmp(name, "count"))
3040 return(xmlXPathCountFunction);
3041 if (!xmlStrcmp(name, "concat"))
3042 return(xmlXPathConcatFunction);
3043 if (!xmlStrcmp(name, "contains"))
3044 return(xmlXPathContainsFunction);
3045 break;
3046 case 'i':
3047 if (!xmlStrcmp(name, "id"))
3048 return(xmlXPathIdFunction);
3049 break;
3050 case 'f':
3051 if (!xmlStrcmp(name, "false"))
3052 return(xmlXPathFalseFunction);
3053 if (!xmlStrcmp(name, "floor"))
3054 return(xmlXPathFloorFunction);
3055 break;
3056 case 'l':
3057 if (!xmlStrcmp(name, "last"))
3058 return(xmlXPathLastFunction);
3059 if (!xmlStrcmp(name, "lang"))
3060 return(xmlXPathLangFunction);
3061 if (!xmlStrcmp(name, "local-part"))
3062 return(xmlXPathLocalPartFunction);
3063 break;
3064 case 'n':
3065 if (!xmlStrcmp(name, "not"))
3066 return(xmlXPathNotFunction);
3067 if (!xmlStrcmp(name, "name"))
3068 return(xmlXPathNameFunction);
3069 if (!xmlStrcmp(name, "namespace"))
3070 return(xmlXPathNamespaceFunction);
3071 if (!xmlStrcmp(name, "normalize"))
3072 return(xmlXPathNormalizeFunction);
3073 if (!xmlStrcmp(name, "number"))
3074 return(xmlXPathNumberFunction);
3075 break;
3076 case 'p':
3077 if (!xmlStrcmp(name, "position"))
3078 return(xmlXPathPositionFunction);
3079 break;
3080 case 'r':
3081 if (!xmlStrcmp(name, "round"))
3082 return(xmlXPathRoundFunction);
3083 break;
3084 case 's':
3085 if (!xmlStrcmp(name, "string"))
3086 return(xmlXPathStringFunction);
3087 if (!xmlStrcmp(name, "string-length"))
3088 return(xmlXPathStringLengthFunction);
3089 if (!xmlStrcmp(name, "starts-with"))
3090 return(xmlXPathStartsWithFunction);
3091 if (!xmlStrcmp(name, "substring"))
3092 return(xmlXPathSubstringFunction);
3093 if (!xmlStrcmp(name, "substring-before"))
3094 return(xmlXPathSubstringBeforeFunction);
3095 if (!xmlStrcmp(name, "substring-after"))
3096 return(xmlXPathSubstringAfterFunction);
3097 if (!xmlStrcmp(name, "sum"))
3098 return(xmlXPathSumFunction);
3099 break;
3100 case 't':
3101 if (!xmlStrcmp(name, "true"))
3102 return(xmlXPathTrueFunction);
3103 if (!xmlStrcmp(name, "translate"))
3104 return(xmlXPathTranslateFunction);
3105 break;
3106 }
3107 return(NULL);
3108}
3109
3110/**
3111 * xmlXPathEvalLocationPathName:
3112 * @ctxt: the XPath Parser context
3113 * @name: a name string
3114 *
3115 * Various names in the beginning of a LocationPath expression
3116 * indicate whether that's an Axis, a node type,
3117 *
3118 * [6] AxisName ::= 'ancestor'
3119 * | 'ancestor-or-self'
3120 * | 'attribute'
3121 * | 'child'
3122 * | 'descendant'
3123 * | 'descendant-or-self'
3124 * | 'following'
3125 * | 'following-sibling'
3126 * | 'namespace'
3127 * | 'parent'
3128 * | 'preceding'
3129 * | 'preceding-sibling'
3130 * | 'self'
3131 * [38] NodeType ::= 'comment'
3132 * | 'text'
3133 * | 'processing-instruction'
3134 * | 'node'
3135 */
3136int
3137xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, CHAR *name) {
3138 switch (name[0]) {
3139 case 'a':
3140 if (!xmlStrcmp(name, "ancestor")) return(AXIS_ANCESTOR);
3141 if (!xmlStrcmp(name, "ancestor-or-self")) return(AXIS_ANCESTOR_OR_SELF);
3142 if (!xmlStrcmp(name, "attribute")) return(AXIS_ATTRIBUTE);
3143 break;
3144 case 'c':
3145 if (!xmlStrcmp(name, "child")) return(AXIS_CHILD);
3146 if (!xmlStrcmp(name, "comment")) return(NODE_TYPE_COMMENT);
3147 break;
3148 case 'd':
3149 if (!xmlStrcmp(name, "descendant")) return(AXIS_DESCENDANT);
3150 if (!xmlStrcmp(name, "descendant-or-self")) return(AXIS_DESCENDANT_OR_SELF);
3151 break;
3152 case 'f':
3153 if (!xmlStrcmp(name, "following")) return(AXIS_FOLLOWING);
3154 if (!xmlStrcmp(name, "following-sibling")) return(AXIS_FOLLOWING_SIBLING);
3155 break;
3156 case 'n':
3157 if (!xmlStrcmp(name, "namespace")) return(AXIS_NAMESPACE);
3158 if (!xmlStrcmp(name, "node")) return(NODE_TYPE_NODE);
3159 break;
3160 case 'p':
3161 if (!xmlStrcmp(name, "parent")) return(AXIS_PARENT);
3162 if (!xmlStrcmp(name, "preceding")) return(AXIS_PRECEDING);
3163 if (!xmlStrcmp(name, "preceding-sibling")) return(AXIS_PRECEDING_SIBLING);
3164 if (!xmlStrcmp(name, "processing-instruction")) return(NODE_TYPE_PI);
3165 break;
3166 case 's':
3167 if (!xmlStrcmp(name, "self")) return(AXIS_SELF);
3168 break;
3169 case 't':
3170 if (!xmlStrcmp(name, "text")) return(NODE_TYPE_TEXT);
3171 break;
3172 }
3173 if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION);
3174 return(0);
3175}
3176
3177/**
3178 * xmlXPathEvalFunctionCall:
3179 * @ctxt: the XPath Parser context
3180 *
3181 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
3182 * [17] Argument ::= Expr
3183 *
3184 * Parse and evaluate a function call, the evaluation of all arguments are
3185 * pushed on the stack
3186 */
3187void
3188xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
3189 CHAR *name;
3190 CHAR *prefix;
3191 xmlXPathFunction func;
3192 int nbargs = 0;
3193
3194 name = xmlXPathParseQName(ctxt, &prefix);
3195 if (name == NULL) {
3196 ERROR(XPATH_EXPR_ERROR);
3197 }
3198 func = xmlXPathIsFunction(ctxt, name);
3199 if (func == NULL) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003200 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003201 ERROR(XPATH_UNKNOWN_FUNC_ERROR);
3202 }
3203#ifdef DEBUG_EXPR
3204 fprintf(xmlXPathDebug, "Calling function %s\n", name);
3205#endif
3206
3207 if (CUR != '(') {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003208 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003209 ERROR(XPATH_EXPR_ERROR);
3210 }
3211 NEXT;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003212
3213 while (CUR != ')') {
3214 xmlXPathEvalExpr(ctxt);
3215 nbargs++;
3216 if (CUR == ')') break;
3217 if (CUR != ',') {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003218 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003219 ERROR(XPATH_EXPR_ERROR);
3220 }
3221 NEXT;
3222 }
3223 NEXT;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003224 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003225 func(ctxt, nbargs);
3226}
3227
3228/**
3229 * xmlXPathEvalPrimaryExpr:
3230 * @ctxt: the XPath Parser context
3231 *
3232 * [15] PrimaryExpr ::= VariableReference
3233 * | '(' Expr ')'
3234 * | Literal
3235 * | Number
3236 * | FunctionCall
3237 *
3238 * Parse and evaluate a primary expression, then push the result on the stack
3239 */
3240void
3241xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
3242 if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
3243 else if (CUR == '(') {
3244 NEXT;
3245 xmlXPathEvalExpr(ctxt);
3246 if (CUR != ')') {
3247 ERROR(XPATH_EXPR_ERROR);
3248 }
3249 NEXT;
3250 } else if (IS_DIGIT(CUR)) {
3251 xmlXPathEvalNumber(ctxt);
3252 } else if ((CUR == '\'') || (CUR == '"')) {
3253 xmlXPathEvalLiteral(ctxt);
3254 } else {
3255 xmlXPathEvalFunctionCall(ctxt);
3256 }
3257}
3258
3259/**
3260 * xmlXPathEvalFilterExpr:
3261 * @ctxt: the XPath Parser context
3262 *
3263 * [20] FilterExpr ::= PrimaryExpr
3264 * | FilterExpr Predicate
3265 *
3266 * Parse and evaluate a filter expression, then push the result on the stack
3267 * Square brackets are used to filter expressions in the same way that
3268 * they are used in location paths. It is an error if the expression to
3269 * be filtered does not evaluate to a node-set. The context node list
3270 * used for evaluating the expression in square brackets is the node-set
3271 * to be filtered listed in document order.
3272 */
3273
3274void
3275xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
3276 /****
3277 xmlNodeSetPtr oldset = NULL;
3278 xmlXPathObjectPtr arg;
3279 ****/
3280
3281 xmlXPathEvalPrimaryExpr(ctxt);
3282 CHECK_ERROR;
3283
3284 if (CUR != '[') return;
3285
3286 CHECK_TYPE(XPATH_NODESET);
3287
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003288 while (CUR == '[') {
3289 xmlXPathEvalPredicate(ctxt);
3290 }
3291
3292
3293}
3294
3295/**
3296 * xmlXPathEvalPathExpr:
3297 * @ctxt: the XPath Parser context
3298 *
3299 * [19] PathExpr ::= LocationPath
3300 * | FilterExpr
3301 * | FilterExpr '/' RelativeLocationPath
3302 * | FilterExpr '//' RelativeLocationPath
3303 *
3304 * Parse and evaluate a path expression, then push the result on the stack
3305 * The / operator and // operators combine an arbitrary expression
3306 * and a relative location path. It is an error if the expression
3307 * does not evaluate to a node-set.
3308 * The / operator does composition in the same way as when / is
3309 * used in a location path. As in location paths, // is short for
3310 * /descendant-or-self::node()/.
3311 */
3312
3313void
3314xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
3315 xmlNodeSetPtr newset = NULL;
3316
3317 /*
3318 * TODO: FilterExpr => PrimaryExpr => FunctionName is possible too
3319 * so we may have to parse a name here, and depending on whether
3320 * it's a function name or not parse a FilterExpr or a LocationPath
3321 * !!!!!!!!!!!! See NOTE1
3322 */
3323
3324 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
3325 (CUR == '\'') || (CUR == '"')) {
3326 xmlXPathEvalFilterExpr(ctxt);
3327 CHECK_ERROR;
3328 if ((CUR == '/') && (NXT(1) == '/')) {
3329 SKIP(2);
3330 if (ctxt->context->nodelist == NULL) {
3331 STRANGE
3332 xmlXPathRoot(ctxt);
3333 }
3334 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
3335 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3336 if (ctxt->context->nodelist != NULL)
3337 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3338 ctxt->context->nodelist = newset;
3339 ctxt->context->node = NULL;
3340 xmlXPathEvalRelativeLocationPath(ctxt);
3341 } else if (CUR == '/') {
3342 xmlXPathEvalRelativeLocationPath(ctxt);
3343 }
3344 } else {
3345 xmlXPathEvalLocationPath(ctxt);
3346 }
3347}
3348
3349/**
3350 * xmlXPathEvalUnionExpr:
3351 * @ctxt: the XPath Parser context
3352 *
3353 * [18] UnionExpr ::= PathExpr
3354 * | UnionExpr '|' PathExpr
3355 *
3356 * Parse and evaluate an union expression, then push the result on the stack
3357 */
3358
3359void
3360xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
3361 xmlXPathEvalPathExpr(ctxt);
3362 CHECK_ERROR;
3363 if (CUR == '|') {
3364 xmlNodeSetPtr old = ctxt->context->nodelist;
3365
3366 xmlXPathEvalPathExpr(ctxt);
3367
3368 if (ctxt->context->nodelist == NULL)
3369 ctxt->context->nodelist = old;
3370 else {
3371 ctxt->context->nodelist =
3372 xmlXPathNodeSetMerge(ctxt->context->nodelist, old);
3373 xmlXPathFreeNodeSet(old);
3374 }
3375 }
3376}
3377
3378/**
3379 * xmlXPathEvalUnaryExpr:
3380 * @ctxt: the XPath Parser context
3381 *
3382 * [27] UnaryExpr ::= UnionExpr
3383 * | '-' UnaryExpr
3384 *
3385 * Parse and evaluate an unary expression, then push the result on the stack
3386 */
3387
3388void
3389xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
3390 int minus = 0;
3391
3392 if (CUR == '-') {
3393 minus = 1;
3394 NEXT;
3395 }
3396 /* xmlXPathEvalUnionExpr(ctxt); NOTE1 !!! */
3397 xmlXPathEvalPrimaryExpr(ctxt);
3398 CHECK_ERROR;
3399 if (minus) {
3400 xmlXPathValueFlipSign(ctxt);
3401 }
3402}
3403
3404/**
3405 * xmlXPathEvalMultiplicativeExpr:
3406 * @ctxt: the XPath Parser context
3407 *
3408 * [26] MultiplicativeExpr ::= UnaryExpr
3409 * | MultiplicativeExpr MultiplyOperator UnaryExpr
3410 * | MultiplicativeExpr 'div' UnaryExpr
3411 * | MultiplicativeExpr 'mod' UnaryExpr
3412 * [34] MultiplyOperator ::= '*'
3413 *
3414 * Parse and evaluate an Additive expression, then push the result on the stack
3415 */
3416
3417void
3418xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
3419 xmlXPathEvalUnaryExpr(ctxt);
3420 CHECK_ERROR;
3421 while ((CUR == '*') ||
3422 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
3423 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
3424 int op = -1;
3425
3426 if (CUR == '*') {
3427 op = 0;
3428 NEXT;
3429 } else if (CUR == 'd') {
3430 op = 1;
3431 SKIP(3);
3432 } else if (CUR == 'm') {
3433 op = 2;
3434 SKIP(3);
3435 }
3436 xmlXPathEvalUnaryExpr(ctxt);
3437 CHECK_ERROR;
3438 switch (op) {
3439 case 0:
3440 xmlXPathMultValues(ctxt);
3441 break;
3442 case 1:
3443 xmlXPathDivValues(ctxt);
3444 break;
3445 case 2:
3446 xmlXPathModValues(ctxt);
3447 break;
3448 }
3449 }
3450}
3451
3452/**
3453 * xmlXPathEvalAdditiveExpr:
3454 * @ctxt: the XPath Parser context
3455 *
3456 * [25] AdditiveExpr ::= MultiplicativeExpr
3457 * | AdditiveExpr '+' MultiplicativeExpr
3458 * | AdditiveExpr '-' MultiplicativeExpr
3459 *
3460 * Parse and evaluate an Additive expression, then push the result on the stack
3461 */
3462
3463void
3464xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
3465 xmlXPathEvalMultiplicativeExpr(ctxt);
3466 CHECK_ERROR;
3467 while ((CUR == '+') || (CUR == '-')) {
3468 int plus;
3469
3470 if (CUR == '+') plus = 1;
3471 else plus = 0;
3472 NEXT;
3473 xmlXPathEvalMultiplicativeExpr(ctxt);
3474 CHECK_ERROR;
3475 if (plus) xmlXPathAddValues(ctxt);
3476 else xmlXPathSubValues(ctxt);
3477 }
3478}
3479
3480/**
3481 * xmlXPathEvalRelationalExpr:
3482 * @ctxt: the XPath Parser context
3483 *
3484 * [24] RelationalExpr ::= AdditiveExpr
3485 * | RelationalExpr '<' AdditiveExpr
3486 * | RelationalExpr '>' AdditiveExpr
3487 * | RelationalExpr '<=' AdditiveExpr
3488 * | RelationalExpr '>=' AdditiveExpr
3489 *
3490 * A <= B > C is allowed ? Answer from James, yes with
3491 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
3492 * which is basically what got implemented.
3493 *
3494 * Parse and evaluate a Relational expression, then push the result
3495 * on the stack
3496 */
3497
3498void
3499xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
3500 xmlXPathEvalAdditiveExpr(ctxt);
3501 CHECK_ERROR;
3502 while ((CUR == '<') ||
3503 (CUR == '>') ||
3504 ((CUR == '<') && (NXT(1) == '=')) ||
3505 ((CUR == '>') && (NXT(1) == '='))) {
Daniel Veillard991e63d1999-08-15 23:32:28 +00003506 int inf, strict, ret;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003507
3508 if (CUR == '<') inf = 1;
3509 else inf = 0;
3510 if (NXT(1) == '=') strict = 0;
3511 else strict = 1;
3512 NEXT;
3513 if (!strict) NEXT;
3514 xmlXPathEvalAdditiveExpr(ctxt);
3515 CHECK_ERROR;
Daniel Veillard991e63d1999-08-15 23:32:28 +00003516 ret = xmlXPathCompareValues(ctxt, inf, strict);
3517 valuePush(ctxt, xmlXPathNewBoolean(ret));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003518 }
3519}
3520
3521/**
3522 * xmlXPathEvalEqualityExpr:
3523 * @ctxt: the XPath Parser context
3524 *
3525 * [23] EqualityExpr ::= RelationalExpr
3526 * | EqualityExpr '=' RelationalExpr
3527 * | EqualityExpr '!=' RelationalExpr
3528 *
3529 * A != B != C is allowed ? Answer from James, yes with
3530 * (RelationalExpr = RelationalExpr) = RelationalExpr
3531 * (RelationalExpr != RelationalExpr) != RelationalExpr
3532 * which is basically what got implemented.
3533 *
3534 * Parse and evaluate an Equality expression, then push the result on the stack
3535 *
3536 */
3537void
3538xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
3539 xmlXPathEvalRelationalExpr(ctxt);
3540 CHECK_ERROR;
3541 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
Daniel Veillard991e63d1999-08-15 23:32:28 +00003542 xmlXPathObjectPtr res;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003543 int eq, equal;
3544
3545 if (CUR == '=') eq = 1;
3546 else eq = 0;
3547 NEXT;
3548 if (!eq) NEXT;
3549 xmlXPathEvalRelationalExpr(ctxt);
3550 CHECK_ERROR;
Daniel Veillard991e63d1999-08-15 23:32:28 +00003551 equal = xmlXPathEqualValues(ctxt);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003552 if (eq) res = xmlXPathNewBoolean(equal);
3553 else res = xmlXPathNewBoolean(!equal);
3554 valuePush(ctxt, res);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003555 }
3556}
3557
3558/**
3559 * xmlXPathEvalAndExpr:
3560 * @ctxt: the XPath Parser context
3561 *
3562 * [22] AndExpr ::= EqualityExpr
3563 * | AndExpr 'and' EqualityExpr
3564 *
3565 * Parse and evaluate an AND expression, then push the result on the stack
3566 *
3567 */
3568void
3569xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
3570 xmlXPathEvalEqualityExpr(ctxt);
3571 CHECK_ERROR;
3572 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) {
3573 xmlXPathObjectPtr arg1, arg2;
3574
3575 SKIP(3);
3576 xmlXPathEvalEqualityExpr(ctxt);
3577 CHECK_ERROR;
3578 arg2 = valuePop(ctxt);
3579 arg1 = valuePop(ctxt);
3580 arg1->boolval &= arg2->boolval;
3581 valuePush(ctxt, arg1);
3582 xmlXPathFreeObject(arg2);
3583 }
3584}
3585
3586/**
3587 * xmlXPathEvalExpr:
3588 * @ctxt: the XPath Parser context
3589 *
3590 * [14] Expr ::= OrExpr
3591 * [21] OrExpr ::= AndExpr
3592 * | OrExpr 'or' AndExpr
3593 *
3594 * Parse and evaluate an expression, then push the result on the stack
3595 *
3596 */
3597void
3598xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
3599 xmlXPathEvalAndExpr(ctxt);
3600 CHECK_ERROR;
3601 while ((CUR == 'o') && (NXT(1) == 'r')) {
3602 xmlXPathObjectPtr arg1, arg2;
3603
3604 SKIP(2);
3605 xmlXPathEvalAndExpr(ctxt);
3606 CHECK_ERROR;
3607 arg2 = valuePop(ctxt);
3608 arg1 = valuePop(ctxt);
3609 arg1->boolval |= arg2->boolval;
3610 valuePush(ctxt, arg1);
3611 xmlXPathFreeObject(arg2);
3612 }
3613}
3614
3615/**
3616 * xmlXPathEvaluatePredicateResult:
3617 * @ctxt: the XPath Parser context
3618 * @res: the Predicate Expression evaluation result
3619 * @index: index of the current node in the current list
3620 *
3621 * Evaluate a predicate result for the current node.
3622 * A PredicateExpr is evaluated by evaluating the Expr and converting
3623 * the result to a boolean. If the result is a number, the result will
3624 * be converted to true if the number is equal to the position of the
3625 * context node in the context node list (as returned by the position
3626 * function) and will be converted to false otherwise; if the result
3627 * is not a number, then the result will be converted as if by a call
3628 * to the boolean function.
3629 */
3630int
3631xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
3632 xmlXPathObjectPtr res, int index) {
3633 if (res == NULL) return(0);
3634 switch (res->type) {
3635 case XPATH_BOOLEAN:
3636 return(res->boolval);
3637 case XPATH_NUMBER:
3638 return(res->floatval == index);
3639 case XPATH_NODESET:
3640 return(res->nodesetval->nodeNr != 0);
3641 case XPATH_STRING:
3642 return((res->stringval != NULL) &&
3643 (xmlStrlen(res->stringval) != 0));
3644 default:
3645 STRANGE
3646 }
3647 return(0);
3648}
3649
3650/**
3651 * xmlXPathEvalPredicate:
3652 * @ctxt: the XPath Parser context
3653 *
3654 * [8] Predicate ::= '[' PredicateExpr ']'
3655 * [9] PredicateExpr ::= Expr
3656 *
3657 * Parse and evaluate a predicate for all the elements of the
3658 * current node list. Then refine the list by removing all
3659 * nodes where the predicate is false.
3660 */
3661void
3662xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
3663 const CHAR *cur;
3664 xmlXPathObjectPtr res;
3665 xmlNodeSetPtr newset = NULL;
3666 int i;
3667
3668 if (CUR != '[') {
3669 ERROR(XPATH_INVALID_PREDICATE_ERROR);
3670 }
3671 NEXT;
3672 if ((ctxt->context->nodelist == NULL) ||
3673 (ctxt->context->nodelist->nodeNr == 0)) {
3674 ctxt->context->node = NULL;
3675 xmlXPathEvalExpr(ctxt);
3676 CHECK_ERROR;
3677 res = valuePop(ctxt);
3678 if (res != NULL)
3679 xmlXPathFreeObject(res);
3680 } else {
3681 cur = ctxt->cur;
3682 newset = xmlXPathNodeSetCreate(NULL);
3683 for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) {
3684 ctxt->cur = cur;
3685 ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
3686 xmlXPathEvalExpr(ctxt);
3687 CHECK_ERROR;
3688 res = valuePop(ctxt);
3689 if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1))
3690 xmlXPathNodeSetAdd(newset,
3691 ctxt->context->nodelist->nodeTab[i]);
3692 if (res != NULL)
3693 xmlXPathFreeObject(res);
3694 }
3695 if (ctxt->context->nodelist != NULL)
3696 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3697 ctxt->context->nodelist = newset;
3698 ctxt->context->node = NULL;
3699 }
3700 if (CUR != ']') {
3701 ERROR(XPATH_INVALID_PREDICATE_ERROR);
3702 }
3703 NEXT;
3704#ifdef DEBUG_STEP
3705 fprintf(xmlXPathDebug, "After predicate : ");
3706 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
3707#endif
3708}
3709
3710/**
3711 * xmlXPathEvalBasis:
3712 * @ctxt: the XPath Parser context
3713 *
3714 * [5] Basis ::= AxisName '::' NodeTest
3715 * | AbbreviatedBasis
3716 * [13] AbbreviatedBasis ::= NodeTest
3717 * | '@' NodeTest
3718 * [7] NodeTest ::= WildcardName
3719 * | NodeType '(' ')'
3720 * | 'processing-instruction' '(' Literal ')'
3721 * [37] WildcardName ::= '*'
3722 * | NCName ':' '*'
3723 * | QName
3724 *
3725 * Evaluate one step in a Location Path
3726 */
3727void
3728xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) {
3729 CHAR *name = NULL;
3730 CHAR *prefix = NULL;
3731 int type = 0;
3732 int axis = AXIS_CHILD; /* the default on abbreviated syntax */
3733 int nodetest = NODE_TEST_NONE;
3734 int nodetype = 0;
3735 xmlNodeSetPtr newset = NULL;
3736
3737 if (CUR == '@') {
3738 TODO /* attributes */
3739 } else if (CUR == '*') {
3740 NEXT;
3741 nodetest = NODE_TEST_ALL;
3742 } else {
3743 name = xmlXPathParseNCName(ctxt);
3744 if (name == NULL) {
3745 ERROR(XPATH_EXPR_ERROR);
3746 }
3747 type = xmlXPathGetNameType(ctxt, name);
3748 switch (type) {
3749 /*
3750 * Simple case: no axis seach all given node types.
3751 */
3752 case NODE_TYPE_COMMENT:
3753 if ((CUR != '(') || (NXT(1) != ')')) break;
3754 SKIP(2);
3755 nodetest = NODE_TEST_TYPE;
3756 nodetype = XML_COMMENT_NODE;
3757 goto search_nodes;
3758 case NODE_TYPE_TEXT:
3759 if ((CUR != '(') || (NXT(1) != ')')) break;
3760 SKIP(2);
3761 nodetest = NODE_TEST_TYPE;
3762 nodetype = XML_TEXT_NODE;
3763 goto search_nodes;
3764 case NODE_TYPE_NODE:
3765 if ((CUR != '(') || (NXT(1) != ')')) {
3766 nodetest = NODE_TEST_NAME;
3767 break;
3768 }
3769 SKIP(2);
3770 nodetest = NODE_TEST_TYPE;
3771 nodetype = XML_ELEMENT_NODE;
3772 goto search_nodes;
3773 case NODE_TYPE_PI:
3774 if (CUR != '(') break;
3775 if (NXT(1) != ')') {
3776 /*
3777 * Specific case: search a PI by name.
3778 */
3779 SKIP(2);
3780 nodetest = NODE_TEST_PI;
3781 xmlXPathEvalLiteral(ctxt);
3782 CHECK_ERROR;
3783 if (CUR != ')')
3784 ERROR(XPATH_UNCLOSED_ERROR);
3785 xmlXPathSearchPI(ctxt, 0);
3786 return;
3787 }
3788 SKIP(2);
3789 nodetest = NODE_TEST_TYPE;
3790 nodetype = XML_PI_NODE;
3791 goto search_nodes;
3792
3793 /*
3794 * Handling of the compund form: got the axis.
3795 */
3796 case AXIS_ANCESTOR:
3797 case AXIS_ANCESTOR_OR_SELF:
3798 case AXIS_ATTRIBUTE:
3799 case AXIS_CHILD:
3800 case AXIS_DESCENDANT:
3801 case AXIS_DESCENDANT_OR_SELF:
3802 case AXIS_FOLLOWING:
3803 case AXIS_FOLLOWING_SIBLING:
3804 case AXIS_NAMESPACE:
3805 case AXIS_PARENT:
3806 case AXIS_PRECEDING:
3807 case AXIS_PRECEDING_SIBLING:
3808 case AXIS_SELF:
3809 if ((CUR != ':') || (NXT(1) != ':')) {
3810 nodetest = NODE_TEST_NAME;
3811 break;
3812 }
3813 SKIP(2);
3814 axis = type;
3815 break;
3816
3817 /*
3818 * Default: abbreviated syntax the axis is AXIS_CHILD
3819 */
3820 default:
3821 nodetest = NODE_TEST_NAME;
3822 }
3823 if (nodetest == NODE_TEST_NONE) {
3824 if (CUR == '*') {
3825 NEXT;
3826 nodetest = NODE_TEST_ALL;
3827 } else {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003828 if (name != NULL)
3829 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003830 name = xmlXPathParseQName(ctxt, &prefix);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003831 if (name == NULL) {
3832 ERROR(XPATH_EXPR_ERROR);
3833 }
3834 type = xmlXPathGetNameType(ctxt, name);
3835 switch (type) {
3836 /*
3837 * Simple case: no axis seach all given node types.
3838 */
3839 case NODE_TYPE_COMMENT:
3840 if ((CUR != '(') || (NXT(1) != ')')) break;
3841 SKIP(2);
3842 nodetest = NODE_TEST_TYPE;
3843 nodetype = XML_COMMENT_NODE;
3844 goto search_nodes;
3845 case NODE_TYPE_TEXT:
3846 if ((CUR != '(') || (NXT(1) != ')')) break;
3847 SKIP(2);
3848 nodetest = NODE_TEST_TYPE;
3849 nodetype = XML_TEXT_NODE;
3850 goto search_nodes;
3851 case NODE_TYPE_NODE:
3852 if ((CUR != '(') || (NXT(1) != ')')) {
3853 nodetest = NODE_TEST_NAME;
3854 break;
3855 }
3856 SKIP(2);
3857 nodetest = NODE_TEST_TYPE;
3858 nodetype = XML_ELEMENT_NODE;
3859 goto search_nodes;
3860 case NODE_TYPE_PI:
3861 if (CUR != '(') break;
3862 if (NXT(1) != ')') {
3863 /*
3864 * Specific case: search a PI by name.
3865 */
3866 SKIP(2);
3867 nodetest = NODE_TEST_PI;
3868 xmlXPathEvalLiteral(ctxt);
3869 CHECK_ERROR;
3870 if (CUR != ')')
3871 ERROR(XPATH_UNCLOSED_ERROR);
3872 xmlXPathSearchPI(ctxt, 0);
3873 return;
3874 }
3875 SKIP(2);
3876 nodetest = NODE_TEST_TYPE;
3877 nodetype = XML_PI_NODE;
3878 goto search_nodes;
3879 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003880 nodetest = NODE_TEST_NAME;
3881 }
3882 } else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) {
3883 NEXT;
3884 prefix = name;
3885 if (CUR == '*') {
3886 NEXT;
3887 nodetest = NODE_TEST_ALL;
3888 } else
3889 name = xmlXPathParseNCName(ctxt);
3890 } else if (name == NULL)
3891 ERROR(XPATH_EXPR_ERROR);
3892 }
3893
3894search_nodes:
3895
3896#ifdef DEBUG_STEP
3897 fprintf(xmlXPathDebug, "Basis : computing new set\n");
3898#endif
3899 newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype,
3900 prefix, name);
3901 if (ctxt->context->nodelist != NULL)
3902 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3903 ctxt->context->nodelist = newset;
3904 ctxt->context->node = NULL;
3905#ifdef DEBUG_STEP
3906 fprintf(xmlXPathDebug, "Basis : ");
3907 xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist);
3908#endif
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003909 if (name != NULL) free(name);
3910 if (prefix != NULL) free(prefix);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003911}
3912
3913/**
3914 * xmlXPathEvalStep:
3915 * @ctxt: the XPath Parser context
3916 *
3917 * [4] Step ::= Basis Predicate*
3918 * | AbbreviatedStep
3919 * [12] AbbreviatedStep ::= '.'
3920 * | '..'
3921 *
3922 * Evaluate one step in a Location Path
3923 * A location step of . is short for self::node(). This is
3924 * particularly useful in conjunction with //. For example, the
3925 * location path .//para is short for
3926 * self::node()/descendant-or-self::node()/child::para
3927 * and so will select all para descendant elements of the context
3928 * node.
3929 * Similarly, a location step of .. is short for parent::node().
3930 * For example, ../title is short for parent::node()/child::title
3931 * and so will select the title children of the parent of the context
3932 * node.
3933 */
3934void
3935xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
3936 xmlNodeSetPtr newset = NULL;
3937
3938 if ((CUR == '.') && (NXT(1) == '.')) {
3939 SKIP(2);
3940 if (ctxt->context->nodelist == NULL) {
3941 STRANGE
3942 xmlXPathRoot(ctxt);
3943 }
3944 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
3945 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3946 if (ctxt->context->nodelist != NULL)
3947 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3948 ctxt->context->nodelist = newset;
3949 ctxt->context->node = NULL;
3950 } else if (CUR == '.') {
3951 NEXT;
3952 } else {
3953 xmlXPathEvalBasis(ctxt);
3954 while (CUR == '[') {
3955 xmlXPathEvalPredicate(ctxt);
3956 }
3957 }
3958#ifdef DEBUG_STEP
3959 fprintf(xmlXPathDebug, "Step : ");
3960 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
3961#endif
3962}
3963
3964/**
3965 * xmlXPathEvalRelativeLocationPath:
3966 * @ctxt: the XPath Parser context
3967 *
3968 * [3] RelativeLocationPath ::= Step
3969 * | RelativeLocationPath '/' Step
3970 * | AbbreviatedRelativeLocationPath
3971 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
3972 *
3973 */
3974void
3975xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) {
3976 xmlNodeSetPtr newset = NULL;
3977
3978 xmlXPathEvalStep(ctxt);
3979 while (CUR == '/') {
3980 if ((CUR == '/') && (NXT(1) == '/')) {
3981 SKIP(2);
3982 if (ctxt->context->nodelist == NULL) {
3983 STRANGE
3984 xmlXPathRoot(ctxt);
3985 }
3986 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
3987 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3988 if (ctxt->context->nodelist != NULL)
3989 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3990 ctxt->context->nodelist = newset;
3991 ctxt->context->node = NULL;
3992 xmlXPathEvalStep(ctxt);
3993 } else if (CUR == '/') {
3994 NEXT;
3995 xmlXPathEvalStep(ctxt);
3996 }
3997 }
3998}
3999
4000/**
4001 * xmlXPathEvalLocationPath:
4002 * @ctxt: the XPath Parser context
4003 *
4004 * [1] LocationPath ::= RelativeLocationPath
4005 * | AbsoluteLocationPath
4006 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
4007 * | AbbreviatedAbsoluteLocationPath
4008 * [10] AbbreviatedAbsoluteLocationPath ::=
4009 * '//' RelativeLocationPath
4010 *
4011 * // is short for /descendant-or-self::node()/. For example,
4012 * //para is short for /descendant-or-self::node()/child::para and
4013 * so will select any para element in the document (even a para element
4014 * that is a document element will be selected by //para since the
4015 * document element node is a child of the root node); div//para is
4016 * short for div/descendant-or-self::node()/child::para and so will
4017 * select all para descendants of div children.
4018 */
4019void
4020xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
4021 xmlNodeSetPtr newset = NULL;
4022
4023 while (CUR == '/') {
4024 if ((CUR == '/') && (NXT(1) == '/')) {
4025 SKIP(2);
4026 if (ctxt->context->nodelist == NULL)
4027 xmlXPathRoot(ctxt);
4028 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
4029 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
4030 if (ctxt->context->nodelist != NULL)
4031 xmlXPathFreeNodeSet(ctxt->context->nodelist);
4032 ctxt->context->nodelist = newset;
4033 ctxt->context->node = NULL;
4034 xmlXPathEvalRelativeLocationPath(ctxt);
4035 } else if (CUR == '/') {
4036 NEXT;
4037 xmlXPathRoot(ctxt);
Daniel Veillardb05deb71999-08-10 19:04:08 +00004038 if (CUR != 0)
4039 xmlXPathEvalRelativeLocationPath(ctxt);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004040 } else {
4041 xmlXPathEvalRelativeLocationPath(ctxt);
4042 }
4043 }
4044}
4045
4046/*
4047 * TODO * extra spaces *
4048 * more tokenization rules ... Not used currently, especially allowing
4049 * spaces before and after ExprToken !!!!!!!!!!!!!
4050 *
4051 * [32] Operator ::= OperatorName
4052 * | MultiplyOperator
4053 * | '/' | '//' | '|' | '+' | '-' | '=' | '!='
4054 * | '<'| '<=' | '>' | '>='
4055 * [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
4056 * [39] ExprWhitespace ::= S
4057 *
4058 * BUG: ExprToken is never referenced.
4059 *
4060 * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
4061 * | WildcardName
4062 * | NodeType
4063 * | Operator
4064 * | FunctionName
4065 * | AxisName
4066 * | Literal
4067 * | Number
4068 * | VariableReference
4069 */
4070
4071/**
4072 * xmlXPathEval:
4073 * @str: the XPath expression
4074 * @ctxt: the XPath context
4075 *
4076 * Evaluate the XPath Location Path in the given context.
4077 *
4078 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
4079 * the caller has to free the object.
4080 */
4081xmlXPathObjectPtr
4082xmlXPathEval(const CHAR *str, xmlXPathContextPtr ctxt) {
4083 xmlXPathParserContextPtr pctxt;
4084 xmlXPathObjectPtr res;
4085
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004086 xmlXPathInit();
4087
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004088 CHECK_CONTEXT
4089
4090 if (xmlXPathDebug == NULL)
4091 xmlXPathDebug = stderr;
4092 pctxt = xmlXPathNewParserContext(str, ctxt);
4093 xmlXPathEvalLocationPath(pctxt);
4094
4095 do {
4096 res = valuePop(pctxt);
4097#ifdef DEBUG
4098#endif
4099 } while (res != NULL);
4100 res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
4101 xmlXPathFreeParserContext(pctxt);
4102 return(res);
4103}
4104
4105/**
4106 * xmlXPathEvalExpression:
4107 * @str: the XPath expression
4108 * @ctxt: the XPath context
4109 *
4110 * Evaluate the XPath expression in the given context.
4111 *
4112 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
4113 * the caller has to free the object.
4114 */
4115xmlXPathObjectPtr
4116xmlXPathEvalExpression(const CHAR *str, xmlXPathContextPtr ctxt) {
4117 xmlXPathParserContextPtr pctxt;
4118 xmlXPathObjectPtr res, tmp;
4119
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004120 xmlXPathInit();
4121
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004122 CHECK_CONTEXT
4123
4124 if (xmlXPathDebug == NULL)
4125 xmlXPathDebug = stderr;
4126 pctxt = xmlXPathNewParserContext(str, ctxt);
4127 xmlXPathEvalExpr(pctxt);
4128
4129 res = valuePop(pctxt);
4130 do {
4131 tmp = valuePop(pctxt);
Daniel Veillarde2d034d1999-07-27 19:52:06 +00004132 if (tmp != NULL);
4133 xmlXPathFreeObject(tmp);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00004134 } while (tmp != NULL);
4135 xmlXPathFreeParserContext(pctxt);
4136 return(res);
4137}
4138