blob: 33c13f04b114ec8e902f7f9ef435a760f09a26fc [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
16#include <malloc.h>
17#include <math.h>
18#include <stdio.h>
19#include "tree.h"
20#include "xpath.h"
21#include "parserInternals.h"
22
23/*
24 * TODO: create a pseudo document element and affect it as the
25 * actual root.
26 */
27/*
Daniel Veillarde2d034d1999-07-27 19:52:06 +000028 * Setup stuff for floating point
Daniel Veillard1566d3a1999-07-15 14:24:29 +000029 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +000030double xmlXPathNAN = 0;
31double xmlXPathPINF = 1;
32double xmlXPathMINF = -1;
33
34/**
35 * xmlXPathInit:
36 *
37 * Initialize the XPath environment
38 */
39void
40xmlXPathInit(void) {
41 static int initialized = 0;
42
43 if (initialized) return;
44
45 xmlXPathNAN = 0;
46 xmlXPathNAN /= 0;
47
48 xmlXPathPINF = 1;
49 xmlXPathPINF /= 0;
50
51 xmlXPathMINF = -1;
52 xmlXPathMINF /= 0;
53
54 initialized = 1;
55}
Daniel Veillard1566d3a1999-07-15 14:24:29 +000056
57/* #define DEBUG */
58/* #define DEBUG_STEP */
59/* #define DEBUG_EXPR */
60
61FILE *xmlXPathDebug = NULL;
62
63#define TODO \
64 fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \
65 __FILE__, __LINE__);
66
67#define STRANGE \
68 fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \
69 __FILE__, __LINE__);
70
Daniel Veillarde2d034d1999-07-27 19:52:06 +000071double xmlXPathStringEvalNumber(const CHAR *str);
Daniel Veillard1566d3a1999-07-15 14:24:29 +000072
73/************************************************************************
74 * *
75 * Parser stacks related functions and macros *
76 * *
77 ************************************************************************/
78
79/*
80 * Generic function for accessing stacks in the Parser Context
81 */
82
83#define PUSH_AND_POP(type, name) \
84extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
85 if (ctxt->name##Nr >= ctxt->name##Max) { \
86 ctxt->name##Max *= 2; \
87 ctxt->name##Tab = (void *) realloc(ctxt->name##Tab, \
88 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
89 if (ctxt->name##Tab == NULL) { \
90 fprintf(xmlXPathDebug, "realloc failed !\n"); \
91 exit(1); \
92 } \
93 } \
94 ctxt->name##Tab[ctxt->name##Nr] = value; \
95 ctxt->name = value; \
96 return(ctxt->name##Nr++); \
97} \
98extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
99 type ret; \
100 if (ctxt->name##Nr <= 0) return(0); \
101 ctxt->name##Nr--; \
102 if (ctxt->name##Nr > 0) \
103 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
104 else \
105 ctxt->name = NULL; \
106 ret = ctxt->name##Tab[ctxt->name##Nr]; \
107 ctxt->name##Tab[ctxt->name##Nr] = 0; \
108 return(ret); \
109} \
110
111PUSH_AND_POP(xmlXPathObjectPtr, value)
112
113/*
114 * Macros for accessing the content. Those should be used only by the parser,
115 * and not exported.
116 *
117 * Dirty macros, i.e. one need to make assumption on the context to use them
118 *
119 * CUR_PTR return the current pointer to the CHAR to be parsed.
120 * CUR returns the current CHAR value, i.e. a 8 bit value if compiled
121 * in ISO-Latin or UTF-8, and the current 16 bit value if compiled
122 * in UNICODE mode. This should be used internally by the parser
123 * only to compare to ASCII values otherwise it would break when
124 * running with UTF-8 encoding.
125 * NXT(n) returns the n'th next CHAR. Same as CUR is should be used only
126 * to compare on ASCII based substring.
127 * SKIP(n) Skip n CHAR, and must also be used only to skip ASCII defined
128 * strings within the parser.
129 * CURRENT Returns the current char value, with the full decoding of
130 * UTF-8 if we are using this mode. It returns an int.
131 * NEXT Skip to the next character, this does the proper decoding
132 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
133 * It returns the pointer to the current CHAR.
134 */
135
136#define CUR (*ctxt->cur)
137#define SKIP(val) ctxt->cur += (val)
138#define NXT(val) ctxt->cur[(val)]
139#define CUR_PTR ctxt->cur
140
141#define SKIP_BLANKS \
142 while (IS_BLANK(*(ctxt->cur))) NEXT
143
144#ifndef USE_UTF_8
145#define CURRENT (*ctxt->cur)
146#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
147#else
148#endif
149
150/************************************************************************
151 * *
152 * Error handling routines *
153 * *
154 ************************************************************************/
155
156#define XPATH_EXPRESSION_OK 0
157#define XPATH_NUMBER_ERROR 1
158#define XPATH_UNFINISHED_LITERAL_ERROR 2
159#define XPATH_START_LITERAL_ERROR 3
160#define XPATH_VARIABLE_REF_ERROR 4
161#define XPATH_UNDEF_VARIABLE_ERROR 5
162#define XPATH_INVALID_PREDICATE_ERROR 6
163#define XPATH_EXPR_ERROR 7
164#define XPATH_UNCLOSED_ERROR 8
165#define XPATH_UNKNOWN_FUNC_ERROR 9
166#define XPATH_INVALID_OPERAND 10
167#define XPATH_INVALID_TYPE 11
168#define XPATH_INVALID_ARITY 12
169
170const char *xmlXPathErrorMessages[] = {
171 "Ok",
172 "Number encoding",
173 "Unfinished litteral",
174 "Start of litteral",
175 "Expected $ for variable reference",
176 "Undefined variable",
177 "Invalid predicate",
178 "Invalid expression",
179 "Missing closing curly brace",
180 "Unregistered function",
181 "Invalid operand",
182 "Invalid type",
183 "Invalid number of arguments",
184};
185
186/**
187 * xmlXPathError:
188 * @ctxt: the XPath Parser context
189 * @file: the file name
190 * @line: the line number
191 * @no: the error number
192 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000193 * Create a new xmlNodeSetPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000194 *
195 * Returns the newly created object.
196 */
197void
198xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
199 int line, int no) {
200 int n;
201 const char *cur;
202 const char *base;
203
204 fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line,
205 xmlXPathErrorMessages[no]);
206
207 cur = ctxt->cur;
208 base = ctxt->base;
209 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
210 cur--;
211 }
212 n = 0;
213 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
214 cur--;
215 if ((*cur == '\n') || (*cur == '\r')) cur++;
216 base = cur;
217 n = 0;
218 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
219 fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++);
220 n++;
221 }
222 fprintf(xmlXPathDebug, "\n");
223 cur = ctxt->cur;
224 while ((*cur == '\n') || (*cur == '\r'))
225 cur--;
226 n = 0;
227 while ((cur != base) && (n++ < 80)) {
228 fprintf(xmlXPathDebug, " ");
229 base++;
230 }
231 fprintf(xmlXPathDebug,"^\n");
232}
233
234#define CHECK_ERROR \
235 if (ctxt->error != XPATH_EXPRESSION_OK) return
236
237#define ERROR(X) \
238 { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
239 ctxt->error = (X); return; }
240
241#define CHECK_TYPE(typeval) \
242 if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \
243 ERROR(XPATH_INVALID_TYPE) \
244
245
246/************************************************************************
247 * *
248 * Routines to handle NodeSets *
249 * *
250 ************************************************************************/
251
252#define XML_NODESET_DEFAULT 10
253/**
254 * xmlXPathNodeSetCreate:
255 * @val: an initial xmlNodePtr, or NULL
256 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000257 * Create a new xmlNodeSetPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000258 *
259 * Returns the newly created object.
260 */
261xmlNodeSetPtr
262xmlXPathNodeSetCreate(xmlNodePtr val) {
263 xmlNodeSetPtr ret;
264
265 ret = (xmlNodeSetPtr) malloc(sizeof(xmlNodeSet));
266 if (ret == NULL) {
267 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
268 return(NULL);
269 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000270 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000271 if (val != NULL) {
272 ret->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
273 sizeof(xmlNodePtr));
274 if (ret->nodeTab == NULL) {
275 fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
276 return(NULL);
277 }
278 memset(ret->nodeTab, 0 ,
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000279 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000280 ret->nodeMax = XML_NODESET_DEFAULT;
281 ret->nodeTab[ret->nodeNr++] = val;
282 }
283 return(ret);
284}
285
286/**
287 * xmlXPathNodeSetAdd:
288 * @cur: the initial node set
289 * @val: a new xmlNodePtr
290 *
291 * add a new xmlNodePtr ot an existing NodeSet
292 */
293void
294xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
295 int i;
296
297 if (val == NULL) return;
298
299 /*
300 * check against doublons
301 */
302 for (i = 0;i < cur->nodeNr;i++)
303 if (cur->nodeTab[i] == val) return;
304
305 /*
306 * grow the nodeTab if needed
307 */
308 if (cur->nodeMax == 0) {
309 cur->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
310 sizeof(xmlNodePtr));
311 if (cur->nodeTab == NULL) {
312 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
313 return;
314 }
315 memset(cur->nodeTab, 0 ,
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000316 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000317 cur->nodeMax = XML_NODESET_DEFAULT;
318 } else if (cur->nodeNr == cur->nodeMax) {
319 xmlNodePtr *temp;
320
321 cur->nodeMax *= 2;
322 temp = (xmlNodePtr *) realloc(cur->nodeTab, cur->nodeMax *
323 sizeof(xmlNodePtr));
324 if (temp == NULL) {
325 fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
326 return;
327 }
328 }
329 cur->nodeTab[cur->nodeNr++] = val;
330}
331
332/**
333 * xmlXPathNodeSetMerge:
334 * @val1: the first NodeSet
335 * @val2: the second NodeSet
336 *
337 * Merges two nodesets, all nodes from @val2 are added to @val1
338 *
339 * Returns val1 once extended or NULL in case of error.
340 */
341xmlNodeSetPtr
342xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
343 int i;
344
345 if (val1 == NULL) return(NULL);
346 if (val2 == NULL) return(val1);
347
348 /*
349 * !!!!! this can be optimized a lot, knowing that both
350 * val1 and val2 already have unicity of their values.
351 */
352
353 for (i = 0;i < val2->nodeNr;i++)
354 xmlXPathNodeSetAdd(val1, val2->nodeTab[i]);
355
356 return(val1);
357}
358
359/**
360 * xmlXPathNodeSetDel:
361 * @cur: the initial node set
362 * @val: an xmlNodePtr
363 *
364 * Removes an xmlNodePtr from an existing NodeSet
365 */
366void
367xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
368 int i;
369
370 if (cur == NULL) return;
371 if (val == NULL) return;
372
373 /*
374 * check against doublons
375 */
376 for (i = 0;i < cur->nodeNr;i++)
377 if (cur->nodeTab[i] == val) break;
378
379 if (i >= cur->nodeNr) {
380#ifdef DEBUG
381 fprintf(xmlXPathDebug,
382 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
383 val->name);
384#endif
385 return;
386 }
387 cur->nodeNr--;
388 for (;i < cur->nodeNr;i++)
389 cur->nodeTab[i] = cur->nodeTab[i + 1];
390 cur->nodeTab[cur->nodeNr] = NULL;
391}
392
393/**
394 * xmlXPathNodeSetRemove:
395 * @cur: the initial node set
396 * @val: the index to remove
397 *
398 * Removes an entry from an existing NodeSet list.
399 */
400void
401xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
402 if (cur == NULL) return;
403 if (val >= cur->nodeNr) return;
404 cur->nodeNr--;
405 for (;val < cur->nodeNr;val++)
406 cur->nodeTab[val] = cur->nodeTab[val + 1];
407 cur->nodeTab[cur->nodeNr] = NULL;
408}
409
410/**
411 * xmlXPathFreeNodeSet:
412 * @obj: the xmlNodeSetPtr to free
413 *
414 * Free the NodeSet compound (not the actual nodes !).
415 */
416void
417xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
418 if (obj == NULL) return;
419 if (obj->nodeTab != NULL) {
420#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000421 memset(obj->nodeTab, 0xB , (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000422#endif
423 free(obj->nodeTab);
424 }
425#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000426 memset(obj, 0xB , (size_t) sizeof(xmlNodeSet));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000427#endif
428 free(obj);
429}
430
431#ifdef DEBUG
432/**
433 * xmlXPathDebugNodeSet:
434 * @output: a FILE * for the output
435 * @obj: the xmlNodeSetPtr to free
436 *
437 * Quick display of a NodeSet
438 */
439void
440xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) {
441 int i;
442
443 if (output == NULL) output = xmlXPathDebug;
444 if (obj == NULL) {
445 fprintf(output, "NodeSet == NULL !\n");
446 return;
447 }
448 if (obj->nodeNr == 0) {
449 fprintf(output, "NodeSet is empty\n");
450 return;
451 }
452 if (obj->nodeTab == NULL) {
453 fprintf(output, " nodeTab == NULL !\n");
454 return;
455 }
456 for (i = 0; i < obj->nodeNr; i++) {
457 if (obj->nodeTab[i] == NULL) {
458 fprintf(output, " NULL !\n");
459 return;
460 }
461 if (obj->nodeTab[i]->name == NULL)
462 fprintf(output, " noname!");
463 else fprintf(output, " %s", obj->nodeTab[i]->name);
464 }
465 fprintf(output, "\n");
466}
467#endif
468
469/************************************************************************
470 * *
471 * Routines to handle Variable *
472 * *
473 * UNIMPLEMENTED CURRENTLY *
474 * *
475 ************************************************************************/
476
477/**
478 * xmlXPathVariablelookup:
479 * @ctxt: the XPath Parser context
480 * @prefix: the variable name namespace if any
481 * @name: the variable name
482 *
483 * Search in the Variable array of the context for the given
484 * variable value.
485 *
486 * UNIMPLEMENTED: always return NULL.
487 *
488 * Returns the value or NULL if not found
489 */
490xmlXPathObjectPtr
491xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt,
492 const CHAR *prefix, const CHAR *name) {
493 return(NULL);
494}
495
496/************************************************************************
497 * *
498 * Routines to handle Values *
499 * *
500 ************************************************************************/
501
502/* Allocations are terrible, one need to optimize all this !!! */
503
504/**
505 * xmlXPathNewFloat:
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000506 * @val: the double value
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000507 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000508 * Create a new xmlXPathObjectPtr of type double and of value @val
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000509 *
510 * Returns the newly created object.
511 */
512xmlXPathObjectPtr
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000513xmlXPathNewFloat(double val) {
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000514 xmlXPathObjectPtr ret;
515
516 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
517 if (ret == NULL) {
518 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
519 return(NULL);
520 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000521 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000522 ret->type = XPATH_NUMBER;
523 ret->floatval = val;
524 return(ret);
525}
526
527/**
528 * xmlXPathNewBoolean:
529 * @val: the boolean value
530 *
531 * Create a new xmlXPathObjectPtr of type boolean and of value @val
532 *
533 * Returns the newly created object.
534 */
535xmlXPathObjectPtr
536xmlXPathNewBoolean(int val) {
537 xmlXPathObjectPtr ret;
538
539 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
540 if (ret == NULL) {
541 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
542 return(NULL);
543 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000544 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000545 ret->type = XPATH_BOOLEAN;
546 ret->boolval = (val != 0);
547 return(ret);
548}
549
550/**
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000551 * xmlXPathNewBoolean:
552 * @val: the CHAR * value
553 *
554 * Create a new xmlXPathObjectPtr of type string and of value @val
555 *
556 * Returns the newly created object.
557 */
558xmlXPathObjectPtr
559xmlXPathNewString(const CHAR *val) {
560 xmlXPathObjectPtr ret;
561
562 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
563 if (ret == NULL) {
564 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
565 return(NULL);
566 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000567 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000568 ret->type = XPATH_STRING;
569 ret->stringval = xmlStrdup(val);
570 return(ret);
571}
572
573/**
574 * xmlXPathNewNodeSet:
575 * @val: the NodePtr value
576 *
577 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
578 * it with the single Node @val
579 *
580 * Returns the newly created object.
581 */
582xmlXPathObjectPtr
583xmlXPathNewNodeSet(xmlNodePtr val) {
584 xmlXPathObjectPtr ret;
585
586 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
587 if (ret == NULL) {
588 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
589 return(NULL);
590 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000591 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000592 ret->type = XPATH_NODESET;
593 ret->nodesetval = xmlXPathNodeSetCreate(val);
594 return(ret);
595}
596
597/**
598 * xmlXPathNewNodeSetList:
599 * @val: an existing NodeSet
600 *
601 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
602 * it with the Nodeset @val
603 *
604 * Returns the newly created object.
605 */
606xmlXPathObjectPtr
607xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
608 xmlXPathObjectPtr ret;
609
610 ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
611 if (ret == NULL) {
612 fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
613 return(NULL);
614 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000615 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000616 ret->type = XPATH_NODESET;
617 ret->nodesetval = val;
618 return(ret);
619}
620
621/**
622 * xmlXPathFreeObject:
623 * @obj: the object to free
624 *
625 * Free up an xmlXPathObjectPtr object.
626 */
627void
628xmlXPathFreeObject(xmlXPathObjectPtr obj) {
629 if (obj == NULL) return;
630 if (obj->nodesetval != NULL)
631 xmlXPathFreeNodeSet(obj->nodesetval);
632 if (obj->stringval != NULL)
633 free(obj->stringval);
634#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000635 memset(obj, 0xB , (size_t) sizeof(xmlXPathObject));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000636#endif
637 free(obj);
638}
639
640/************************************************************************
641 * *
642 * Routines to handle XPath contexts *
643 * *
644 ************************************************************************/
645
646/**
647 * xmlXPathNewContext:
648 * @doc: the XML document
649 * @variables: the variable list
650 * @functions: the function list
651 * @namespaces: the namespace list
652 *
653 * Create a new xmlXPathContext
654 *
655 * Returns the xmlXPathContext just allocated.
656 */
657xmlXPathContextPtr
658xmlXPathNewContext(xmlDocPtr doc, void *variables, void *functions,
659 void *namespaces) {
660 xmlXPathContextPtr ret;
661
662 ret = (xmlXPathContextPtr) malloc(sizeof(xmlXPathContext));
663 if (ret == NULL) {
664 fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n");
665 return(NULL);
666 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000667 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000668 ret->doc = doc;
669 ret->variables = variables;
670 ret->functions = functions;
671 ret->namespaces = namespaces;
672 return(ret);
673}
674
675/**
676 * xmlXPathFreeContext:
677 * @ctxt: the context to free
678 *
679 * Free up an xmlXPathContext
680 */
681void
682xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
683#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000684 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000685#endif
686 free(ctxt);
687}
688
689/************************************************************************
690 * *
691 * Routines to handle XPath parser contexts *
692 * *
693 ************************************************************************/
694
695#define CHECK_CTXT \
696 if (ctxt == NULL) { \
697 fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \
698 __FILE__, __LINE__); \
699 } \
700
701
702#define CHECK_CONTEXT \
703 if (ctxt == NULL) { \
704 fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \
705 __FILE__, __LINE__); \
706 } \
707 if (ctxt->doc == NULL) { \
708 fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \
709 __FILE__, __LINE__); \
710 } \
711 if (ctxt->doc->root == NULL) { \
712 fprintf(xmlXPathDebug, \
713 "%s:%d Internal error: document without root\n", \
714 __FILE__, __LINE__); \
715 } \
716
717
718/**
719 * xmlXPathNewParserContext:
720 * @str: the XPath expression
721 * @ctxt: the XPath context
722 *
723 * Create a new xmlXPathParserContext
724 *
725 * Returns the xmlXPathParserContext just allocated.
726 */
727xmlXPathParserContextPtr
728xmlXPathNewParserContext(const CHAR *str, xmlXPathContextPtr ctxt) {
729 xmlXPathParserContextPtr ret;
730
731 ret = (xmlXPathParserContextPtr) malloc(sizeof(xmlXPathParserContext));
732 if (ret == NULL) {
733 fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n");
734 return(NULL);
735 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000736 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000737 ret->cur = ret->base = str;
738 ret->context = ctxt;
739
740 /* Allocate the value stack */
741 ret->valueTab = (xmlXPathObjectPtr *)
742 malloc(10 * sizeof(xmlXPathObjectPtr));
743 ret->valueNr = 0;
744 ret->valueMax = 10;
745 ret->value = NULL;
746 return(ret);
747}
748
749/**
750 * xmlXPathFreeParserContext:
751 * @ctxt: the context to free
752 *
753 * Free up an xmlXPathParserContext
754 */
755void
756xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
757 if (ctxt->valueTab != NULL) {
758#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000759 memset(ctxt->valueTab, 0xB , 10 * (size_t) sizeof(xmlXPathObjectPtr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000760#endif
761 free(ctxt->valueTab);
762 }
763#ifdef DEBUG
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000764 memset(ctxt, 0xB , (size_t) sizeof(xmlXPathParserContext));
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000765#endif
766 free(ctxt);
767}
768
769/************************************************************************
770 * *
771 * The implicit core function library *
772 * *
773 ************************************************************************/
774
775/*
776 * TODO: check the semantic for all these operations in case of operands
777 * with different types, Cast function probably need to be provided
778 * to simplify the coding.
779 */
780
781/*
782 * Auto-pop and cast to a number
783 */
784void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
785
786#define CHECK_ARITY(x) \
787 if (nargs != (x)) { \
788 ERROR(XPATH_INVALID_ARITY); \
789 } \
790
791
792#define POP_FLOAT \
793 arg = valuePop(ctxt); \
794 if (arg == NULL) { \
795 ERROR(XPATH_INVALID_OPERAND); \
796 } \
797 if (arg->type != XPATH_NUMBER) { \
798 valuePush(ctxt, arg); \
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000799 xmlXPathNumberFunction(ctxt, 1); \
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000800 arg = valuePop(ctxt); \
801 }
802
803/**
804 * xmlXPathEqualValues:
805 * @arg1: first XPath object argument
806 * @arg2: second XPath object argument
807 *
808 * Implement the equal operation on XPath objects content: @arg1 == @arg2
809 *
810 * Returns 0 or 1 depending on the results of the test.
811 * TODO: rewrite using the stack for evaluation
812 */
813int
814xmlXPathEqualValues(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
815 if (arg1 == arg2) {
816#ifdef DEBUG_EXPR
817 fprintf(xmlXPathDebug, "Equal: by pointer\n");
818#endif
819 return(1);
820 }
821 if ((arg1 == NULL) || (arg2 == NULL)) {
822#ifdef DEBUG_EXPR
823 fprintf(xmlXPathDebug, "Equal: arg NULL\n");
824#endif
825 return(0);
826 }
827 if (arg1->type != arg2->type) {
828 /* TODO : see 4.3 Boolean section !!!!!!!!!!! */
829#ifdef DEBUG_EXPR
830 fprintf(xmlXPathDebug, "Equal: distinct types\n");
831#endif
832 return(0);
833 }
834 switch (arg1->type) {
835 case XPATH_UNDEFINED:
836#ifdef DEBUG_EXPR
837 fprintf(xmlXPathDebug, "Equal: undefined\n");
838#endif
839 return(0);
840 case XPATH_NODESET:
841 TODO /* compare nodesets */
842 break;
843 case XPATH_BOOLEAN:
844#ifdef DEBUG_EXPR
845 fprintf(xmlXPathDebug, "Equal: %d boolean %d \n",
846 arg1->boolval, arg2->boolval);
847#endif
848 return(arg1->boolval == arg2->boolval);
849 case XPATH_NUMBER:
850#ifdef DEBUG_EXPR
851 fprintf(xmlXPathDebug, "Equal: %f number %f \n",
852 arg1->floatval, arg2->floatval);
853#endif
854 return(arg1->floatval == arg2->floatval);
855 case XPATH_STRING:
856#ifdef DEBUG_EXPR
857 fprintf(xmlXPathDebug, "Equal: %s string %s \n",
858 arg1->stringval, arg2->stringval);
859#endif
860 return(!xmlStrcmp(arg1->stringval, arg2->stringval));
861 }
862 return(1);
863}
864
865/**
866 * xmlXPathCompareValues:
867 * @inf: less than (1) or greater than (2)
868 * @strict: is the comparison strict
869 * @arg1: first XPath object argument
870 * @arg2: second XPath object argument
871 *
872 * Implement the compare operation on XPath objects:
873 * @arg1 < @arg2 (1, 1, ...
874 * @arg1 <= @arg2 (1, 0, ...
875 * @arg1 > @arg2 (0, 1, ...
876 * @arg1 >= @arg2 (0, 0, ...
877 *
878 * Returns 0 or 1 depending on the results of the test.
879 */
880int
881xmlXPathCompareValues(int inf, int strict, xmlXPathObjectPtr arg1,
882 xmlXPathObjectPtr arg2) {
883 TODO /* compare */
884 return(0);
885}
886
887/**
888 * xmlXPathValueFlipSign:
889 * @ctxt: the XPath Parser context
890 *
891 * Implement the unary - operation on an XPath object
892 * The numeric operators convert their operands to numbers as if
893 * by calling the number function.
894 */
895void
896xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
897 xmlXPathObjectPtr arg;
898
899 POP_FLOAT
900 arg->floatval = -arg->floatval;
901 valuePush(ctxt, arg);
902}
903
904/**
905 * xmlXPathAddValues:
906 * @ctxt: the XPath Parser context
907 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000908 * Implement the add operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000909 * The numeric operators convert their operands to numbers as if
910 * by calling the number function.
911 */
912void
913xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
914 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000915 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000916
917 POP_FLOAT
918 val = arg->floatval;
919 xmlXPathFreeObject(arg);
920
921 POP_FLOAT
922 arg->floatval += val;
923 valuePush(ctxt, arg);
924}
925
926/**
927 * xmlXPathSubValues:
928 * @ctxt: the XPath Parser context
929 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000930 * Implement the substraction operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000931 * The numeric operators convert their operands to numbers as if
932 * by calling the number function.
933 */
934void
935xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
936 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000937 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000938
939 POP_FLOAT
940 val = arg->floatval;
941 xmlXPathFreeObject(arg);
942
943 POP_FLOAT
944 arg->floatval -= val;
945 valuePush(ctxt, arg);
946}
947
948/**
949 * xmlXPathMultValues:
950 * @ctxt: the XPath Parser context
951 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000952 * Implement the multiply operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000953 * The numeric operators convert their operands to numbers as if
954 * by calling the number function.
955 */
956void
957xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
958 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000959 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000960
961 POP_FLOAT
962 val = arg->floatval;
963 xmlXPathFreeObject(arg);
964
965 POP_FLOAT
966 arg->floatval *= val;
967 valuePush(ctxt, arg);
968}
969
970/**
971 * xmlXPathDivValues:
972 * @ctxt: the XPath Parser context
973 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000974 * Implement the div operation on XPath objects:
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000975 * The numeric operators convert their operands to numbers as if
976 * by calling the number function.
977 */
978void
979xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
980 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +0000981 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +0000982
983 POP_FLOAT
984 val = arg->floatval;
985 xmlXPathFreeObject(arg);
986
987 POP_FLOAT
988 arg->floatval /= val;
989 valuePush(ctxt, arg);
990}
991
992/**
993 * xmlXPathModValues:
994 * @ctxt: the XPath Parser context
995 *
996 * Implement the div operation on XPath objects: @arg1 / @arg2
997 * The numeric operators convert their operands to numbers as if
998 * by calling the number function.
999 */
1000void
1001xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
1002 xmlXPathObjectPtr arg;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001003 double val;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001004
1005 POP_FLOAT
1006 val = arg->floatval;
1007 xmlXPathFreeObject(arg);
1008
1009 POP_FLOAT
1010 arg->floatval /= val;
1011 valuePush(ctxt, arg);
1012}
1013
1014/************************************************************************
1015 * *
1016 * The traversal functions *
1017 * *
1018 ************************************************************************/
1019
1020#define AXIS_ANCESTOR 1
1021#define AXIS_ANCESTOR_OR_SELF 2
1022#define AXIS_ATTRIBUTE 3
1023#define AXIS_CHILD 4
1024#define AXIS_DESCENDANT 5
1025#define AXIS_DESCENDANT_OR_SELF 6
1026#define AXIS_FOLLOWING 7
1027#define AXIS_FOLLOWING_SIBLING 8
1028#define AXIS_NAMESPACE 9
1029#define AXIS_PARENT 10
1030#define AXIS_PRECEDING 11
1031#define AXIS_PRECEDING_SIBLING 12
1032#define AXIS_SELF 13
1033
1034/*
1035 * A traversal function enumerates nodes along an axis.
1036 * Initially it must be called with NULL, and it indicates
1037 * termination on the axis by returning NULL.
1038 */
1039typedef xmlNodePtr (*xmlXPathTraversalFunction)
1040 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
1041
1042/**
1043 * mlXPathNextSelf:
1044 * @ctxt: the XPath Parser context
1045 * @cur: the current node in the traversal
1046 *
1047 * Traversal function for the "self" direction
1048 * he self axis contains just the context node itself
1049 */
1050xmlNodePtr
1051xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1052 if (cur == NULL)
1053 return(ctxt->context->node);
1054 return(NULL);
1055}
1056
1057/**
1058 * mlXPathNextChild:
1059 * @ctxt: the XPath Parser context
1060 * @cur: the current node in the traversal
1061 *
1062 * Traversal function for the "child" direction
1063 * The child axis contains the children of the context node in document order.
1064 */
1065xmlNodePtr
1066xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1067 if (cur == NULL)
1068 return(ctxt->context->node->childs);
1069 return(cur->next);
1070}
1071
1072/**
1073 * mlXPathNextDescendant:
1074 * @ctxt: the XPath Parser context
1075 * @cur: the current node in the traversal
1076 *
1077 * Traversal function for the "descendant" direction
1078 * the descendant axis contains the descendants of the context node in document
1079 * order; a descendant is a child or a child of a child and so on.
1080 */
1081xmlNodePtr
1082xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1083 if (cur == NULL)
1084 return(ctxt->context->node->childs);
1085
1086 if (cur->childs != NULL) return(cur->childs);
1087 if (cur->next != NULL) return(cur->next);
1088
1089 do {
1090 cur = cur->parent;
1091 if (cur == NULL) return(NULL);
1092 if (cur == ctxt->context->node) return(NULL);
1093 if (cur->next != NULL) {
1094 cur = cur->next;
1095 return(cur);
1096 }
1097 } while (cur != NULL);
1098 return(cur);
1099}
1100
1101/**
1102 * mlXPathNextDescendantOrSelf:
1103 * @ctxt: the XPath Parser context
1104 * @cur: the current node in the traversal
1105 *
1106 * Traversal function for the "descendant-or-self" direction
1107 * the descendant-or-self axis contains the context node and the descendants
1108 * of the context node in document order; thus the context node is the first
1109 * node on the axis, and the first child of the context node is the second node
1110 * on the axis
1111 */
1112xmlNodePtr
1113xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1114 if (cur == NULL)
1115 return(ctxt->context->node);
1116
1117 if (cur->childs != NULL) return(cur->childs);
1118 if (cur->next != NULL) return(cur->next);
1119
1120 do {
1121 cur = cur->parent;
1122 if (cur == NULL) return(NULL);
1123 if (cur == ctxt->context->node) return(NULL);
1124 if (cur->next != NULL) {
1125 cur = cur->next;
1126 return(cur);
1127 }
1128 } while (cur != NULL);
1129 return(cur);
1130}
1131
1132/**
1133 * xmlXPathNextParent:
1134 * @ctxt: the XPath Parser context
1135 * @cur: the current node in the traversal
1136 *
1137 * Traversal function for the "parent" direction
1138 * The parent axis contains the parent of the context node, if there is one.
1139 */
1140xmlNodePtr
1141xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1142 /*
1143 * !!!!!!!!!!!!!
1144 * the parent of an attribute or namespace node is the element
1145 * to which the attribute or namespace node is attached
1146 */
1147 if (cur == NULL)
1148 return(ctxt->context->node->parent);
1149 return(NULL);
1150}
1151
1152/**
1153 * xmlXPathNextAncestor:
1154 * @ctxt: the XPath Parser context
1155 * @cur: the current node in the traversal
1156 *
1157 * Traversal function for the "ancestor" direction
1158 * the ancestor axis contains the ancestors of the context node; the ancestors
1159 * of the context node consist of the parent of context node and the parent's
1160 * parent and so on; the nodes are ordered in reverse document order; thus the
1161 * parent is the first node on the axis, and the parent's parent is the second
1162 * node on the axis
1163 */
1164xmlNodePtr
1165xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1166 /*
1167 * !!!!!!!!!!!!!
1168 * the parent of an attribute or namespace node is the element
1169 * to which the attribute or namespace node is attached
1170 */
1171 if (cur == NULL)
1172 return(ctxt->context->node->parent);
1173 if (cur == ctxt->context->doc->root) return(NULL);
1174 return(cur->parent);
1175}
1176
1177/**
1178 * xmlXPathNextAncestorOrSelf:
1179 * @ctxt: the XPath Parser context
1180 * @cur: the current node in the traversal
1181 *
1182 * Traversal function for the "ancestor-or-self" direction
1183 * he ancestor-or-self axis contains the context node and ancestors of the context
1184 * node in reverse document order; thus the context node is the first node on the
1185 * axis, and the context node's parent the second; parent here is defined the same
1186 * as with the parent axis.
1187 */
1188xmlNodePtr
1189xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1190 /*
1191 * !!!!!!!!!!!!!
1192 * the parent of an attribute or namespace node is the element
1193 * to which the attribute or namespace node is attached
1194 */
1195 if (cur == NULL)
1196 return(ctxt->context->node);
1197 if (cur == ctxt->context->doc->root) return(NULL);
1198 return(cur->parent);
1199}
1200
1201/**
1202 * xmlXPathNextFollowingSibling:
1203 * @ctxt: the XPath Parser context
1204 * @cur: the current node in the traversal
1205 *
1206 * Traversal function for the "following-sibling" direction
1207 * The following-sibling axis contains the following siblings of the context
1208 * node in document order.
1209 */
1210xmlNodePtr
1211xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1212 if (cur == NULL)
1213 return(ctxt->context->node->next);
1214 return(cur->next);
1215}
1216
1217/**
1218 * xmlXPathNextPrecedingSibling:
1219 * @ctxt: the XPath Parser context
1220 * @cur: the current node in the traversal
1221 *
1222 * Traversal function for the "preceding-sibling" direction
1223 * The preceding-sibling axis contains the preceding siblings of the context
1224 * node in reverse document order; the first preceding sibling is first on the
1225 * axis; the sibling preceding that node is the second on the axis and so on.
1226 */
1227xmlNodePtr
1228xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1229 if (cur == NULL)
1230 return(ctxt->context->node->prev);
1231 return(cur->prev);
1232}
1233
1234/**
1235 * xmlXPathNextFollowing:
1236 * @ctxt: the XPath Parser context
1237 * @cur: the current node in the traversal
1238 *
1239 * Traversal function for the "following" direction
1240 * The following axis contains all nodes in the same document as the context
1241 * node that are after the context node in document order, excluding any
1242 * descendants and excluding attribute nodes and namespace nodes; the nodes
1243 * are ordered in document order
1244 */
1245xmlNodePtr
1246xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1247 if (cur == NULL)
1248 return(ctxt->context->node->next);; /* !!!!!!!!! */
1249 if (cur->childs != NULL) return(cur->childs);
1250 if (cur->next != NULL) return(cur->next);
1251
1252 do {
1253 cur = cur->parent;
1254 if (cur == NULL) return(NULL);
1255 if (cur == ctxt->context->doc->root) return(NULL);
1256 if (cur->next != NULL) {
1257 cur = cur->next;
1258 return(cur);
1259 }
1260 } while (cur != NULL);
1261 return(cur);
1262}
1263
1264/**
1265 * xmlXPathNextPreceding:
1266 * @ctxt: the XPath Parser context
1267 * @cur: the current node in the traversal
1268 *
1269 * Traversal function for the "preceding" direction
1270 * the preceding axis contains all nodes in the same document as the context
1271 * node that are before the context node in document order, excluding any
1272 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
1273 * ordered in reverse document order
1274 */
1275xmlNodePtr
1276xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
1277 if (cur == NULL)
1278 return(ctxt->context->node->prev); /* !!!!!!!!! */
1279 if (cur->last != NULL) return(cur->last);
1280 if (cur->prev != NULL) return(cur->prev);
1281
1282 do {
1283 cur = cur->parent;
1284 if (cur == NULL) return(NULL);
1285 if (cur == ctxt->context->doc->root) return(NULL);
1286 if (cur->prev != NULL) {
1287 cur = cur->prev;
1288 return(cur);
1289 }
1290 } while (cur != NULL);
1291 return(cur);
1292}
1293
1294/**
1295 * xmlXPathNextNamespace:
1296 * @ctxt: the XPath Parser context
1297 * @cur: the current attribute in the traversal
1298 *
1299 * Traversal function for the "namespace" direction
1300 * the namespace axis contains the namespace nodes of the context node;
1301 * the order of nodes on this axis is implementation-defined; the axis will
1302 * be empty unless the context node is an element
1303 */
1304xmlAttrPtr
1305xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
1306 TODO /* namespace traversal */
1307 return(NULL);
1308}
1309
1310/**
1311 * xmlXPathNextAttribute:
1312 * @ctxt: the XPath Parser context
1313 * @cur: the current attribute in the traversal
1314 *
1315 * Traversal function for the "attribute" direction
1316 */
1317xmlAttrPtr
1318xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
1319 if (cur == NULL)
1320 return(ctxt->context->node->properties);
1321 return(cur->next);
1322}
1323
1324/************************************************************************
1325 * *
1326 * NodeTest Functions *
1327 * *
1328 ************************************************************************/
1329
1330#define NODE_TEST_NONE 0
1331#define NODE_TEST_TYPE 1
1332#define NODE_TEST_PI 2
1333#define NODE_TEST_ALL 3
1334#define NODE_TEST_NS 4
1335#define NODE_TEST_NAME 5
1336
1337#define NODE_TYPE_COMMENT 50
1338#define NODE_TYPE_TEXT 51
1339#define NODE_TYPE_PI 52
1340#define NODE_TYPE_NODE 53
1341
1342#define IS_FUNCTION 200
1343
1344/**
1345 * xmlXPathNodeCollectAndTest:
1346 * @ctxt: the XPath Parser context
1347 * @cur: the current node to test
1348 *
1349 * This is the function implementing a step: based on the current list
1350 * of nodes, it builds up a new list, looking at all nodes under that
1351 * axis and selecting them.
1352 *
1353 * Returns the new NodeSet resulting from the search.
1354 */
1355xmlNodeSetPtr
1356xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis,
1357 int test, int type, const CHAR *prefix, const CHAR *name) {
1358#ifdef DEBUG_STEP
1359 int n = 0, t = 0;
1360#endif
1361 int i;
1362 xmlNodeSetPtr ret;
1363 xmlXPathTraversalFunction next = NULL;
1364 xmlNodePtr cur = NULL;
1365
1366 if (ctxt->context->nodelist == NULL) {
1367 if (ctxt->context->node == NULL) {
1368 fprintf(xmlXPathDebug,
1369 "xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n",
1370 __FILE__, __LINE__);
1371 return(NULL);
1372 }
1373 STRANGE
1374 return(NULL);
1375 }
1376#ifdef DEBUG_STEP
1377 fprintf(xmlXPathDebug, "new step : ");
1378#endif
1379 switch (axis) {
1380 case AXIS_ANCESTOR:
1381#ifdef DEBUG_STEP
1382 fprintf(xmlXPathDebug, "axis 'ancestors' ");
1383#endif
1384 next = xmlXPathNextAncestor; break;
1385 case AXIS_ANCESTOR_OR_SELF:
1386#ifdef DEBUG_STEP
1387 fprintf(xmlXPathDebug, "axis 'ancestors-or-self' ");
1388#endif
1389 next = xmlXPathNextAncestorOrSelf; break;
1390 case AXIS_ATTRIBUTE:
1391#ifdef DEBUG_STEP
1392 fprintf(xmlXPathDebug, "axis 'attributes' ");
1393#endif
1394 TODO /* attribute axis */
1395 break;
1396 case AXIS_CHILD:
1397#ifdef DEBUG_STEP
1398 fprintf(xmlXPathDebug, "axis 'child' ");
1399#endif
1400 next = xmlXPathNextChild; break;
1401 case AXIS_DESCENDANT:
1402#ifdef DEBUG_STEP
1403 fprintf(xmlXPathDebug, "axis 'descendant' ");
1404#endif
1405 next = xmlXPathNextDescendant; break;
1406 case AXIS_DESCENDANT_OR_SELF:
1407#ifdef DEBUG_STEP
1408 fprintf(xmlXPathDebug, "axis 'descendant-or-self' ");
1409#endif
1410 next = xmlXPathNextDescendantOrSelf; break;
1411 case AXIS_FOLLOWING:
1412#ifdef DEBUG_STEP
1413 fprintf(xmlXPathDebug, "axis 'following' ");
1414#endif
1415 next = xmlXPathNextFollowing; break;
1416 case AXIS_FOLLOWING_SIBLING:
1417#ifdef DEBUG_STEP
1418 fprintf(xmlXPathDebug, "axis 'following-siblings' ");
1419#endif
1420 next = xmlXPathNextFollowingSibling; break;
1421 case AXIS_NAMESPACE:
1422#ifdef DEBUG_STEP
1423 fprintf(xmlXPathDebug, "axis 'namespace' ");
1424#endif
1425 TODO /* namespace axis */
1426 break;
1427 case AXIS_PARENT:
1428#ifdef DEBUG_STEP
1429 fprintf(xmlXPathDebug, "axis 'parent' ");
1430#endif
1431 next = xmlXPathNextParent; break;
1432 case AXIS_PRECEDING:
1433#ifdef DEBUG_STEP
1434 fprintf(xmlXPathDebug, "axis 'preceding' ");
1435#endif
1436 next = xmlXPathNextPreceding; break;
1437 case AXIS_PRECEDING_SIBLING:
1438#ifdef DEBUG_STEP
1439 fprintf(xmlXPathDebug, "axis 'preceding-sibling' ");
1440#endif
1441 next = xmlXPathNextPrecedingSibling; break;
1442 case AXIS_SELF:
1443#ifdef DEBUG_STEP
1444 fprintf(xmlXPathDebug, "axis 'self' ");
1445#endif
1446 next = xmlXPathNextSelf; break;
1447 }
1448 if (next == NULL) return(NULL);
1449 ret = xmlXPathNodeSetCreate(NULL);
1450#ifdef DEBUG_STEP
1451 fprintf(xmlXPathDebug, " context contains %d nodes\n",
1452 ctxt->context->nodelist->nodeNr);
1453 switch (test) {
1454 case NODE_TEST_NONE:
1455 fprintf(xmlXPathDebug, " seaching for none !!!\n");
1456 break;
1457 case NODE_TEST_TYPE:
1458 fprintf(xmlXPathDebug, " seaching for type %d\n", type);
1459 break;
1460 case NODE_TEST_PI:
1461 fprintf(xmlXPathDebug, " seaching for PI !!!\n");
1462 TODO /* PI search */
1463 break;
1464 case NODE_TEST_ALL:
1465 fprintf(xmlXPathDebug, " seaching for *\n");
1466 break;
1467 case NODE_TEST_NS:
1468 fprintf(xmlXPathDebug, " seaching for namespace %s\n",
1469 prefix);
1470 break;
1471 case NODE_TEST_NAME:
1472 fprintf(xmlXPathDebug, " seaching for name %s\n", name);
1473 if (prefix != NULL)
1474 fprintf(xmlXPathDebug, " with namespace %s\n",
1475 prefix);
1476 break;
1477 }
1478 fprintf(xmlXPathDebug, "Testing : ");
1479#endif
1480 for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) {
1481 ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
1482
1483 cur = NULL;
1484 do {
1485 cur = next(ctxt, cur);
1486 if (cur == NULL) break;
1487#ifdef DEBUG_STEP
1488 t++;
1489 fprintf(xmlXPathDebug, " %s", cur->name);
1490#endif
1491 switch (test) {
1492 case NODE_TEST_NONE:
1493 STRANGE
1494 return(NULL);
1495 case NODE_TEST_TYPE:
1496 if (cur->type == type) {
1497#ifdef DEBUG_STEP
1498 n++;
1499#endif
1500 xmlXPathNodeSetAdd(ret, cur);
1501 }
1502 break;
1503 case NODE_TEST_PI:
1504 TODO /* PI search */
1505 break;
1506 case NODE_TEST_ALL:
1507 if (cur->type == XML_ELEMENT_NODE) {
1508#ifdef DEBUG_STEP
1509 n++;
1510#endif
1511 xmlXPathNodeSetAdd(ret, cur);
1512 }
1513 break;
1514 case NODE_TEST_NS:
1515 TODO /* NS search */
1516 break;
1517 case NODE_TEST_NAME:
1518 if (!xmlStrcmp(name, cur->name) &&
1519 (((prefix == NULL) ||
1520 ((cur->ns != NULL) &&
1521 (!xmlStrcmp(prefix, cur->ns->href)))))) {
1522#ifdef DEBUG_STEP
1523 n++;
1524#endif
1525 xmlXPathNodeSetAdd(ret, cur);
1526 }
1527 break;
1528
1529 }
1530 } while (cur != NULL);
1531 }
1532#ifdef DEBUG_STEP
1533 fprintf(xmlXPathDebug,
1534 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
1535#endif
1536 return(ret);
1537}
1538
1539
1540/************************************************************************
1541 * *
1542 * Implicit tree core function library *
1543 * *
1544 ************************************************************************/
1545
1546/**
1547 * xmlXPathRoot:
1548 * @ctxt: the XPath Parser context
1549 *
1550 * Initialize the context to the root of the document
1551 */
1552void
1553xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
1554 if (ctxt->context->nodelist != NULL)
1555 xmlXPathFreeNodeSet(ctxt->context->nodelist);
1556 ctxt->context->node = ctxt->context->doc->root;
1557 ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->doc->root);
1558}
1559
1560/**
1561 * xmlXPathSearchPI:
1562 * @ctxt: the XPath Parser context
1563 *
1564 * Search a processing instruction by name. The name is
1565 * in the object on the stack.
1566 */
1567void
1568xmlXPathSearchPI(xmlXPathParserContextPtr ctxt, int nargs) {
1569 TODO /* Search PI */
1570 CHECK_ARITY(0);
1571 CHECK_TYPE(XPATH_NUMBER)
1572}
1573
1574/************************************************************************
1575 * *
1576 * The explicit core function library *
1577 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
1578 * *
1579 ************************************************************************/
1580
1581
1582/**
1583 * xmlXPathLastFunction:
1584 * @ctxt: the XPath Parser context
1585 *
1586 * Implement the last() XPath function
1587 * The last function returns the number of nodes in the context node list.
1588 */
1589void
1590xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1591 CHECK_ARITY(0);
1592 if ((ctxt->context->nodelist == NULL) ||
1593 (ctxt->context->node == NULL) ||
1594 (ctxt->context->nodelist->nodeNr == 0)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001595 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001596 } else {
1597 valuePush(ctxt,
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001598 xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001599 }
1600}
1601
1602/**
1603 * xmlXPathPositionFunction:
1604 * @ctxt: the XPath Parser context
1605 *
1606 * Implement the position() XPath function
1607 * The position function returns the position of the context node in the
1608 * context node list. The first position is 1, and so the last positionr
1609 * will be equal to last().
1610 */
1611void
1612xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1613 int i;
1614
1615 CHECK_ARITY(0);
1616 if ((ctxt->context->nodelist == NULL) ||
1617 (ctxt->context->node == NULL) ||
1618 (ctxt->context->nodelist->nodeNr == 0)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001619 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001620 }
1621 for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) {
1622 if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001623 valuePush(ctxt, xmlXPathNewFloat((double) i + 1));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001624 return;
1625 }
1626 }
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001627 valuePush(ctxt, xmlXPathNewFloat((double) 0));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001628}
1629
1630/**
1631 * xmlXPathCountFunction:
1632 * @ctxt: the XPath Parser context
1633 *
1634 * Implement the count() XPath function
1635 */
1636void
1637xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1638 xmlXPathObjectPtr cur;
1639
1640 CHECK_ARITY(1);
1641 CHECK_TYPE(XPATH_NODESET);
1642 cur = valuePop(ctxt);
1643
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001644 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001645 xmlXPathFreeObject(cur);
1646}
1647
1648/**
1649 * xmlXPathIdFunction:
1650 * @ctxt: the XPath Parser context
1651 *
1652 * Implement the id() XPath function
1653 * The id function selects elements by their unique ID
1654 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
1655 * then the result is the union of the result of applying id to the
1656 * string value of each of the nodes in the argument node-set. When the
1657 * argument to id is of any other type, the argument is converted to a
1658 * string as if by a call to the string function; the string is split
1659 * into a whitespace-separated list of tokens (whitespace is any sequence
1660 * of characters matching the production S); the result is a node-set
1661 * containing the elements in the same document as the context node that
1662 * have a unique ID equal to any of the tokens in the list.
1663 */
1664void
1665xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1666 CHECK_ARITY(1);
1667 TODO /* Write ID/IDREF support in libxml first */
1668}
1669
1670/**
1671 * xmlXPathLocalPartFunction:
1672 * @ctxt: the XPath Parser context
1673 *
1674 * Implement the local-part() XPath function
1675 * The local-part function returns a string containing the local part
1676 * of the name of the node in the argument node-set that is first in
1677 * document order. If the node-set is empty or the first node has no
1678 * name, an empty string is returned. If the argument is omitted it
1679 * defaults to the context node.
1680 */
1681void
1682xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1683 xmlXPathObjectPtr cur;
1684
1685 CHECK_ARITY(1);
1686 CHECK_TYPE(XPATH_NODESET);
1687 cur = valuePop(ctxt);
1688
1689 if (cur->nodesetval->nodeNr == 0) {
1690 valuePush(ctxt, xmlXPathNewString(""));
1691 } else {
1692 int i = 0; /* Should be first in document order !!!!! */
1693 valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
1694 }
1695 xmlXPathFreeObject(cur);
1696}
1697
1698/**
1699 * xmlXPathNamespaceFunction:
1700 * @ctxt: the XPath Parser context
1701 *
1702 * Implement the namespace() XPath function
1703 * The namespace function returns a string containing the namespace URI
1704 * of the expanded name of the node in the argument node-set that is
1705 * first in document order. If the node-set is empty, the first node has
1706 * no name, or the expanded name has no namespace URI, an empty string
1707 * is returned. If the argument is omitted it defaults to the context node.
1708 */
1709void
1710xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1711 xmlXPathObjectPtr cur;
1712
1713 CHECK_ARITY(1);
1714 CHECK_TYPE(XPATH_NODESET);
1715 cur = valuePop(ctxt);
1716
1717 if (cur->nodesetval->nodeNr == 0) {
1718 valuePush(ctxt, xmlXPathNewString(""));
1719 } else {
1720 int i = 0; /* Should be first in document order !!!!! */
1721
1722 if (cur->nodesetval->nodeTab[i]->ns == NULL)
1723 valuePush(ctxt, xmlXPathNewString(""));
1724 else
1725 valuePush(ctxt, xmlXPathNewString(
1726 cur->nodesetval->nodeTab[i]->ns->href));
1727 }
1728 xmlXPathFreeObject(cur);
1729}
1730
1731/**
1732 * xmlXPathNameFunction:
1733 * @ctxt: the XPath Parser context
1734 *
1735 * Implement the name() XPath function
1736 * The name function returns a string containing a QName representing
1737 * the name of the node in the argument node-set that is first in documenti
1738 * order. The QName must represent the name with respect to the namespace
1739 * declarations in effect on the node whose name is being represented.
1740 * Typically, this will be the form in which the name occurred in the XML
1741 * source. This need not be the case if there are namespace declarations
1742 * in effect on the node that associate multiple prefixes with the same
1743 * namespace. However, an implementation may include information about
1744 * the original prefix in its representation of nodes; in this case, an
1745 * implementation can ensure that the returned string is always the same
1746 * as the QName used in the XML source. If the argument it omitted it
1747 * defaults to the context node.
1748 * Libxml keep the original prefix so the "real qualified name" used is
1749 * returned.
1750 */
1751void
1752xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1753 xmlXPathObjectPtr cur;
1754
1755 CHECK_ARITY(1);
1756 CHECK_TYPE(XPATH_NODESET);
1757 cur = valuePop(ctxt);
1758
1759 if (cur->nodesetval->nodeNr == 0) {
1760 valuePush(ctxt, xmlXPathNewString(""));
1761 } else {
1762 int i = 0; /* Should be first in document order !!!!! */
1763
1764 if (cur->nodesetval->nodeTab[i]->ns == NULL)
1765 valuePush(ctxt, xmlXPathNewString(
1766 cur->nodesetval->nodeTab[i]->name));
1767
1768 else {
1769 CHAR name[2000];
1770 sprintf(name, "%s:%s", cur->nodesetval->nodeTab[i]->ns->prefix,
1771 cur->nodesetval->nodeTab[i]->name);
1772 valuePush(ctxt, xmlXPathNewString(name));
1773 }
1774 }
1775 xmlXPathFreeObject(cur);
1776}
1777
1778/**
1779 * xmlXPathStringFunction:
1780 * @ctxt: the XPath Parser context
1781 *
1782 * Implement the string() XPath function
1783 * he string function converts an object to a string as follows:
1784 * - A node-set is converted to a string by returning the value of
1785 * the node in the node-set that is first in document order.
1786 * If the node-set is empty, an empty string is returned.
1787 * - A number is converted to a string as follows
1788 * + NaN is converted to the string NaN
1789 * + positive zero is converted to the string 0
1790 * + negative zero is converted to the string 0
1791 * + positive infinity is converted to the string Infinity
1792 * + negative infinity is converted to the string -Infinity
1793 * + if the number is an integer, the number is represented in
1794 * decimal form as a Number with no decimal point and no leading
1795 * zeros, preceded by a minus sign (-) if the number is negative
1796 * + otherwise, the number is represented in decimal form as a
1797 * Number including a decimal point with at least one digit
1798 * before the decimal point and at least one digit after the
1799 * decimal point, preceded by a minus sign (-) if the number
1800 * is negative; there must be no leading zeros before the decimal
1801 * point apart possibly from the one required digit immediatelyi
1802 * before the decimal point; beyond the one required digit
1803 * after the decimal point there must be as many, but only as
1804 * many, more digits as are needed to uniquely distinguish the
1805 * number from all other IEEE 754 numeric values.
1806 * - The boolean false value is converted to the string false.
1807 * The boolean true value is converted to the string true.
1808 */
1809void
1810xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1811 xmlXPathObjectPtr cur;
1812
1813 CHECK_ARITY(1);
1814 cur = valuePop(ctxt);
1815 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
1816 switch (cur->type) {
1817 case XPATH_NODESET:
1818 if (cur->nodesetval->nodeNr == 0) {
1819 valuePush(ctxt, xmlXPathNewString(""));
1820 } else {
1821 CHAR *res;
1822 int i = 0; /* Should be first in document order !!!!! */
1823 res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
1824 valuePush(ctxt, xmlXPathNewString(res));
1825 free(res);
1826 }
1827 xmlXPathFreeObject(cur);
1828 return;
1829 case XPATH_STRING:
1830 valuePush(ctxt, cur);
1831 return;
1832 case XPATH_BOOLEAN:
1833 if (cur->boolval) valuePush(ctxt, xmlXPathNewString("true"));
1834 else valuePush(ctxt, xmlXPathNewString("false"));
1835 xmlXPathFreeObject(cur);
1836 return;
1837 case XPATH_NUMBER: {
1838 CHAR buf[100];
1839
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001840 if (isnan(cur->floatval))
1841 sprintf(buf, "NaN");
1842 else if (isinf(cur->floatval) > 0)
1843 sprintf(buf, "+Infinity");
1844 else if (isinf(cur->floatval) < 0)
1845 sprintf(buf, "-Infinity");
1846 else
1847 sprintf(buf, "%0g", cur->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001848 valuePush(ctxt, xmlXPathNewString(buf));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00001849 xmlXPathFreeObject(cur);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00001850 return;
1851 }
1852 }
1853 STRANGE
1854}
1855
1856/**
1857 * xmlXPathStringLengthFunction:
1858 * @ctxt: the XPath Parser context
1859 *
1860 * Implement the string-length() XPath function
1861 * The string-length returns the number of characters in the string
1862 * (see [3.6 Strings]). If the argument is omitted, it defaults to
1863 * the context node converted to a string, in other words the value
1864 * of the context node.
1865 */
1866void
1867xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1868 xmlXPathObjectPtr cur;
1869
1870 if (nargs == 0) {
1871 if (ctxt->context->node == NULL) {
1872 valuePush(ctxt, xmlXPathNewFloat(0));
1873 } else {
1874 CHAR *content;
1875
1876 content = xmlNodeGetContent(ctxt->context->node);
1877 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
1878 free(content);
1879 }
1880 return;
1881 }
1882 CHECK_ARITY(1);
1883 CHECK_TYPE(XPATH_STRING);
1884 cur = valuePop(ctxt);
1885 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
1886 xmlXPathFreeObject(cur);
1887}
1888
1889/**
1890 * xmlXPathConcatFunction:
1891 * @ctxt: the XPath Parser context
1892 *
1893 * Implement the concat() XPath function
1894 * The concat function returns the concatenation of its arguments.
1895 */
1896void
1897xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1898 xmlXPathObjectPtr cur, new;
1899 CHAR *tmp;
1900
1901 if (nargs < 2) {
1902 CHECK_ARITY(2);
1903 }
1904
1905 cur = valuePop(ctxt);
1906 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
1907 xmlXPathFreeObject(cur);
1908 return;
1909 }
1910 nargs--;
1911
1912 while (nargs > 0) {
1913 new = valuePop(ctxt);
1914 if ((new == NULL) || (new->type != XPATH_STRING)) {
1915 xmlXPathFreeObject(new);
1916 xmlXPathFreeObject(cur);
1917 ERROR(XPATH_INVALID_TYPE);
1918 }
1919 tmp = xmlStrcat(new->stringval, cur->stringval);
1920 new->stringval = cur->stringval;
1921 cur->stringval = tmp;
1922
1923 xmlXPathFreeObject(new);
1924 nargs--;
1925 }
1926 valuePush(ctxt, cur);
1927}
1928
1929/**
1930 * xmlXPathContainsFunction:
1931 * @ctxt: the XPath Parser context
1932 *
1933 * Implement the contains() XPath function
1934 * The contains function returns true if the first argument string
1935 * contains the second argument string, and otherwise returns false.
1936 */
1937void
1938xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1939 xmlXPathObjectPtr hay, needle;
1940
1941 CHECK_ARITY(2);
1942 CHECK_TYPE(XPATH_STRING);
1943 needle = valuePop(ctxt);
1944 hay = valuePop(ctxt);
1945 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
1946 xmlXPathFreeObject(hay);
1947 xmlXPathFreeObject(needle);
1948 ERROR(XPATH_INVALID_TYPE);
1949 }
1950 if (xmlStrstr(hay->stringval, needle->stringval))
1951 valuePush(ctxt, xmlXPathNewBoolean(1));
1952 else
1953 valuePush(ctxt, xmlXPathNewBoolean(0));
1954 xmlXPathFreeObject(hay);
1955 xmlXPathFreeObject(needle);
1956}
1957
1958/**
1959 * xmlXPathStartsWithFunction:
1960 * @ctxt: the XPath Parser context
1961 *
1962 * Implement the starts-with() XPath function
1963 * The starts-with function returns true if the first argument string
1964 * starts with the second argument string, and otherwise returns false.
1965 */
1966void
1967xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
1968 xmlXPathObjectPtr hay, needle;
1969 int n;
1970
1971 CHECK_ARITY(2);
1972 CHECK_TYPE(XPATH_STRING);
1973 needle = valuePop(ctxt);
1974 hay = valuePop(ctxt);
1975 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
1976 xmlXPathFreeObject(hay);
1977 xmlXPathFreeObject(needle);
1978 ERROR(XPATH_INVALID_TYPE);
1979 }
1980 n = xmlStrlen(needle->stringval);
1981 if (xmlStrncmp(hay->stringval, needle->stringval, n))
1982 valuePush(ctxt, xmlXPathNewBoolean(0));
1983 else
1984 valuePush(ctxt, xmlXPathNewBoolean(1));
1985 xmlXPathFreeObject(hay);
1986 xmlXPathFreeObject(needle);
1987}
1988
1989/**
1990 * xmlXPathSubstringFunction:
1991 * @ctxt: the XPath Parser context
1992 *
1993 * Implement the substring() XPath function
1994 * The substring function returns the substring of the first argument
1995 * starting at the position specified in the second argument with
1996 * length specified in the third argument. For example,
1997 * substring("12345",2,3) returns "234". If the third argument is not
1998 * specified, it returns the substring starting at the position specified
1999 * in the second argument and continuing to the end of the string. For
2000 * example, substring("12345",2) returns "2345". More precisely, each
2001 * character in the string (see [3.6 Strings]) is considered to have a
2002 * numeric position: the position of the first character is 1, the position
2003 * of the second character is 2 and so on. The returned substring contains
2004 * those characters for which the position of the character is greater than
2005 * or equal to the second argument and, if the third argument is specified,
2006 * less than the sum of the second and third arguments; the comparisons
2007 * and addition used for the above follow the standard IEEE 754 rules. Thus:
2008 * - substring("12345", 1.5, 2.6) returns "234"
2009 * - substring("12345", 0, 3) returns "12"
2010 * - substring("12345", 0 div 0, 3) returns ""
2011 * - substring("12345", 1, 0 div 0) returns ""
2012 * - substring("12345", -42, 1 div 0) returns "12345"
2013 * - substring("12345", -1 div 0, 1 div 0) returns ""
2014 */
2015void
2016xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2017 xmlXPathObjectPtr str, start, len;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002018 double le, in;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002019 int i, l;
2020 CHAR *ret;
2021
2022 /*
2023 * Conformance needs to be checked !!!!!
2024 */
2025 if (nargs < 2) {
2026 CHECK_ARITY(2);
2027 }
2028 if (nargs > 3) {
2029 CHECK_ARITY(3);
2030 }
2031 if (nargs == 3) {
2032 CHECK_TYPE(XPATH_NUMBER);
2033 len = valuePop(ctxt);
2034 le = len->floatval;
2035 xmlXPathFreeObject(len);
2036 } else {
2037 le = 2000000000;
2038 }
2039 CHECK_TYPE(XPATH_NUMBER);
2040 start = valuePop(ctxt);
2041 in = start->floatval;
2042 xmlXPathFreeObject(start);
2043 CHECK_TYPE(XPATH_STRING);
2044 str = valuePop(ctxt);
2045 le += in;
2046
2047 /* integer index of the first char */
2048 i = in;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002049 if (((double)i) != in) i++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002050
2051 /* integer index of the last char */
2052 l = le;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002053 if (((double)l) != le) l++;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002054
2055 /* back to a zero based len */
2056 i--;
2057 l--;
2058
2059 /* check against the string len */
2060 if (l > 1024) {
2061 l = xmlStrlen(str->stringval);
2062 }
2063 if (i < 0) {
2064 i = 0;
2065 }
2066
2067 /* number of chars to copy */
2068 l -= i;
2069
2070 ret = xmlStrsub(str->stringval, i, l);
2071 if (ret == NULL)
2072 valuePush(ctxt, xmlXPathNewString(""));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002073 else {
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002074 valuePush(ctxt, xmlXPathNewString(ret));
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002075 free(ret);
2076 }
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002077 xmlXPathFreeObject(str);
2078}
2079
2080/**
2081 * xmlXPathSubstringBeforeFunction:
2082 * @ctxt: the XPath Parser context
2083 *
2084 * Implement the substring-before() XPath function
2085 * The substring-before function returns the substring of the first
2086 * argument string that precedes the first occurrence of the second
2087 * argument string in the first argument string, or the empty string
2088 * if the first argument string does not contain the second argument
2089 * string. For example, substring-before("1999/04/01","/") returns 1999.
2090 */
2091void
2092xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2093 CHECK_ARITY(2);
2094 TODO /* substring before */
2095}
2096
2097/**
2098 * xmlXPathSubstringAfterFunction:
2099 * @ctxt: the XPath Parser context
2100 *
2101 * Implement the substring-after() XPath function
2102 * The substring-after function returns the substring of the first
2103 * argument string that follows the first occurrence of the second
2104 * argument string in the first argument string, or the empty stringi
2105 * if the first argument string does not contain the second argument
2106 * string. For example, substring-after("1999/04/01","/") returns 04/01,
2107 * and substring-after("1999/04/01","19") returns 99/04/01.
2108 */
2109void
2110xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2111 CHECK_ARITY(2);
2112 TODO /* substring after */
2113}
2114
2115/**
2116 * xmlXPathNormalizeFunction:
2117 * @ctxt: the XPath Parser context
2118 *
2119 * Implement the normalize() XPath function
2120 * The normalize function returns the argument string with white
2121 * space normalized by stripping leading and trailing whitespace
2122 * and replacing sequences of whitespace characters by a single
2123 * space. Whitespace characters are the same allowed by the S production
2124 * in XML. If the argument is omitted, it defaults to the context
2125 * node converted to a string, in other words the value of the context node.
2126 */
2127void
2128xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2129 CHECK_ARITY(1);
2130 TODO /* normalize isn't as boring as translate, but pretty much */
2131}
2132
2133/**
2134 * xmlXPathTranslateFunction:
2135 * @ctxt: the XPath Parser context
2136 *
2137 * Implement the translate() XPath function
2138 * The translate function returns the first argument string with
2139 * occurrences of characters in the second argument string replaced
2140 * by the character at the corresponding position in the third argument
2141 * string. For example, translate("bar","abc","ABC") returns the string
2142 * BAr. If there is a character in the second argument string with no
2143 * character at a corresponding position in the third argument string
2144 * (because the second argument string is longer than the third argument
2145 * string), then occurrences of that character in the first argument
2146 * string are removed. For example, translate("--aaa--","abc-","ABC")
2147 * returns "AAA". If a character occurs more than once in second
2148 * argument string, then the first occurrence determines the replacement
2149 * character. If the third argument string is longer than the second
2150 * argument string, then excess characters are ignored.
2151 */
2152void
2153xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2154 CHECK_ARITY(3);
2155 TODO /* translate is boring, waiting for UTF-8 representation too */
2156}
2157
2158/**
2159 * xmlXPathBooleanFunction:
2160 * @ctxt: the XPath Parser context
2161 *
2162 * Implement the boolean() XPath function
2163 * he boolean function converts its argument to a boolean as follows:
2164 * - a number is true if and only if it is neither positive or
2165 * negative zero nor NaN
2166 * - a node-set is true if and only if it is non-empty
2167 * - a string is true if and only if its length is non-zero
2168 */
2169void
2170xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2171 xmlXPathObjectPtr cur;
2172 int res = 0;
2173
2174 CHECK_ARITY(1);
2175 cur = valuePop(ctxt);
2176 if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
2177 switch (cur->type) {
2178 case XPATH_NODESET:
2179 if ((cur->nodesetval == NULL) ||
2180 (cur->nodesetval->nodeNr == 0)) res = 0;
2181 else
2182 res = 1;
2183 break;
2184 case XPATH_STRING:
2185 if ((cur->stringval == NULL) ||
2186 (cur->stringval[0] == 0)) res = 0;
2187 else
2188 res = 1;
2189 break;
2190 case XPATH_BOOLEAN:
2191 valuePush(ctxt, cur);
2192 return;
2193 case XPATH_NUMBER:
2194 if (cur->floatval) res = 1;
2195 break;
2196 default:
2197 STRANGE
2198 }
2199 xmlXPathFreeObject(cur);
2200 valuePush(ctxt, xmlXPathNewBoolean(res));
2201}
2202
2203/**
2204 * xmlXPathNotFunction:
2205 * @ctxt: the XPath Parser context
2206 *
2207 * Implement the not() XPath function
2208 * The not function returns true if its argument is false,
2209 * and false otherwise.
2210 */
2211void
2212xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2213 CHECK_ARITY(1);
2214 CHECK_TYPE(XPATH_BOOLEAN);
2215 ctxt->value->boolval = ! ctxt->value->boolval;
2216}
2217
2218/**
2219 * xmlXPathTrueFunction:
2220 * @ctxt: the XPath Parser context
2221 *
2222 * Implement the true() XPath function
2223 */
2224void
2225xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2226 CHECK_ARITY(0);
2227 valuePush(ctxt, xmlXPathNewBoolean(1));
2228}
2229
2230/**
2231 * xmlXPathFalseFunction:
2232 * @ctxt: the XPath Parser context
2233 *
2234 * Implement the false() XPath function
2235 */
2236void
2237xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2238 CHECK_ARITY(0);
2239 valuePush(ctxt, xmlXPathNewBoolean(0));
2240}
2241
2242/**
2243 * xmlXPathLangFunction:
2244 * @ctxt: the XPath Parser context
2245 *
2246 * Implement the lang() XPath function
2247 * The lang function returns true or false depending on whether the
2248 * language of the context node as specified by xml:lang attributes
2249 * is the same as or is a sublanguage of the language specified by
2250 * the argument string. The language of the context node is determined
2251 * by the value of the xml:lang attribute on the context node, or, if
2252 * the context node has no xml:lang attribute, by the value of the
2253 * xml:lang attribute on the nearest ancestor of the context node that
2254 * has an xml:lang attribute. If there is no such attribute, then lang
2255 * returns false. If there is such an attribute, then lang returns
2256 * true if the attribute value is equal to the argument ignoring case,
2257 * or if there is some suffix starting with - such that the attribute
2258 * value is equal to the argument ignoring that suffix of the attribute
2259 * value and ignoring case.
2260 */
2261void
2262xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2263 CHECK_ARITY(0);
2264 TODO /* Write down xml:lang support in libxml first */
2265}
2266
2267/**
2268 * xmlXPathNumberFunction:
2269 * @ctxt: the XPath Parser context
2270 *
2271 * Implement the number() XPath function
2272 */
2273void
2274xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2275 xmlXPathObjectPtr cur;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002276 double res;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002277
2278 CHECK_ARITY(1);
2279 cur = valuePop(ctxt);
2280 switch (cur->type) {
2281 case XPATH_NODESET:
2282 valuePush(ctxt, cur);
2283 xmlXPathStringFunction(ctxt, 1);
2284 cur = valuePop(ctxt);
2285 case XPATH_STRING:
2286 res = xmlXPathStringEvalNumber(cur->stringval);
2287 valuePush(ctxt, xmlXPathNewFloat(res));
2288 xmlXPathFreeObject(cur);
2289 return;
2290 case XPATH_BOOLEAN:
2291 if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
2292 else valuePush(ctxt, xmlXPathNewFloat(0.0));
2293 xmlXPathFreeObject(cur);
2294 return;
2295 case XPATH_NUMBER:
2296 valuePush(ctxt, cur);
2297 return;
2298 }
2299 STRANGE
2300}
2301
2302/**
2303 * xmlXPathSumFunction:
2304 * @ctxt: the XPath Parser context
2305 *
2306 * Implement the sum() XPath function
2307 * The sum function returns the sum of the values of the nodes in
2308 * the argument node-set.
2309 */
2310void
2311xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2312 CHECK_ARITY(1);
2313 TODO /* BUG Sum : don't understand the definition */
2314}
2315
2316/**
2317 * xmlXPathFloorFunction:
2318 * @ctxt: the XPath Parser context
2319 *
2320 * Implement the floor() XPath function
2321 * The floor function returns the largest (closest to positive infinity)
2322 * number that is not greater than the argument and that is an integer.
2323 */
2324void
2325xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
2326 CHECK_ARITY(1);
2327 CHECK_TYPE(XPATH_NUMBER);
2328 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002329 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002330}
2331
2332/**
2333 * xmlXPathCeilingFunction:
2334 * @ctxt: the XPath Parser context
2335 *
2336 * Implement the ceiling() XPath function
2337 * The ceiling function returns the smallest (closest to negative infinity)
2338 * number that is not less than the argument and that is an integer.
2339 */
2340void
2341xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002342 double f;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002343
2344 CHECK_ARITY(1);
2345 CHECK_TYPE(XPATH_NUMBER);
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002346 f = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002347 if (f != ctxt->value->floatval)
2348 ctxt->value->floatval = f + 1;
2349}
2350
2351/**
2352 * xmlXPathRoundFunction:
2353 * @ctxt: the XPath Parser context
2354 *
2355 * Implement the round() XPath function
2356 * The round function returns the number that is closest to the
2357 * argument and that is an integer. If there are two such numbers,
2358 * then the one that is even is returned.
2359 */
2360void
2361xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002362 double f;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002363
2364 CHECK_ARITY(1);
2365 CHECK_TYPE(XPATH_NUMBER);
2366 /* round(0.50000001) => 0 !!!!! */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002367 f = (double)((int) ctxt->value->floatval);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002368 if (ctxt->value->floatval < f + 0.5)
2369 ctxt->value->floatval = f;
2370 else if (ctxt->value->floatval == f + 0.5)
2371 ctxt->value->floatval = f; /* !!!! Not following the spec here */
2372 else
2373 ctxt->value->floatval = f + 1;
2374}
2375
2376/************************************************************************
2377 * *
2378 * The Parser *
2379 * *
2380 ************************************************************************/
2381
2382/*
2383 * a couple of forward declarations since we use a recursive call based
2384 * implementation.
2385 */
2386void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
2387void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
2388void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
2389void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
2390
2391/**
2392 * xmlXPathParseNCName:
2393 * @ctxt: the XPath Parser context
2394 *
2395 * parse an XML namespace non qualified name.
2396 *
2397 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
2398 *
2399 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
2400 * CombiningChar | Extender
2401 *
2402 * Returns the namespace name or NULL
2403 */
2404
2405CHAR *
2406xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
2407 const CHAR *q;
2408 CHAR *ret = NULL;
2409
2410 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
2411 q = NEXT;
2412
2413 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
2414 (CUR == '.') || (CUR == '-') ||
2415 (CUR == '_') ||
2416 (IS_COMBINING(CUR)) ||
2417 (IS_EXTENDER(CUR)))
2418 NEXT;
2419
2420 ret = xmlStrndup(q, CUR_PTR - q);
2421
2422 return(ret);
2423}
2424
2425/**
2426 * xmlXPathParseQName:
2427 * @ctxt: the XPath Parser context
2428 * @prefix: a CHAR **
2429 *
2430 * parse an XML qualified name
2431 *
2432 * [NS 5] QName ::= (Prefix ':')? LocalPart
2433 *
2434 * [NS 6] Prefix ::= NCName
2435 *
2436 * [NS 7] LocalPart ::= NCName
2437 *
2438 * Returns the function returns the local part, and prefix is updated
2439 * to get the Prefix if any.
2440 */
2441
2442CHAR *
2443xmlXPathParseQName(xmlXPathParserContextPtr ctxt, CHAR **prefix) {
2444 CHAR *ret = NULL;
2445
2446 *prefix = NULL;
2447 ret = xmlXPathParseNCName(ctxt);
2448 if (CUR == ':') {
2449 *prefix = ret;
2450 NEXT;
2451 ret = xmlXPathParseNCName(ctxt);
2452 }
2453 return(ret);
2454}
2455
2456/**
2457 * xmlXPathStringEvalNumber:
2458 * @str: A string to scan
2459 *
2460 * [30] Number ::= Digits ('.' Digits)?
2461 * | '.' Digits
2462 * [31] Digits ::= [0-9]+
2463 *
2464 * Parse and evaluate a Number in the string
2465 *
2466 * BUG: "1.' is not valid ... James promised correction
2467 * as Digits ('.' Digits?)?
2468 *
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002469 * Returns the double value.
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002470 */
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002471double
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002472xmlXPathStringEvalNumber(const CHAR *str) {
2473 const CHAR *cur = str;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002474 double ret = 0.0;
2475 double mult = 1;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002476 int ok = 0;
2477
2478 while (*cur == ' ') cur++;
2479 if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002480 return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002481 }
2482 while ((*cur >= '0') && (*cur <= '9')) {
2483 ret = ret * 10 + (*cur - '0');
2484 ok = 1;
2485 cur++;
2486 }
2487 if (*cur == '.') {
2488 cur++;
2489 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002490 return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002491 }
2492 while ((*cur >= '0') && (*cur <= '9')) {
2493 mult /= 10;
2494 ret = ret + (*cur - '0') * mult;
2495 cur++;
2496 }
2497 }
2498 while (*cur == ' ') cur++;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002499 if (*cur != 0) return(xmlXPathNAN);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002500 return(ret);
2501}
2502
2503/**
2504 * xmlXPathEvalNumber:
2505 * @ctxt: the XPath Parser context
2506 *
2507 * [30] Number ::= Digits ('.' Digits)?
2508 * | '.' Digits
2509 * [31] Digits ::= [0-9]+
2510 *
2511 * Parse and evaluate a Number, then push it on the stack
2512 *
2513 * BUG: "1.' is not valid ... James promised correction
2514 * as Digits ('.' Digits?)?
2515 */
2516void
2517xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002518 double ret = 0.0;
2519 double mult = 1;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002520 int ok = 0;
2521
2522 CHECK_ERROR;
2523 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
2524 ERROR(XPATH_NUMBER_ERROR);
2525 }
2526 while ((CUR >= '0') && (CUR <= '9')) {
2527 ret = ret * 10 + (CUR - '0');
2528 ok = 1;
2529 NEXT;
2530 }
2531 if (CUR == '.') {
2532 NEXT;
2533 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
2534 ERROR(XPATH_NUMBER_ERROR);
2535 }
2536 while ((CUR >= '0') && (CUR <= '9')) {
2537 mult /= 10;
2538 ret = ret + (CUR - '0') * mult;
2539 NEXT;
2540 }
2541 }
2542 valuePush(ctxt, xmlXPathNewFloat(ret));
2543}
2544
2545/**
2546 * xmlXPathEvalLiteral:
2547 * @ctxt: the XPath Parser context
2548 *
2549 * Parse a Literal and push it on the stack.
2550 *
2551 * [29] Literal ::= '"' [^"]* '"'
2552 * | "'" [^']* "'"
2553 *
2554 * TODO: memory allocation could be improved.
2555 */
2556void
2557xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
2558 const CHAR *q;
2559 CHAR *ret = NULL;
2560
2561 if (CUR == '"') {
2562 NEXT;
2563 q = CUR_PTR;
2564 while ((IS_CHAR(CUR)) && (CUR != '"'))
2565 NEXT;
2566 if (!IS_CHAR(CUR)) {
2567 ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
2568 } else {
2569 ret = xmlStrndup(q, CUR_PTR - q);
2570 NEXT;
2571 }
2572 } else if (CUR == '\'') {
2573 NEXT;
2574 q = CUR_PTR;
2575 while ((IS_CHAR(CUR)) && (CUR != '\''))
2576 NEXT;
2577 if (!IS_CHAR(CUR)) {
2578 ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
2579 } else {
2580 ret = xmlStrndup(q, CUR_PTR - q);
2581 NEXT;
2582 }
2583 } else {
2584 ERROR(XPATH_START_LITERAL_ERROR);
2585 }
2586 if (ret == NULL) return;
2587 valuePush(ctxt, xmlXPathNewString(ret));
2588 free(ret);
2589}
2590
2591/**
2592 * xmlXPathEvalVariableReference:
2593 * @ctxt: the XPath Parser context
2594 *
2595 * Parse a VariableReference, evaluate it and push it on the stack.
2596 *
2597 * The variable bindings consist of a mapping from variable names
2598 * to variable values. The value of a variable is an object, which
2599 * of any of the types that are possible for the value of an expression,
2600 * and may also be of additional types not specified here.
2601 *
2602 * Early evaluation is possible since:
2603 * The variable bindings [...] used to evaluate a subexpression are
2604 * always the same as those used to evaluate the containing expression.
2605 *
2606 * [36] VariableReference ::= '$' QName
2607 */
2608void
2609xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
2610 CHAR *name;
2611 CHAR *prefix;
2612 xmlXPathObjectPtr value;
2613
2614 if (CUR != '$') {
2615 ERROR(XPATH_VARIABLE_REF_ERROR);
2616 }
2617 name = xmlXPathParseQName(ctxt, &prefix);
2618 if (name == NULL) {
2619 ERROR(XPATH_VARIABLE_REF_ERROR);
2620 }
2621 value = xmlXPathVariablelookup(ctxt, prefix, name);
2622 if (value == NULL) {
2623 ERROR(XPATH_UNDEF_VARIABLE_ERROR);
2624 }
2625 valuePush(ctxt, value);
2626 if (prefix != NULL) free(prefix);
2627 free(name);
2628}
2629
2630
2631/**
2632 * xmlXPathFunctionLookup:
2633 * @ctxt: the XPath Parser context
2634 * @name: a name string
2635 *
2636 * Search for a function of the given name
2637 *
2638 * [35] FunctionName ::= QName - NodeType
2639 *
2640 * TODO: for the moment the list is hardcoded from the spec !!!!
2641 *
2642 * Returns the xmlXPathFunction if found, or NULL otherwise
2643 */
2644xmlXPathFunction
2645xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, CHAR *name) {
2646 switch (name[0]) {
2647 case 'b':
2648 if (!xmlStrcmp(name, "boolean"))
2649 return(xmlXPathBooleanFunction);
2650 break;
2651 case 'c':
2652 if (!xmlStrcmp(name, "ceiling"))
2653 return(xmlXPathCeilingFunction);
2654 if (!xmlStrcmp(name, "count"))
2655 return(xmlXPathCountFunction);
2656 if (!xmlStrcmp(name, "concat"))
2657 return(xmlXPathConcatFunction);
2658 if (!xmlStrcmp(name, "contains"))
2659 return(xmlXPathContainsFunction);
2660 break;
2661 case 'i':
2662 if (!xmlStrcmp(name, "id"))
2663 return(xmlXPathIdFunction);
2664 break;
2665 case 'f':
2666 if (!xmlStrcmp(name, "false"))
2667 return(xmlXPathFalseFunction);
2668 if (!xmlStrcmp(name, "floor"))
2669 return(xmlXPathFloorFunction);
2670 break;
2671 case 'l':
2672 if (!xmlStrcmp(name, "last"))
2673 return(xmlXPathLastFunction);
2674 if (!xmlStrcmp(name, "lang"))
2675 return(xmlXPathLangFunction);
2676 if (!xmlStrcmp(name, "local-part"))
2677 return(xmlXPathLocalPartFunction);
2678 break;
2679 case 'n':
2680 if (!xmlStrcmp(name, "not"))
2681 return(xmlXPathNotFunction);
2682 if (!xmlStrcmp(name, "name"))
2683 return(xmlXPathNameFunction);
2684 if (!xmlStrcmp(name, "namespace"))
2685 return(xmlXPathNamespaceFunction);
2686 if (!xmlStrcmp(name, "normalize"))
2687 return(xmlXPathNormalizeFunction);
2688 if (!xmlStrcmp(name, "number"))
2689 return(xmlXPathNumberFunction);
2690 break;
2691 case 'p':
2692 if (!xmlStrcmp(name, "position"))
2693 return(xmlXPathPositionFunction);
2694 break;
2695 case 'r':
2696 if (!xmlStrcmp(name, "round"))
2697 return(xmlXPathRoundFunction);
2698 break;
2699 case 's':
2700 if (!xmlStrcmp(name, "string"))
2701 return(xmlXPathStringFunction);
2702 if (!xmlStrcmp(name, "string-length"))
2703 return(xmlXPathStringLengthFunction);
2704 if (!xmlStrcmp(name, "starts-with"))
2705 return(xmlXPathStartsWithFunction);
2706 if (!xmlStrcmp(name, "substring"))
2707 return(xmlXPathSubstringFunction);
2708 if (!xmlStrcmp(name, "substring-before"))
2709 return(xmlXPathSubstringBeforeFunction);
2710 if (!xmlStrcmp(name, "substring-after"))
2711 return(xmlXPathSubstringAfterFunction);
2712 if (!xmlStrcmp(name, "sum"))
2713 return(xmlXPathSumFunction);
2714 break;
2715 case 't':
2716 if (!xmlStrcmp(name, "true"))
2717 return(xmlXPathTrueFunction);
2718 if (!xmlStrcmp(name, "translate"))
2719 return(xmlXPathTranslateFunction);
2720 break;
2721 }
2722 return(NULL);
2723}
2724
2725/**
2726 * xmlXPathEvalLocationPathName:
2727 * @ctxt: the XPath Parser context
2728 * @name: a name string
2729 *
2730 * Various names in the beginning of a LocationPath expression
2731 * indicate whether that's an Axis, a node type,
2732 *
2733 * [6] AxisName ::= 'ancestor'
2734 * | 'ancestor-or-self'
2735 * | 'attribute'
2736 * | 'child'
2737 * | 'descendant'
2738 * | 'descendant-or-self'
2739 * | 'following'
2740 * | 'following-sibling'
2741 * | 'namespace'
2742 * | 'parent'
2743 * | 'preceding'
2744 * | 'preceding-sibling'
2745 * | 'self'
2746 * [38] NodeType ::= 'comment'
2747 * | 'text'
2748 * | 'processing-instruction'
2749 * | 'node'
2750 */
2751int
2752xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, CHAR *name) {
2753 switch (name[0]) {
2754 case 'a':
2755 if (!xmlStrcmp(name, "ancestor")) return(AXIS_ANCESTOR);
2756 if (!xmlStrcmp(name, "ancestor-or-self")) return(AXIS_ANCESTOR_OR_SELF);
2757 if (!xmlStrcmp(name, "attribute")) return(AXIS_ATTRIBUTE);
2758 break;
2759 case 'c':
2760 if (!xmlStrcmp(name, "child")) return(AXIS_CHILD);
2761 if (!xmlStrcmp(name, "comment")) return(NODE_TYPE_COMMENT);
2762 break;
2763 case 'd':
2764 if (!xmlStrcmp(name, "descendant")) return(AXIS_DESCENDANT);
2765 if (!xmlStrcmp(name, "descendant-or-self")) return(AXIS_DESCENDANT_OR_SELF);
2766 break;
2767 case 'f':
2768 if (!xmlStrcmp(name, "following")) return(AXIS_FOLLOWING);
2769 if (!xmlStrcmp(name, "following-sibling")) return(AXIS_FOLLOWING_SIBLING);
2770 break;
2771 case 'n':
2772 if (!xmlStrcmp(name, "namespace")) return(AXIS_NAMESPACE);
2773 if (!xmlStrcmp(name, "node")) return(NODE_TYPE_NODE);
2774 break;
2775 case 'p':
2776 if (!xmlStrcmp(name, "parent")) return(AXIS_PARENT);
2777 if (!xmlStrcmp(name, "preceding")) return(AXIS_PRECEDING);
2778 if (!xmlStrcmp(name, "preceding-sibling")) return(AXIS_PRECEDING_SIBLING);
2779 if (!xmlStrcmp(name, "processing-instruction")) return(NODE_TYPE_PI);
2780 break;
2781 case 's':
2782 if (!xmlStrcmp(name, "self")) return(AXIS_SELF);
2783 break;
2784 case 't':
2785 if (!xmlStrcmp(name, "text")) return(NODE_TYPE_TEXT);
2786 break;
2787 }
2788 if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION);
2789 return(0);
2790}
2791
2792/**
2793 * xmlXPathEvalFunctionCall:
2794 * @ctxt: the XPath Parser context
2795 *
2796 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
2797 * [17] Argument ::= Expr
2798 *
2799 * Parse and evaluate a function call, the evaluation of all arguments are
2800 * pushed on the stack
2801 */
2802void
2803xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
2804 CHAR *name;
2805 CHAR *prefix;
2806 xmlXPathFunction func;
2807 int nbargs = 0;
2808
2809 name = xmlXPathParseQName(ctxt, &prefix);
2810 if (name == NULL) {
2811 ERROR(XPATH_EXPR_ERROR);
2812 }
2813 func = xmlXPathIsFunction(ctxt, name);
2814 if (func == NULL) {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002815 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002816 ERROR(XPATH_UNKNOWN_FUNC_ERROR);
2817 }
2818#ifdef DEBUG_EXPR
2819 fprintf(xmlXPathDebug, "Calling function %s\n", name);
2820#endif
2821
2822 if (CUR != '(') {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002823 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002824 ERROR(XPATH_EXPR_ERROR);
2825 }
2826 NEXT;
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002827
2828 while (CUR != ')') {
2829 xmlXPathEvalExpr(ctxt);
2830 nbargs++;
2831 if (CUR == ')') break;
2832 if (CUR != ',') {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002833 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002834 ERROR(XPATH_EXPR_ERROR);
2835 }
2836 NEXT;
2837 }
2838 NEXT;
Daniel Veillarde2d034d1999-07-27 19:52:06 +00002839 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00002840 func(ctxt, nbargs);
2841}
2842
2843/**
2844 * xmlXPathEvalPrimaryExpr:
2845 * @ctxt: the XPath Parser context
2846 *
2847 * [15] PrimaryExpr ::= VariableReference
2848 * | '(' Expr ')'
2849 * | Literal
2850 * | Number
2851 * | FunctionCall
2852 *
2853 * Parse and evaluate a primary expression, then push the result on the stack
2854 */
2855void
2856xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
2857 if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
2858 else if (CUR == '(') {
2859 NEXT;
2860 xmlXPathEvalExpr(ctxt);
2861 if (CUR != ')') {
2862 ERROR(XPATH_EXPR_ERROR);
2863 }
2864 NEXT;
2865 } else if (IS_DIGIT(CUR)) {
2866 xmlXPathEvalNumber(ctxt);
2867 } else if ((CUR == '\'') || (CUR == '"')) {
2868 xmlXPathEvalLiteral(ctxt);
2869 } else {
2870 xmlXPathEvalFunctionCall(ctxt);
2871 }
2872}
2873
2874/**
2875 * xmlXPathEvalFilterExpr:
2876 * @ctxt: the XPath Parser context
2877 *
2878 * [20] FilterExpr ::= PrimaryExpr
2879 * | FilterExpr Predicate
2880 *
2881 * Parse and evaluate a filter expression, then push the result on the stack
2882 * Square brackets are used to filter expressions in the same way that
2883 * they are used in location paths. It is an error if the expression to
2884 * be filtered does not evaluate to a node-set. The context node list
2885 * used for evaluating the expression in square brackets is the node-set
2886 * to be filtered listed in document order.
2887 */
2888
2889void
2890xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
2891 /****
2892 xmlNodeSetPtr oldset = NULL;
2893 xmlXPathObjectPtr arg;
2894 ****/
2895
2896 xmlXPathEvalPrimaryExpr(ctxt);
2897 CHECK_ERROR;
2898
2899 if (CUR != '[') return;
2900
2901 CHECK_TYPE(XPATH_NODESET);
2902
2903 /******
2904 TODO: transition from PathExpr to Expr using paths ...
2905 arg = valuePop(ctxt);
2906 oldset = ctxt->context->nodeset;
2907 ctxt->context->nodeset = arg->nodesetval;
2908 ******/
2909
2910 while (CUR == '[') {
2911 xmlXPathEvalPredicate(ctxt);
2912 }
2913
2914
2915}
2916
2917/**
2918 * xmlXPathEvalPathExpr:
2919 * @ctxt: the XPath Parser context
2920 *
2921 * [19] PathExpr ::= LocationPath
2922 * | FilterExpr
2923 * | FilterExpr '/' RelativeLocationPath
2924 * | FilterExpr '//' RelativeLocationPath
2925 *
2926 * Parse and evaluate a path expression, then push the result on the stack
2927 * The / operator and // operators combine an arbitrary expression
2928 * and a relative location path. It is an error if the expression
2929 * does not evaluate to a node-set.
2930 * The / operator does composition in the same way as when / is
2931 * used in a location path. As in location paths, // is short for
2932 * /descendant-or-self::node()/.
2933 */
2934
2935void
2936xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
2937 xmlNodeSetPtr newset = NULL;
2938
2939 /*
2940 * TODO: FilterExpr => PrimaryExpr => FunctionName is possible too
2941 * so we may have to parse a name here, and depending on whether
2942 * it's a function name or not parse a FilterExpr or a LocationPath
2943 * !!!!!!!!!!!! See NOTE1
2944 */
2945
2946 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
2947 (CUR == '\'') || (CUR == '"')) {
2948 xmlXPathEvalFilterExpr(ctxt);
2949 CHECK_ERROR;
2950 if ((CUR == '/') && (NXT(1) == '/')) {
2951 SKIP(2);
2952 if (ctxt->context->nodelist == NULL) {
2953 STRANGE
2954 xmlXPathRoot(ctxt);
2955 }
2956 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
2957 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
2958 if (ctxt->context->nodelist != NULL)
2959 xmlXPathFreeNodeSet(ctxt->context->nodelist);
2960 ctxt->context->nodelist = newset;
2961 ctxt->context->node = NULL;
2962 xmlXPathEvalRelativeLocationPath(ctxt);
2963 } else if (CUR == '/') {
2964 xmlXPathEvalRelativeLocationPath(ctxt);
2965 }
2966 } else {
2967 xmlXPathEvalLocationPath(ctxt);
2968 }
2969}
2970
2971/**
2972 * xmlXPathEvalUnionExpr:
2973 * @ctxt: the XPath Parser context
2974 *
2975 * [18] UnionExpr ::= PathExpr
2976 * | UnionExpr '|' PathExpr
2977 *
2978 * Parse and evaluate an union expression, then push the result on the stack
2979 */
2980
2981void
2982xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
2983 xmlXPathEvalPathExpr(ctxt);
2984 CHECK_ERROR;
2985 if (CUR == '|') {
2986 xmlNodeSetPtr old = ctxt->context->nodelist;
2987
2988 xmlXPathEvalPathExpr(ctxt);
2989
2990 if (ctxt->context->nodelist == NULL)
2991 ctxt->context->nodelist = old;
2992 else {
2993 ctxt->context->nodelist =
2994 xmlXPathNodeSetMerge(ctxt->context->nodelist, old);
2995 xmlXPathFreeNodeSet(old);
2996 }
2997 }
2998}
2999
3000/**
3001 * xmlXPathEvalUnaryExpr:
3002 * @ctxt: the XPath Parser context
3003 *
3004 * [27] UnaryExpr ::= UnionExpr
3005 * | '-' UnaryExpr
3006 *
3007 * Parse and evaluate an unary expression, then push the result on the stack
3008 */
3009
3010void
3011xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
3012 int minus = 0;
3013
3014 if (CUR == '-') {
3015 minus = 1;
3016 NEXT;
3017 }
3018 /* xmlXPathEvalUnionExpr(ctxt); NOTE1 !!! */
3019 xmlXPathEvalPrimaryExpr(ctxt);
3020 CHECK_ERROR;
3021 if (minus) {
3022 xmlXPathValueFlipSign(ctxt);
3023 }
3024}
3025
3026/**
3027 * xmlXPathEvalMultiplicativeExpr:
3028 * @ctxt: the XPath Parser context
3029 *
3030 * [26] MultiplicativeExpr ::= UnaryExpr
3031 * | MultiplicativeExpr MultiplyOperator UnaryExpr
3032 * | MultiplicativeExpr 'div' UnaryExpr
3033 * | MultiplicativeExpr 'mod' UnaryExpr
3034 * [34] MultiplyOperator ::= '*'
3035 *
3036 * Parse and evaluate an Additive expression, then push the result on the stack
3037 */
3038
3039void
3040xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
3041 xmlXPathEvalUnaryExpr(ctxt);
3042 CHECK_ERROR;
3043 while ((CUR == '*') ||
3044 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
3045 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
3046 int op = -1;
3047
3048 if (CUR == '*') {
3049 op = 0;
3050 NEXT;
3051 } else if (CUR == 'd') {
3052 op = 1;
3053 SKIP(3);
3054 } else if (CUR == 'm') {
3055 op = 2;
3056 SKIP(3);
3057 }
3058 xmlXPathEvalUnaryExpr(ctxt);
3059 CHECK_ERROR;
3060 switch (op) {
3061 case 0:
3062 xmlXPathMultValues(ctxt);
3063 break;
3064 case 1:
3065 xmlXPathDivValues(ctxt);
3066 break;
3067 case 2:
3068 xmlXPathModValues(ctxt);
3069 break;
3070 }
3071 }
3072}
3073
3074/**
3075 * xmlXPathEvalAdditiveExpr:
3076 * @ctxt: the XPath Parser context
3077 *
3078 * [25] AdditiveExpr ::= MultiplicativeExpr
3079 * | AdditiveExpr '+' MultiplicativeExpr
3080 * | AdditiveExpr '-' MultiplicativeExpr
3081 *
3082 * Parse and evaluate an Additive expression, then push the result on the stack
3083 */
3084
3085void
3086xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
3087 xmlXPathEvalMultiplicativeExpr(ctxt);
3088 CHECK_ERROR;
3089 while ((CUR == '+') || (CUR == '-')) {
3090 int plus;
3091
3092 if (CUR == '+') plus = 1;
3093 else plus = 0;
3094 NEXT;
3095 xmlXPathEvalMultiplicativeExpr(ctxt);
3096 CHECK_ERROR;
3097 if (plus) xmlXPathAddValues(ctxt);
3098 else xmlXPathSubValues(ctxt);
3099 }
3100}
3101
3102/**
3103 * xmlXPathEvalRelationalExpr:
3104 * @ctxt: the XPath Parser context
3105 *
3106 * [24] RelationalExpr ::= AdditiveExpr
3107 * | RelationalExpr '<' AdditiveExpr
3108 * | RelationalExpr '>' AdditiveExpr
3109 * | RelationalExpr '<=' AdditiveExpr
3110 * | RelationalExpr '>=' AdditiveExpr
3111 *
3112 * A <= B > C is allowed ? Answer from James, yes with
3113 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
3114 * which is basically what got implemented.
3115 *
3116 * Parse and evaluate a Relational expression, then push the result
3117 * on the stack
3118 */
3119
3120void
3121xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
3122 xmlXPathEvalAdditiveExpr(ctxt);
3123 CHECK_ERROR;
3124 while ((CUR == '<') ||
3125 (CUR == '>') ||
3126 ((CUR == '<') && (NXT(1) == '=')) ||
3127 ((CUR == '>') && (NXT(1) == '='))) {
3128 xmlXPathObjectPtr arg1, arg2, res;
3129 int inf, strict, equal;
3130
3131 if (CUR == '<') inf = 1;
3132 else inf = 0;
3133 if (NXT(1) == '=') strict = 0;
3134 else strict = 1;
3135 NEXT;
3136 if (!strict) NEXT;
3137 xmlXPathEvalAdditiveExpr(ctxt);
3138 CHECK_ERROR;
3139 arg2 = valuePop(ctxt);
3140 arg1 = valuePop(ctxt);
3141 equal = xmlXPathCompareValues(inf, strict, arg1, arg2);
3142 res = xmlXPathNewBoolean(equal);
3143 valuePush(ctxt, res);
3144 xmlXPathFreeObject(arg1);
3145 xmlXPathFreeObject(arg2);
3146 }
3147}
3148
3149/**
3150 * xmlXPathEvalEqualityExpr:
3151 * @ctxt: the XPath Parser context
3152 *
3153 * [23] EqualityExpr ::= RelationalExpr
3154 * | EqualityExpr '=' RelationalExpr
3155 * | EqualityExpr '!=' RelationalExpr
3156 *
3157 * A != B != C is allowed ? Answer from James, yes with
3158 * (RelationalExpr = RelationalExpr) = RelationalExpr
3159 * (RelationalExpr != RelationalExpr) != RelationalExpr
3160 * which is basically what got implemented.
3161 *
3162 * Parse and evaluate an Equality expression, then push the result on the stack
3163 *
3164 */
3165void
3166xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
3167 xmlXPathEvalRelationalExpr(ctxt);
3168 CHECK_ERROR;
3169 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
3170 xmlXPathObjectPtr arg1, arg2, res;
3171 int eq, equal;
3172
3173 if (CUR == '=') eq = 1;
3174 else eq = 0;
3175 NEXT;
3176 if (!eq) NEXT;
3177 xmlXPathEvalRelationalExpr(ctxt);
3178 CHECK_ERROR;
3179 arg2 = valuePop(ctxt);
3180 arg1 = valuePop(ctxt);
3181 equal = xmlXPathEqualValues(arg1, arg2);
3182 if (eq) res = xmlXPathNewBoolean(equal);
3183 else res = xmlXPathNewBoolean(!equal);
3184 valuePush(ctxt, res);
3185 xmlXPathFreeObject(arg1);
3186 xmlXPathFreeObject(arg2);
3187 }
3188}
3189
3190/**
3191 * xmlXPathEvalAndExpr:
3192 * @ctxt: the XPath Parser context
3193 *
3194 * [22] AndExpr ::= EqualityExpr
3195 * | AndExpr 'and' EqualityExpr
3196 *
3197 * Parse and evaluate an AND expression, then push the result on the stack
3198 *
3199 */
3200void
3201xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
3202 xmlXPathEvalEqualityExpr(ctxt);
3203 CHECK_ERROR;
3204 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) {
3205 xmlXPathObjectPtr arg1, arg2;
3206
3207 SKIP(3);
3208 xmlXPathEvalEqualityExpr(ctxt);
3209 CHECK_ERROR;
3210 arg2 = valuePop(ctxt);
3211 arg1 = valuePop(ctxt);
3212 arg1->boolval &= arg2->boolval;
3213 valuePush(ctxt, arg1);
3214 xmlXPathFreeObject(arg2);
3215 }
3216}
3217
3218/**
3219 * xmlXPathEvalExpr:
3220 * @ctxt: the XPath Parser context
3221 *
3222 * [14] Expr ::= OrExpr
3223 * [21] OrExpr ::= AndExpr
3224 * | OrExpr 'or' AndExpr
3225 *
3226 * Parse and evaluate an expression, then push the result on the stack
3227 *
3228 */
3229void
3230xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
3231 xmlXPathEvalAndExpr(ctxt);
3232 CHECK_ERROR;
3233 while ((CUR == 'o') && (NXT(1) == 'r')) {
3234 xmlXPathObjectPtr arg1, arg2;
3235
3236 SKIP(2);
3237 xmlXPathEvalAndExpr(ctxt);
3238 CHECK_ERROR;
3239 arg2 = valuePop(ctxt);
3240 arg1 = valuePop(ctxt);
3241 arg1->boolval |= arg2->boolval;
3242 valuePush(ctxt, arg1);
3243 xmlXPathFreeObject(arg2);
3244 }
3245}
3246
3247/**
3248 * xmlXPathEvaluatePredicateResult:
3249 * @ctxt: the XPath Parser context
3250 * @res: the Predicate Expression evaluation result
3251 * @index: index of the current node in the current list
3252 *
3253 * Evaluate a predicate result for the current node.
3254 * A PredicateExpr is evaluated by evaluating the Expr and converting
3255 * the result to a boolean. If the result is a number, the result will
3256 * be converted to true if the number is equal to the position of the
3257 * context node in the context node list (as returned by the position
3258 * function) and will be converted to false otherwise; if the result
3259 * is not a number, then the result will be converted as if by a call
3260 * to the boolean function.
3261 */
3262int
3263xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
3264 xmlXPathObjectPtr res, int index) {
3265 if (res == NULL) return(0);
3266 switch (res->type) {
3267 case XPATH_BOOLEAN:
3268 return(res->boolval);
3269 case XPATH_NUMBER:
3270 return(res->floatval == index);
3271 case XPATH_NODESET:
3272 return(res->nodesetval->nodeNr != 0);
3273 case XPATH_STRING:
3274 return((res->stringval != NULL) &&
3275 (xmlStrlen(res->stringval) != 0));
3276 default:
3277 STRANGE
3278 }
3279 return(0);
3280}
3281
3282/**
3283 * xmlXPathEvalPredicate:
3284 * @ctxt: the XPath Parser context
3285 *
3286 * [8] Predicate ::= '[' PredicateExpr ']'
3287 * [9] PredicateExpr ::= Expr
3288 *
3289 * Parse and evaluate a predicate for all the elements of the
3290 * current node list. Then refine the list by removing all
3291 * nodes where the predicate is false.
3292 */
3293void
3294xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
3295 const CHAR *cur;
3296 xmlXPathObjectPtr res;
3297 xmlNodeSetPtr newset = NULL;
3298 int i;
3299
3300 if (CUR != '[') {
3301 ERROR(XPATH_INVALID_PREDICATE_ERROR);
3302 }
3303 NEXT;
3304 if ((ctxt->context->nodelist == NULL) ||
3305 (ctxt->context->nodelist->nodeNr == 0)) {
3306 ctxt->context->node = NULL;
3307 xmlXPathEvalExpr(ctxt);
3308 CHECK_ERROR;
3309 res = valuePop(ctxt);
3310 if (res != NULL)
3311 xmlXPathFreeObject(res);
3312 } else {
3313 cur = ctxt->cur;
3314 newset = xmlXPathNodeSetCreate(NULL);
3315 for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) {
3316 ctxt->cur = cur;
3317 ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
3318 xmlXPathEvalExpr(ctxt);
3319 CHECK_ERROR;
3320 res = valuePop(ctxt);
3321 if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1))
3322 xmlXPathNodeSetAdd(newset,
3323 ctxt->context->nodelist->nodeTab[i]);
3324 if (res != NULL)
3325 xmlXPathFreeObject(res);
3326 }
3327 if (ctxt->context->nodelist != NULL)
3328 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3329 ctxt->context->nodelist = newset;
3330 ctxt->context->node = NULL;
3331 }
3332 if (CUR != ']') {
3333 ERROR(XPATH_INVALID_PREDICATE_ERROR);
3334 }
3335 NEXT;
3336#ifdef DEBUG_STEP
3337 fprintf(xmlXPathDebug, "After predicate : ");
3338 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
3339#endif
3340}
3341
3342/**
3343 * xmlXPathEvalBasis:
3344 * @ctxt: the XPath Parser context
3345 *
3346 * [5] Basis ::= AxisName '::' NodeTest
3347 * | AbbreviatedBasis
3348 * [13] AbbreviatedBasis ::= NodeTest
3349 * | '@' NodeTest
3350 * [7] NodeTest ::= WildcardName
3351 * | NodeType '(' ')'
3352 * | 'processing-instruction' '(' Literal ')'
3353 * [37] WildcardName ::= '*'
3354 * | NCName ':' '*'
3355 * | QName
3356 *
3357 * Evaluate one step in a Location Path
3358 */
3359void
3360xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) {
3361 CHAR *name = NULL;
3362 CHAR *prefix = NULL;
3363 int type = 0;
3364 int axis = AXIS_CHILD; /* the default on abbreviated syntax */
3365 int nodetest = NODE_TEST_NONE;
3366 int nodetype = 0;
3367 xmlNodeSetPtr newset = NULL;
3368
3369 if (CUR == '@') {
3370 TODO /* attributes */
3371 } else if (CUR == '*') {
3372 NEXT;
3373 nodetest = NODE_TEST_ALL;
3374 } else {
3375 name = xmlXPathParseNCName(ctxt);
3376 if (name == NULL) {
3377 ERROR(XPATH_EXPR_ERROR);
3378 }
3379 type = xmlXPathGetNameType(ctxt, name);
3380 switch (type) {
3381 /*
3382 * Simple case: no axis seach all given node types.
3383 */
3384 case NODE_TYPE_COMMENT:
3385 if ((CUR != '(') || (NXT(1) != ')')) break;
3386 SKIP(2);
3387 nodetest = NODE_TEST_TYPE;
3388 nodetype = XML_COMMENT_NODE;
3389 goto search_nodes;
3390 case NODE_TYPE_TEXT:
3391 if ((CUR != '(') || (NXT(1) != ')')) break;
3392 SKIP(2);
3393 nodetest = NODE_TEST_TYPE;
3394 nodetype = XML_TEXT_NODE;
3395 goto search_nodes;
3396 case NODE_TYPE_NODE:
3397 if ((CUR != '(') || (NXT(1) != ')')) {
3398 nodetest = NODE_TEST_NAME;
3399 break;
3400 }
3401 SKIP(2);
3402 nodetest = NODE_TEST_TYPE;
3403 nodetype = XML_ELEMENT_NODE;
3404 goto search_nodes;
3405 case NODE_TYPE_PI:
3406 if (CUR != '(') break;
3407 if (NXT(1) != ')') {
3408 /*
3409 * Specific case: search a PI by name.
3410 */
3411 SKIP(2);
3412 nodetest = NODE_TEST_PI;
3413 xmlXPathEvalLiteral(ctxt);
3414 CHECK_ERROR;
3415 if (CUR != ')')
3416 ERROR(XPATH_UNCLOSED_ERROR);
3417 xmlXPathSearchPI(ctxt, 0);
3418 return;
3419 }
3420 SKIP(2);
3421 nodetest = NODE_TEST_TYPE;
3422 nodetype = XML_PI_NODE;
3423 goto search_nodes;
3424
3425 /*
3426 * Handling of the compund form: got the axis.
3427 */
3428 case AXIS_ANCESTOR:
3429 case AXIS_ANCESTOR_OR_SELF:
3430 case AXIS_ATTRIBUTE:
3431 case AXIS_CHILD:
3432 case AXIS_DESCENDANT:
3433 case AXIS_DESCENDANT_OR_SELF:
3434 case AXIS_FOLLOWING:
3435 case AXIS_FOLLOWING_SIBLING:
3436 case AXIS_NAMESPACE:
3437 case AXIS_PARENT:
3438 case AXIS_PRECEDING:
3439 case AXIS_PRECEDING_SIBLING:
3440 case AXIS_SELF:
3441 if ((CUR != ':') || (NXT(1) != ':')) {
3442 nodetest = NODE_TEST_NAME;
3443 break;
3444 }
3445 SKIP(2);
3446 axis = type;
3447 break;
3448
3449 /*
3450 * Default: abbreviated syntax the axis is AXIS_CHILD
3451 */
3452 default:
3453 nodetest = NODE_TEST_NAME;
3454 }
3455 if (nodetest == NODE_TEST_NONE) {
3456 if (CUR == '*') {
3457 NEXT;
3458 nodetest = NODE_TEST_ALL;
3459 } else {
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003460 if (name != NULL)
3461 free(name);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003462 name = xmlXPathParseQName(ctxt, &prefix);
3463 nodetest = NODE_TEST_NAME;
3464 }
3465 } else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) {
3466 NEXT;
3467 prefix = name;
3468 if (CUR == '*') {
3469 NEXT;
3470 nodetest = NODE_TEST_ALL;
3471 } else
3472 name = xmlXPathParseNCName(ctxt);
3473 } else if (name == NULL)
3474 ERROR(XPATH_EXPR_ERROR);
3475 }
3476
3477search_nodes:
3478
3479#ifdef DEBUG_STEP
3480 fprintf(xmlXPathDebug, "Basis : computing new set\n");
3481#endif
3482 newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype,
3483 prefix, name);
3484 if (ctxt->context->nodelist != NULL)
3485 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3486 ctxt->context->nodelist = newset;
3487 ctxt->context->node = NULL;
3488#ifdef DEBUG_STEP
3489 fprintf(xmlXPathDebug, "Basis : ");
3490 xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist);
3491#endif
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003492 if (name != NULL) free(name);
3493 if (prefix != NULL) free(prefix);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003494}
3495
3496/**
3497 * xmlXPathEvalStep:
3498 * @ctxt: the XPath Parser context
3499 *
3500 * [4] Step ::= Basis Predicate*
3501 * | AbbreviatedStep
3502 * [12] AbbreviatedStep ::= '.'
3503 * | '..'
3504 *
3505 * Evaluate one step in a Location Path
3506 * A location step of . is short for self::node(). This is
3507 * particularly useful in conjunction with //. For example, the
3508 * location path .//para is short for
3509 * self::node()/descendant-or-self::node()/child::para
3510 * and so will select all para descendant elements of the context
3511 * node.
3512 * Similarly, a location step of .. is short for parent::node().
3513 * For example, ../title is short for parent::node()/child::title
3514 * and so will select the title children of the parent of the context
3515 * node.
3516 */
3517void
3518xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
3519 xmlNodeSetPtr newset = NULL;
3520
3521 if ((CUR == '.') && (NXT(1) == '.')) {
3522 SKIP(2);
3523 if (ctxt->context->nodelist == NULL) {
3524 STRANGE
3525 xmlXPathRoot(ctxt);
3526 }
3527 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
3528 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3529 if (ctxt->context->nodelist != NULL)
3530 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3531 ctxt->context->nodelist = newset;
3532 ctxt->context->node = NULL;
3533 } else if (CUR == '.') {
3534 NEXT;
3535 } else {
3536 xmlXPathEvalBasis(ctxt);
3537 while (CUR == '[') {
3538 xmlXPathEvalPredicate(ctxt);
3539 }
3540 }
3541#ifdef DEBUG_STEP
3542 fprintf(xmlXPathDebug, "Step : ");
3543 xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
3544#endif
3545}
3546
3547/**
3548 * xmlXPathEvalRelativeLocationPath:
3549 * @ctxt: the XPath Parser context
3550 *
3551 * [3] RelativeLocationPath ::= Step
3552 * | RelativeLocationPath '/' Step
3553 * | AbbreviatedRelativeLocationPath
3554 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
3555 *
3556 */
3557void
3558xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) {
3559 xmlNodeSetPtr newset = NULL;
3560
3561 xmlXPathEvalStep(ctxt);
3562 while (CUR == '/') {
3563 if ((CUR == '/') && (NXT(1) == '/')) {
3564 SKIP(2);
3565 if (ctxt->context->nodelist == NULL) {
3566 STRANGE
3567 xmlXPathRoot(ctxt);
3568 }
3569 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
3570 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3571 if (ctxt->context->nodelist != NULL)
3572 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3573 ctxt->context->nodelist = newset;
3574 ctxt->context->node = NULL;
3575 xmlXPathEvalStep(ctxt);
3576 } else if (CUR == '/') {
3577 NEXT;
3578 xmlXPathEvalStep(ctxt);
3579 }
3580 }
3581}
3582
3583/**
3584 * xmlXPathEvalLocationPath:
3585 * @ctxt: the XPath Parser context
3586 *
3587 * [1] LocationPath ::= RelativeLocationPath
3588 * | AbsoluteLocationPath
3589 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
3590 * | AbbreviatedAbsoluteLocationPath
3591 * [10] AbbreviatedAbsoluteLocationPath ::=
3592 * '//' RelativeLocationPath
3593 *
3594 * // is short for /descendant-or-self::node()/. For example,
3595 * //para is short for /descendant-or-self::node()/child::para and
3596 * so will select any para element in the document (even a para element
3597 * that is a document element will be selected by //para since the
3598 * document element node is a child of the root node); div//para is
3599 * short for div/descendant-or-self::node()/child::para and so will
3600 * select all para descendants of div children.
3601 */
3602void
3603xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
3604 xmlNodeSetPtr newset = NULL;
3605
3606 while (CUR == '/') {
3607 if ((CUR == '/') && (NXT(1) == '/')) {
3608 SKIP(2);
3609 if (ctxt->context->nodelist == NULL)
3610 xmlXPathRoot(ctxt);
3611 newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
3612 NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
3613 if (ctxt->context->nodelist != NULL)
3614 xmlXPathFreeNodeSet(ctxt->context->nodelist);
3615 ctxt->context->nodelist = newset;
3616 ctxt->context->node = NULL;
3617 xmlXPathEvalRelativeLocationPath(ctxt);
3618 } else if (CUR == '/') {
3619 NEXT;
3620 xmlXPathRoot(ctxt);
3621 xmlXPathEvalRelativeLocationPath(ctxt);
3622 } else {
3623 xmlXPathEvalRelativeLocationPath(ctxt);
3624 }
3625 }
3626}
3627
3628/*
3629 * TODO * extra spaces *
3630 * more tokenization rules ... Not used currently, especially allowing
3631 * spaces before and after ExprToken !!!!!!!!!!!!!
3632 *
3633 * [32] Operator ::= OperatorName
3634 * | MultiplyOperator
3635 * | '/' | '//' | '|' | '+' | '-' | '=' | '!='
3636 * | '<'| '<=' | '>' | '>='
3637 * [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
3638 * [39] ExprWhitespace ::= S
3639 *
3640 * BUG: ExprToken is never referenced.
3641 *
3642 * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
3643 * | WildcardName
3644 * | NodeType
3645 * | Operator
3646 * | FunctionName
3647 * | AxisName
3648 * | Literal
3649 * | Number
3650 * | VariableReference
3651 */
3652
3653/**
3654 * xmlXPathEval:
3655 * @str: the XPath expression
3656 * @ctxt: the XPath context
3657 *
3658 * Evaluate the XPath Location Path in the given context.
3659 *
3660 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
3661 * the caller has to free the object.
3662 */
3663xmlXPathObjectPtr
3664xmlXPathEval(const CHAR *str, xmlXPathContextPtr ctxt) {
3665 xmlXPathParserContextPtr pctxt;
3666 xmlXPathObjectPtr res;
3667
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003668 xmlXPathInit();
3669
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003670 CHECK_CONTEXT
3671
3672 if (xmlXPathDebug == NULL)
3673 xmlXPathDebug = stderr;
3674 pctxt = xmlXPathNewParserContext(str, ctxt);
3675 xmlXPathEvalLocationPath(pctxt);
3676
3677 do {
3678 res = valuePop(pctxt);
3679#ifdef DEBUG
3680#endif
3681 } while (res != NULL);
3682 res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
3683 xmlXPathFreeParserContext(pctxt);
3684 return(res);
3685}
3686
3687/**
3688 * xmlXPathEvalExpression:
3689 * @str: the XPath expression
3690 * @ctxt: the XPath context
3691 *
3692 * Evaluate the XPath expression in the given context.
3693 *
3694 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
3695 * the caller has to free the object.
3696 */
3697xmlXPathObjectPtr
3698xmlXPathEvalExpression(const CHAR *str, xmlXPathContextPtr ctxt) {
3699 xmlXPathParserContextPtr pctxt;
3700 xmlXPathObjectPtr res, tmp;
3701
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003702 xmlXPathInit();
3703
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003704 CHECK_CONTEXT
3705
3706 if (xmlXPathDebug == NULL)
3707 xmlXPathDebug = stderr;
3708 pctxt = xmlXPathNewParserContext(str, ctxt);
3709 xmlXPathEvalExpr(pctxt);
3710
3711 res = valuePop(pctxt);
3712 do {
3713 tmp = valuePop(pctxt);
Daniel Veillarde2d034d1999-07-27 19:52:06 +00003714 if (tmp != NULL);
3715 xmlXPathFreeObject(tmp);
Daniel Veillard1566d3a1999-07-15 14:24:29 +00003716 } while (tmp != NULL);
3717 xmlXPathFreeParserContext(pctxt);
3718 return(res);
3719}
3720