blob: ff7a89b342a990c53ce75b7c92b2dcaf0f771abb [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * xpath.c: XML Path Language implementation
3 * XPath is a language for addressing parts of an XML document,
4 * designed to be used by both XSLT and XPointer
5 *
6 * Reference: W3C Recommendation 16 November 1999
7 * http://www.w3.org/TR/1999/REC-xpath-19991116
8 * Public reference:
9 * http://www.w3.org/TR/xpath
10 *
11 * See COPYRIGHT for the status of this software
12 *
13 * Author: Daniel.Veillard@w3.org
14 *
15 * 14 Nov 2000 ht - truncated declaration of xmlXPathEvalRelativeLocationPath
16 * for VMS
17 */
18
19#ifdef WIN32
20#include "win32config.h"
21#else
22#include "config.h"
23#endif
24
25#include <libxml/xmlversion.h>
26#ifdef LIBXML_XPATH_ENABLED
27
28#include <stdio.h>
29#include <string.h>
30
31#ifdef HAVE_SYS_TYPES_H
32#include <sys/types.h>
33#endif
34#ifdef HAVE_MATH_H
35#include <math.h>
36#endif
37#ifdef HAVE_FLOAT_H
38#include <float.h>
39#endif
40#ifdef HAVE_IEEEFP_H
41#include <ieeefp.h>
42#endif
43#ifdef HAVE_NAN_H
44#include <nan.h>
45#endif
46#ifdef HAVE_CTYPE_H
47#include <ctype.h>
48#endif
49
50#include <libxml/xmlmemory.h>
51#include <libxml/tree.h>
52#include <libxml/valid.h>
53#include <libxml/xpath.h>
54#include <libxml/xpathInternals.h>
55#include <libxml/parserInternals.h>
56#include <libxml/hash.h>
57#ifdef LIBXML_XPTR_ENABLED
58#include <libxml/xpointer.h>
59#endif
60#ifdef LIBXML_DEBUG_ENABLED
61#include <libxml/debugXML.h>
62#endif
63#include <libxml/xmlerror.h>
64
65/* #define DEBUG */
66/* #define DEBUG_STEP */
67/* #define DEBUG_EXPR */
68
69void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
70double xmlXPathStringEvalNumber(const xmlChar *str);
71
72/*
73 * Setup stuff for floating point
74 * The lack of portability of this section of the libc is annoying !
75 */
76double xmlXPathNAN = 0;
77double xmlXPathPINF = 1;
78double xmlXPathNINF = -1;
79
80#ifndef isinf
81#ifndef HAVE_ISINF
82
83#if HAVE_FPCLASS
84
85int isinf(double d) {
86 fpclass_t type = fpclass(d);
87 switch (type) {
88 case FP_NINF:
89 return(-1);
90 case FP_PINF:
91 return(1);
92 }
93 return(0);
94}
95
96#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
97
98#if HAVE_FP_CLASS_H
99#include <fp_class.h>
100#endif
101
102int isinf(double d) {
103#if HAVE_FP_CLASS
104 int fpclass = fp_class(d);
105#else
106 int fpclass = fp_class_d(d);
107#endif
108 if (fpclass == FP_POS_INF)
109 return(1);
110 if (fpclass == FP_NEG_INF)
111 return(-1);
112 return(0);
113}
114
115#elif defined(HAVE_CLASS)
116
117int isinf(double d) {
118 int fpclass = class(d);
119 if (fpclass == FP_PLUS_INF)
120 return(1);
121 if (fpclass == FP_MINUS_INF)
122 return(-1);
123 return(0);
124}
125#elif defined(finite) || defined(HAVE_FINITE)
126int isinf(double x) { return !finite(x) && x==x; }
127#elif defined(HUGE_VAL)
128int isinf(double x)
129{
130 if (x == HUGE_VAL)
131 return(1);
132 if (x == -HUGE_VAL)
133 return(-1);
134 return(0);
135}
136#endif
137
138#endif /* ! HAVE_ISINF */
139#endif /* ! defined(isinf) */
140
141#ifndef isnan
142#ifndef HAVE_ISNAN
143
144#ifdef HAVE_ISNAND
145#define isnan(f) isnand(f)
146#endif /* HAVE_iSNAND */
147
148#endif /* ! HAVE_iSNAN */
149#endif /* ! defined(isnan) */
150
151/**
152 * xmlXPathInit:
153 *
154 * Initialize the XPath environment
155 */
156void
157xmlXPathInit(void) {
158 static int initialized = 0;
159
160 if (initialized) return;
161
162 xmlXPathNAN = 0;
163 xmlXPathNAN /= 0;
164
165 xmlXPathPINF = 1;
166 xmlXPathPINF /= 0;
167
168 xmlXPathNINF = -1;
169 xmlXPathNINF /= 0;
170
171 initialized = 1;
172}
173
174/************************************************************************
175 * *
176 * Debugging related functions *
177 * *
178 ************************************************************************/
179
180#define TODO \
181 xmlGenericError(xmlGenericErrorContext, \
182 "Unimplemented block at %s:%d\n", \
183 __FILE__, __LINE__);
184
185#define STRANGE \
186 xmlGenericError(xmlGenericErrorContext, \
187 "Internal error at %s:%d\n", \
188 __FILE__, __LINE__);
189
190#ifdef LIBXML_DEBUG_ENABLED
191void xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
192 int i;
193 char shift[100];
194
195 for (i = 0;((i < depth) && (i < 25));i++)
196 shift[2 * i] = shift[2 * i + 1] = ' ';
197 shift[2 * i] = shift[2 * i + 1] = 0;
198 if (cur == NULL) {
199 fprintf(output, shift);
200 fprintf(output, "Node is NULL !\n");
201 return;
202
203 }
204
205 if ((cur->type == XML_DOCUMENT_NODE) ||
206 (cur->type == XML_HTML_DOCUMENT_NODE)) {
207 fprintf(output, shift);
208 fprintf(output, " /\n");
209 } else if (cur->type == XML_ATTRIBUTE_NODE)
210 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth);
211 else
212 xmlDebugDumpOneNode(output, cur, depth);
213}
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000214void xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) {
215 xmlNodePtr tmp;
216 int i;
217 char shift[100];
218
219 for (i = 0;((i < depth) && (i < 25));i++)
220 shift[2 * i] = shift[2 * i + 1] = ' ';
221 shift[2 * i] = shift[2 * i + 1] = 0;
222 if (cur == NULL) {
223 fprintf(output, shift);
224 fprintf(output, "Node is NULL !\n");
225 return;
226
227 }
228
229 while (cur != NULL) {
230 tmp = cur;
231 cur = cur->next;
232 xmlDebugDumpOneNode(output, tmp, depth);
233 }
234}
Owen Taylor3473f882001-02-23 17:55:21 +0000235
236void xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) {
237 int i;
238 char shift[100];
239
240 for (i = 0;((i < depth) && (i < 25));i++)
241 shift[2 * i] = shift[2 * i + 1] = ' ';
242 shift[2 * i] = shift[2 * i + 1] = 0;
243
244 if (cur == NULL) {
245 fprintf(output, shift);
246 fprintf(output, "NodeSet is NULL !\n");
247 return;
248
249 }
250
251 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr);
252 for (i = 0;i < cur->nodeNr;i++) {
253 fprintf(output, shift);
254 fprintf(output, "%d", i + 1);
255 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1);
256 }
257}
258
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000259void xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) {
260 int i;
261 char shift[100];
262
263 for (i = 0;((i < depth) && (i < 25));i++)
264 shift[2 * i] = shift[2 * i + 1] = ' ';
265 shift[2 * i] = shift[2 * i + 1] = 0;
266
267 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) {
268 fprintf(output, shift);
269 fprintf(output, "Value Tree is NULL !\n");
270 return;
271
272 }
273
274 fprintf(output, shift);
275 fprintf(output, "%d", i + 1);
276 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
277}
Owen Taylor3473f882001-02-23 17:55:21 +0000278#if defined(LIBXML_XPTR_ENABLED)
279void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
280void xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) {
281 int i;
282 char shift[100];
283
284 for (i = 0;((i < depth) && (i < 25));i++)
285 shift[2 * i] = shift[2 * i + 1] = ' ';
286 shift[2 * i] = shift[2 * i + 1] = 0;
287
288 if (cur == NULL) {
289 fprintf(output, shift);
290 fprintf(output, "LocationSet is NULL !\n");
291 return;
292
293 }
294
295 for (i = 0;i < cur->locNr;i++) {
296 fprintf(output, shift);
297 fprintf(output, "%d : ", i + 1);
298 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
299 }
300}
301#endif
302
303void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) {
304 int i;
305 char shift[100];
306
307 for (i = 0;((i < depth) && (i < 25));i++)
308 shift[2 * i] = shift[2 * i + 1] = ' ';
309 shift[2 * i] = shift[2 * i + 1] = 0;
310
311 fprintf(output, shift);
312
313 if (cur == NULL) {
314 fprintf(output, "Object is empty (NULL)\n");
315 return;
316 }
317 switch(cur->type) {
318 case XPATH_UNDEFINED:
319 fprintf(output, "Object is uninitialized\n");
320 break;
321 case XPATH_NODESET:
322 fprintf(output, "Object is a Node Set :\n");
323 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth);
324 break;
325 case XPATH_XSLT_TREE:
326 fprintf(output, "Object is an XSLT value tree :\n");
Daniel Veillardf7cd4812001-02-23 18:44:52 +0000327 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth);
Owen Taylor3473f882001-02-23 17:55:21 +0000328 break;
329 case XPATH_BOOLEAN:
330 fprintf(output, "Object is a Boolean : ");
331 if (cur->boolval) fprintf(output, "true\n");
332 else fprintf(output, "false\n");
333 break;
334 case XPATH_NUMBER:
335 fprintf(output, "Object is a number : %0g\n", cur->floatval);
336 break;
337 case XPATH_STRING:
338 fprintf(output, "Object is a string : ");
339 xmlDebugDumpString(output, cur->stringval);
340 fprintf(output, "\n");
341 break;
342 case XPATH_POINT:
343 fprintf(output, "Object is a point : index %d in node", cur->index);
344 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1);
345 fprintf(output, "\n");
346 break;
347 case XPATH_RANGE:
348 if ((cur->user2 == NULL) ||
349 ((cur->user2 == cur->user) && (cur->index == cur->index2))) {
350 fprintf(output, "Object is a collapsed range :\n");
351 fprintf(output, shift);
352 if (cur->index >= 0)
353 fprintf(output, "index %d in ", cur->index);
354 fprintf(output, "node\n");
355 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
356 depth + 1);
357 } else {
358 fprintf(output, "Object is a range :\n");
359 fprintf(output, shift);
360 fprintf(output, "From ");
361 if (cur->index >= 0)
362 fprintf(output, "index %d in ", cur->index);
363 fprintf(output, "node\n");
364 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user,
365 depth + 1);
366 fprintf(output, shift);
367 fprintf(output, "To ");
368 if (cur->index2 >= 0)
369 fprintf(output, "index %d in ", cur->index2);
370 fprintf(output, "node\n");
371 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2,
372 depth + 1);
373 fprintf(output, "\n");
374 }
375 break;
376 case XPATH_LOCATIONSET:
377#if defined(LIBXML_XPTR_ENABLED)
378 fprintf(output, "Object is a Location Set:\n");
379 xmlXPathDebugDumpLocationSet(output,
380 (xmlLocationSetPtr) cur->user, depth);
381#endif
382 break;
383 case XPATH_USERS:
384 fprintf(output, "Object is user defined\n");
385 break;
386 }
387}
388#endif
389
390/************************************************************************
391 * *
392 * Parser stacks related functions and macros *
393 * *
394 ************************************************************************/
395
396/*
397 * Generic function for accessing stacks in the Parser Context
398 */
399
400#define PUSH_AND_POP(type, name) \
401extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
402 if (ctxt->name##Nr >= ctxt->name##Max) { \
403 ctxt->name##Max *= 2; \
404 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
405 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
406 if (ctxt->name##Tab == NULL) { \
407 xmlGenericError(xmlGenericErrorContext, \
408 "realloc failed !\n"); \
409 return(0); \
410 } \
411 } \
412 ctxt->name##Tab[ctxt->name##Nr] = value; \
413 ctxt->name = value; \
414 return(ctxt->name##Nr++); \
415} \
416extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
417 type ret; \
418 if (ctxt->name##Nr <= 0) return(0); \
419 ctxt->name##Nr--; \
420 if (ctxt->name##Nr > 0) \
421 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
422 else \
423 ctxt->name = NULL; \
424 ret = ctxt->name##Tab[ctxt->name##Nr]; \
425 ctxt->name##Tab[ctxt->name##Nr] = 0; \
426 return(ret); \
427} \
428
429PUSH_AND_POP(xmlXPathObjectPtr, value)
430
431/*
432 * Macros for accessing the content. Those should be used only by the parser,
433 * and not exported.
434 *
435 * Dirty macros, i.e. one need to make assumption on the context to use them
436 *
437 * CUR_PTR return the current pointer to the xmlChar to be parsed.
438 * CUR returns the current xmlChar value, i.e. a 8 bit value
439 * in ISO-Latin or UTF-8.
440 * This should be used internally by the parser
441 * only to compare to ASCII values otherwise it would break when
442 * running with UTF-8 encoding.
443 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
444 * to compare on ASCII based substring.
445 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
446 * strings within the parser.
447 * CURRENT Returns the current char value, with the full decoding of
448 * UTF-8 if we are using this mode. It returns an int.
449 * NEXT Skip to the next character, this does the proper decoding
450 * in UTF-8 mode. It also pop-up unfinished entities on the fly.
451 * It returns the pointer to the current xmlChar.
452 */
453
454#define CUR (*ctxt->cur)
455#define SKIP(val) ctxt->cur += (val)
456#define NXT(val) ctxt->cur[(val)]
457#define CUR_PTR ctxt->cur
458
459#define SKIP_BLANKS \
460 while (IS_BLANK(*(ctxt->cur))) NEXT
461
462#define CURRENT (*ctxt->cur)
463#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
464
465/************************************************************************
466 * *
467 * Error handling routines *
468 * *
469 ************************************************************************/
470
471
472const char *xmlXPathErrorMessages[] = {
473 "Ok",
474 "Number encoding",
475 "Unfinished litteral",
476 "Start of litteral",
477 "Expected $ for variable reference",
478 "Undefined variable",
479 "Invalid predicate",
480 "Invalid expression",
481 "Missing closing curly brace",
482 "Unregistered function",
483 "Invalid operand",
484 "Invalid type",
485 "Invalid number of arguments",
486 "Invalid context size",
487 "Invalid context position",
488 "Memory allocation error",
489 "Syntax error",
490 "Resource error",
491 "Sub resource error",
492 "Undefined namespace prefix"
493};
494
495/**
496 * xmlXPathError:
497 * @ctxt: the XPath Parser context
498 * @file: the file name
499 * @line: the line number
500 * @no: the error number
501 *
502 * Create a new xmlNodeSetPtr of type double and of value @val
503 *
504 * Returns the newly created object.
505 */
506void
507xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
508 int line, int no) {
509 int n;
510 const xmlChar *cur;
511 const xmlChar *base;
512
513 xmlGenericError(xmlGenericErrorContext,
514 "Error %s:%d: %s\n", file, line,
515 xmlXPathErrorMessages[no]);
516
517 cur = ctxt->cur;
518 base = ctxt->base;
519 while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
520 cur--;
521 }
522 n = 0;
523 while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
524 cur--;
525 if ((*cur == '\n') || (*cur == '\r')) cur++;
526 base = cur;
527 n = 0;
528 while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
529 xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
530 n++;
531 }
532 xmlGenericError(xmlGenericErrorContext, "\n");
533 cur = ctxt->cur;
534 while ((*cur == '\n') || (*cur == '\r'))
535 cur--;
536 n = 0;
537 while ((cur != base) && (n++ < 80)) {
538 xmlGenericError(xmlGenericErrorContext, " ");
539 base++;
540 }
541 xmlGenericError(xmlGenericErrorContext,"^\n");
542}
543
544
545/************************************************************************
546 * *
547 * Routines to handle NodeSets *
548 * *
549 ************************************************************************/
550
551/**
552 * xmlXPathCmpNodes:
553 * @node1: the first node
554 * @node2: the second node
555 *
556 * Compare two nodes w.r.t document order
557 *
558 * Returns -2 in case of error 1 if first point < second point, 0 if
559 * that's the same node, -1 otherwise
560 */
561int
562xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
563 int depth1, depth2;
564 xmlNodePtr cur, root;
565
566 if ((node1 == NULL) || (node2 == NULL))
567 return(-2);
568 /*
569 * a couple of optimizations which will avoid computations in most cases
570 */
571 if (node1 == node2)
572 return(0);
573 if (node1 == node2->prev)
574 return(1);
575 if (node1 == node2->next)
576 return(-1);
577
578 /*
579 * compute depth to root
580 */
581 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
582 if (cur == node1)
583 return(1);
584 depth2++;
585 }
586 root = cur;
587 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
588 if (cur == node2)
589 return(-1);
590 depth1++;
591 }
592 /*
593 * Distinct document (or distinct entities :-( ) case.
594 */
595 if (root != cur) {
596 return(-2);
597 }
598 /*
599 * get the nearest common ancestor.
600 */
601 while (depth1 > depth2) {
602 depth1--;
603 node1 = node1->parent;
604 }
605 while (depth2 > depth1) {
606 depth2--;
607 node2 = node2->parent;
608 }
609 while (node1->parent != node2->parent) {
610 node1 = node1->parent;
611 node2 = node2->parent;
612 /* should not happen but just in case ... */
613 if ((node1 == NULL) || (node2 == NULL))
614 return(-2);
615 }
616 /*
617 * Find who's first.
618 */
619 if (node1 == node2->next)
620 return(-1);
621 for (cur = node1->next;cur != NULL;cur = cur->next)
622 if (cur == node2)
623 return(1);
624 return(-1); /* assume there is no sibling list corruption */
625}
626
627/**
628 * xmlXPathNodeSetSort:
629 * @set: the node set
630 *
631 * Sort the node set in document order
632 */
633void
634xmlXPathNodeSetSort(xmlNodeSetPtr set) {
635 int i, j, incr, len, rc;
636 xmlNodePtr tmp;
637
638 if (set == NULL)
639 return;
640
641 /* Use Shell's sort to sort the node-set */
642 len = set->nodeNr;
643 for (incr = len / 2; incr > 0; incr /= 2) {
644 for (i = incr; i < len; i++) {
645 j = i - incr;
646 while (j >= 0) {
647 rc = xmlXPathCmpNodes(set->nodeTab[j], set->nodeTab[j + incr]);
648 if (rc != 1 && rc != -2) {
649 tmp = set->nodeTab[j];
650 set->nodeTab[j] = set->nodeTab[j + incr];
651 set->nodeTab[j + incr] = tmp;
652 j -= incr;
653 } else
654 break;
655 }
656 }
657 }
658}
659
660#define XML_NODESET_DEFAULT 10
661/**
662 * xmlXPathNodeSetCreate:
663 * @val: an initial xmlNodePtr, or NULL
664 *
665 * Create a new xmlNodeSetPtr of type double and of value @val
666 *
667 * Returns the newly created object.
668 */
669xmlNodeSetPtr
670xmlXPathNodeSetCreate(xmlNodePtr val) {
671 xmlNodeSetPtr ret;
672
673 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
674 if (ret == NULL) {
675 xmlGenericError(xmlGenericErrorContext,
676 "xmlXPathNewNodeSet: out of memory\n");
677 return(NULL);
678 }
679 memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
680 if (val != NULL) {
681 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
682 sizeof(xmlNodePtr));
683 if (ret->nodeTab == NULL) {
684 xmlGenericError(xmlGenericErrorContext,
685 "xmlXPathNewNodeSet: out of memory\n");
686 return(NULL);
687 }
688 memset(ret->nodeTab, 0 ,
689 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
690 ret->nodeMax = XML_NODESET_DEFAULT;
691 ret->nodeTab[ret->nodeNr++] = val;
692 }
693 return(ret);
694}
695
696/**
697 * xmlXPathNodeSetAdd:
698 * @cur: the initial node set
699 * @val: a new xmlNodePtr
700 *
701 * add a new xmlNodePtr ot an existing NodeSet
702 */
703void
704xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
705 int i;
706
707 if (val == NULL) return;
708
709 /*
710 * check against doublons
711 */
712 for (i = 0;i < cur->nodeNr;i++)
713 if (cur->nodeTab[i] == val) return;
714
715 /*
716 * grow the nodeTab if needed
717 */
718 if (cur->nodeMax == 0) {
719 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
720 sizeof(xmlNodePtr));
721 if (cur->nodeTab == NULL) {
722 xmlGenericError(xmlGenericErrorContext,
723 "xmlXPathNodeSetAdd: out of memory\n");
724 return;
725 }
726 memset(cur->nodeTab, 0 ,
727 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
728 cur->nodeMax = XML_NODESET_DEFAULT;
729 } else if (cur->nodeNr == cur->nodeMax) {
730 xmlNodePtr *temp;
731
732 cur->nodeMax *= 2;
733 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
734 sizeof(xmlNodePtr));
735 if (temp == NULL) {
736 xmlGenericError(xmlGenericErrorContext,
737 "xmlXPathNodeSetAdd: out of memory\n");
738 return;
739 }
740 cur->nodeTab = temp;
741 }
742 cur->nodeTab[cur->nodeNr++] = val;
743}
744
745/**
746 * xmlXPathNodeSetAddUnique:
747 * @cur: the initial node set
748 * @val: a new xmlNodePtr
749 *
750 * add a new xmlNodePtr ot an existing NodeSet, optimized version
751 * when we are sure the node is not already in the set.
752 */
753void
754xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
755 if (val == NULL) return;
756
757 /*
758 * grow the nodeTab if needed
759 */
760 if (cur->nodeMax == 0) {
761 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
762 sizeof(xmlNodePtr));
763 if (cur->nodeTab == NULL) {
764 xmlGenericError(xmlGenericErrorContext,
765 "xmlXPathNodeSetAddUnique: out of memory\n");
766 return;
767 }
768 memset(cur->nodeTab, 0 ,
769 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
770 cur->nodeMax = XML_NODESET_DEFAULT;
771 } else if (cur->nodeNr == cur->nodeMax) {
772 xmlNodePtr *temp;
773
774 cur->nodeMax *= 2;
775 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
776 sizeof(xmlNodePtr));
777 if (temp == NULL) {
778 xmlGenericError(xmlGenericErrorContext,
779 "xmlXPathNodeSetAddUnique: out of memory\n");
780 return;
781 }
782 cur->nodeTab = temp;
783 }
784 cur->nodeTab[cur->nodeNr++] = val;
785}
786
787/**
788 * xmlXPathNodeSetMerge:
789 * @val1: the first NodeSet or NULL
790 * @val2: the second NodeSet
791 *
792 * Merges two nodesets, all nodes from @val2 are added to @val1
793 * if @val1 is NULL, a new set is created and copied from @val2
794 *
795 * Returns val1 once extended or NULL in case of error.
796 */
797xmlNodeSetPtr
798xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
799 int i, j, initNr;
800
801 if (val2 == NULL) return(val1);
802 if (val1 == NULL) {
803 val1 = xmlXPathNodeSetCreate(NULL);
804 }
805
806 initNr = val1->nodeNr;
807
808 for (i = 0;i < val2->nodeNr;i++) {
809 /*
810 * check against doublons
811 */
812 for (j = 0; j < initNr; j++)
813 if (val1->nodeTab[j] == val2->nodeTab[i]) continue;
814
815 /*
816 * grow the nodeTab if needed
817 */
818 if (val1->nodeMax == 0) {
819 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
820 sizeof(xmlNodePtr));
821 if (val1->nodeTab == NULL) {
822 xmlGenericError(xmlGenericErrorContext,
823 "xmlXPathNodeSetMerge: out of memory\n");
824 return(NULL);
825 }
826 memset(val1->nodeTab, 0 ,
827 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
828 val1->nodeMax = XML_NODESET_DEFAULT;
829 } else if (val1->nodeNr == val1->nodeMax) {
830 xmlNodePtr *temp;
831
832 val1->nodeMax *= 2;
833 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
834 sizeof(xmlNodePtr));
835 if (temp == NULL) {
836 xmlGenericError(xmlGenericErrorContext,
837 "xmlXPathNodeSetMerge: out of memory\n");
838 return(NULL);
839 }
840 val1->nodeTab = temp;
841 }
842 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
843 }
844
845 return(val1);
846}
847
848/**
849 * xmlXPathNodeSetDel:
850 * @cur: the initial node set
851 * @val: an xmlNodePtr
852 *
853 * Removes an xmlNodePtr from an existing NodeSet
854 */
855void
856xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
857 int i;
858
859 if (cur == NULL) return;
860 if (val == NULL) return;
861
862 /*
863 * check against doublons
864 */
865 for (i = 0;i < cur->nodeNr;i++)
866 if (cur->nodeTab[i] == val) break;
867
868 if (i >= cur->nodeNr) {
869#ifdef DEBUG
870 xmlGenericError(xmlGenericErrorContext,
871 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
872 val->name);
873#endif
874 return;
875 }
876 cur->nodeNr--;
877 for (;i < cur->nodeNr;i++)
878 cur->nodeTab[i] = cur->nodeTab[i + 1];
879 cur->nodeTab[cur->nodeNr] = NULL;
880}
881
882/**
883 * xmlXPathNodeSetRemove:
884 * @cur: the initial node set
885 * @val: the index to remove
886 *
887 * Removes an entry from an existing NodeSet list.
888 */
889void
890xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
891 if (cur == NULL) return;
892 if (val >= cur->nodeNr) return;
893 cur->nodeNr--;
894 for (;val < cur->nodeNr;val++)
895 cur->nodeTab[val] = cur->nodeTab[val + 1];
896 cur->nodeTab[cur->nodeNr] = NULL;
897}
898
899/**
900 * xmlXPathFreeNodeSet:
901 * @obj: the xmlNodeSetPtr to free
902 *
903 * Free the NodeSet compound (not the actual nodes !).
904 */
905void
906xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
907 if (obj == NULL) return;
908 if (obj->nodeTab != NULL) {
Daniel Veillard48b2f892001-02-25 16:11:03 +0000909 MEM_CLEANUP(obj->nodeTab, (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Owen Taylor3473f882001-02-23 17:55:21 +0000910 xmlFree(obj->nodeTab);
911 }
Daniel Veillard48b2f892001-02-25 16:11:03 +0000912 MEM_CLEANUP(obj, (size_t) sizeof(xmlNodeSet));
Owen Taylor3473f882001-02-23 17:55:21 +0000913 xmlFree(obj);
914}
915
916/**
917 * xmlXPathFreeValueTree:
918 * @obj: the xmlNodeSetPtr to free
919 *
920 * Free the NodeSet compound and the actual tree, this is different
921 * from xmlXPathFreeNodeSet()
922 */
923void
924xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
925 int i;
926
927 if (obj == NULL) return;
928 for (i = 0;i < obj->nodeNr;i++)
929 if (obj->nodeTab[i] != NULL)
Daniel Veillardbbd51d52001-02-24 03:07:03 +0000930 xmlFreeNodeList(obj->nodeTab[i]);
Owen Taylor3473f882001-02-23 17:55:21 +0000931
932 if (obj->nodeTab != NULL) {
Daniel Veillard48b2f892001-02-25 16:11:03 +0000933 MEM_CLEANUP(obj->nodeTab, (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
Owen Taylor3473f882001-02-23 17:55:21 +0000934 xmlFree(obj->nodeTab);
935 }
Daniel Veillard48b2f892001-02-25 16:11:03 +0000936 MEM_CLEANUP(obj, (size_t) sizeof(xmlNodeSet));
Owen Taylor3473f882001-02-23 17:55:21 +0000937 xmlFree(obj);
938}
939
940#if defined(DEBUG) || defined(DEBUG_STEP)
941/**
942 * xmlGenericErrorContextNodeSet:
943 * @output: a FILE * for the output
944 * @obj: the xmlNodeSetPtr to free
945 *
946 * Quick display of a NodeSet
947 */
948void
949xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
950 int i;
951
952 if (output == NULL) output = xmlGenericErrorContext;
953 if (obj == NULL) {
954 fprintf(output, "NodeSet == NULL !\n");
955 return;
956 }
957 if (obj->nodeNr == 0) {
958 fprintf(output, "NodeSet is empty\n");
959 return;
960 }
961 if (obj->nodeTab == NULL) {
962 fprintf(output, " nodeTab == NULL !\n");
963 return;
964 }
965 for (i = 0; i < obj->nodeNr; i++) {
966 if (obj->nodeTab[i] == NULL) {
967 fprintf(output, " NULL !\n");
968 return;
969 }
970 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
971 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
972 fprintf(output, " /");
973 else if (obj->nodeTab[i]->name == NULL)
974 fprintf(output, " noname!");
975 else fprintf(output, " %s", obj->nodeTab[i]->name);
976 }
977 fprintf(output, "\n");
978}
979#endif
980
981/**
982 * xmlXPathNewNodeSet:
983 * @val: the NodePtr value
984 *
985 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
986 * it with the single Node @val
987 *
988 * Returns the newly created object.
989 */
990xmlXPathObjectPtr
991xmlXPathNewNodeSet(xmlNodePtr val) {
992 xmlXPathObjectPtr ret;
993
994 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
995 if (ret == NULL) {
996 xmlGenericError(xmlGenericErrorContext,
997 "xmlXPathNewNodeSet: out of memory\n");
998 return(NULL);
999 }
1000 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1001 ret->type = XPATH_NODESET;
1002 ret->nodesetval = xmlXPathNodeSetCreate(val);
1003 return(ret);
1004}
1005
1006/**
1007 * xmlXPathNewValueTree:
1008 * @val: the NodePtr value
1009 *
1010 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
1011 * it with the tree root @val
1012 *
1013 * Returns the newly created object.
1014 */
1015xmlXPathObjectPtr
1016xmlXPathNewValueTree(xmlNodePtr val) {
1017 xmlXPathObjectPtr ret;
1018
1019 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1020 if (ret == NULL) {
1021 xmlGenericError(xmlGenericErrorContext,
1022 "xmlXPathNewNodeSet: out of memory\n");
1023 return(NULL);
1024 }
1025 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1026 ret->type = XPATH_XSLT_TREE;
1027 ret->nodesetval = xmlXPathNodeSetCreate(val);
1028 return(ret);
1029}
1030
1031/**
1032 * xmlXPathNewNodeSetList:
1033 * @val: an existing NodeSet
1034 *
1035 * Create a new xmlXPathObjectPtr of type NodeSet and initialize
1036 * it with the Nodeset @val
1037 *
1038 * Returns the newly created object.
1039 */
1040xmlXPathObjectPtr
1041xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
1042 xmlXPathObjectPtr ret;
1043 int i;
1044
1045 if (val == NULL)
1046 ret = NULL;
1047 else if (val->nodeTab == NULL)
1048 ret = xmlXPathNewNodeSet(NULL);
1049 else
1050 {
1051 ret = xmlXPathNewNodeSet(val->nodeTab[0]);
1052 for (i = 1; i < val->nodeNr; ++i)
1053 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
1054 }
1055
1056 return(ret);
1057}
1058
1059/**
1060 * xmlXPathWrapNodeSet:
1061 * @val: the NodePtr value
1062 *
1063 * Wrap the Nodeset @val in a new xmlXPathObjectPtr
1064 *
1065 * Returns the newly created object.
1066 */
1067xmlXPathObjectPtr
1068xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
1069 xmlXPathObjectPtr ret;
1070
1071 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1072 if (ret == NULL) {
1073 xmlGenericError(xmlGenericErrorContext,
1074 "xmlXPathWrapNodeSet: out of memory\n");
1075 return(NULL);
1076 }
1077 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1078 ret->type = XPATH_NODESET;
1079 ret->nodesetval = val;
1080 return(ret);
1081}
1082
1083/**
1084 * xmlXPathFreeNodeSetList:
1085 * @obj: an existing NodeSetList object
1086 *
1087 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
1088 * the list contrary to xmlXPathFreeObject().
1089 */
1090void
1091xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
1092 if (obj == NULL) return;
Daniel Veillard48b2f892001-02-25 16:11:03 +00001093 MEM_CLEANUP(obj, (size_t) sizeof(xmlXPathObject));
Owen Taylor3473f882001-02-23 17:55:21 +00001094 xmlFree(obj);
1095}
1096
1097/************************************************************************
1098 * *
1099 * Routines to handle extra functions *
1100 * *
1101 ************************************************************************/
1102
1103/**
1104 * xmlXPathRegisterFunc:
1105 * @ctxt: the XPath context
1106 * @name: the function name
1107 * @f: the function implementation or NULL
1108 *
1109 * Register a new function. If @f is NULL it unregisters the function
1110 *
1111 * Returns 0 in case of success, -1 in case of error
1112 */
1113int
1114xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
1115 xmlXPathFunction f) {
1116 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
1117}
1118
1119/**
1120 * xmlXPathRegisterFuncNS:
1121 * @ctxt: the XPath context
1122 * @name: the function name
1123 * @ns_uri: the function namespace URI
1124 * @f: the function implementation or NULL
1125 *
1126 * Register a new function. If @f is NULL it unregisters the function
1127 *
1128 * Returns 0 in case of success, -1 in case of error
1129 */
1130int
1131xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1132 const xmlChar *ns_uri, xmlXPathFunction f) {
1133 if (ctxt == NULL)
1134 return(-1);
1135 if (name == NULL)
1136 return(-1);
1137
1138 if (ctxt->funcHash == NULL)
1139 ctxt->funcHash = xmlHashCreate(0);
1140 if (ctxt->funcHash == NULL)
1141 return(-1);
1142 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
1143}
1144
1145/**
1146 * xmlXPathFunctionLookup:
1147 * @ctxt: the XPath context
1148 * @name: the function name
1149 *
1150 * Search in the Function array of the context for the given
1151 * function.
1152 *
1153 * Returns the xmlXPathFunction or NULL if not found
1154 */
1155xmlXPathFunction
1156xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1157 return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
1158}
1159
1160/**
1161 * xmlXPathFunctionLookupNS:
1162 * @ctxt: the XPath context
1163 * @name: the function name
1164 * @ns_uri: the function namespace URI
1165 *
1166 * Search in the Function array of the context for the given
1167 * function.
1168 *
1169 * Returns the xmlXPathFunction or NULL if not found
1170 */
1171xmlXPathFunction
1172xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1173 const xmlChar *ns_uri) {
1174 if (ctxt == NULL)
1175 return(NULL);
1176 if (ctxt->funcHash == NULL)
1177 return(NULL);
1178 if (name == NULL)
1179 return(NULL);
1180
1181 return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
1182}
1183
1184/**
1185 * xmlXPathRegisteredFuncsCleanup:
1186 * @ctxt: the XPath context
1187 *
1188 * Cleanup the XPath context data associated to registered functions
1189 */
1190void
1191xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
1192 if (ctxt == NULL)
1193 return;
1194
1195 xmlHashFree(ctxt->funcHash, NULL);
1196 ctxt->funcHash = NULL;
1197}
1198
1199/************************************************************************
1200 * *
1201 * Routines to handle Variable *
1202 * *
1203 ************************************************************************/
1204
1205/**
1206 * xmlXPathRegisterVariable:
1207 * @ctxt: the XPath context
1208 * @name: the variable name
1209 * @value: the variable value or NULL
1210 *
1211 * Register a new variable value. If @value is NULL it unregisters
1212 * the variable
1213 *
1214 * Returns 0 in case of success, -1 in case of error
1215 */
1216int
1217xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
1218 xmlXPathObjectPtr value) {
1219 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
1220}
1221
1222/**
1223 * xmlXPathRegisterVariableNS:
1224 * @ctxt: the XPath context
1225 * @name: the variable name
1226 * @ns_uri: the variable namespace URI
1227 * @value: the variable value or NULL
1228 *
1229 * Register a new variable value. If @value is NULL it unregisters
1230 * the variable
1231 *
1232 * Returns 0 in case of success, -1 in case of error
1233 */
1234int
1235xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1236 const xmlChar *ns_uri,
1237 xmlXPathObjectPtr value) {
1238 if (ctxt == NULL)
1239 return(-1);
1240 if (name == NULL)
1241 return(-1);
1242
1243 if (ctxt->varHash == NULL)
1244 ctxt->varHash = xmlHashCreate(0);
1245 if (ctxt->varHash == NULL)
1246 return(-1);
1247 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
1248 (void *) value,
1249 (xmlHashDeallocator)xmlXPathFreeObject));
1250}
1251
1252/**
1253 * xmlXPathRegisterVariableLookup:
1254 * @ctxt: the XPath context
1255 * @f: the lookup function
1256 * @data: the lookup data
1257 *
1258 * register an external mechanism to do variable lookup
1259 */
1260void
1261xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
1262 xmlXPathVariableLookupFunc f, void *data) {
1263 if (ctxt == NULL)
1264 return;
1265 ctxt->varLookupFunc = (void *) f;
1266 ctxt->varLookupData = data;
1267}
1268
1269/**
1270 * xmlXPathVariableLookup:
1271 * @ctxt: the XPath context
1272 * @name: the variable name
1273 *
1274 * Search in the Variable array of the context for the given
1275 * variable value.
1276 *
1277 * Returns the value or NULL if not found
1278 */
1279xmlXPathObjectPtr
1280xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
1281 if (ctxt == NULL)
1282 return(NULL);
1283
1284 if (ctxt->varLookupFunc != NULL) {
1285 xmlXPathObjectPtr ret;
1286
1287 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1288 (ctxt->varLookupData, name, NULL);
1289 if (ret != NULL) return(ret);
1290 }
1291 return(xmlXPathVariableLookupNS(ctxt, name, NULL));
1292}
1293
1294/**
1295 * xmlXPathVariableLookupNS:
1296 * @ctxt: the XPath context
1297 * @name: the variable name
1298 * @ns_uri: the variable namespace URI
1299 *
1300 * Search in the Variable array of the context for the given
1301 * variable value.
1302 *
1303 * Returns the value or NULL if not found
1304 */
1305xmlXPathObjectPtr
1306xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
1307 const xmlChar *ns_uri) {
1308 if (ctxt == NULL)
1309 return(NULL);
1310
1311 if (ctxt->varLookupFunc != NULL) {
1312 xmlXPathObjectPtr ret;
1313
1314 ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
1315 (ctxt->varLookupData, name, ns_uri);
1316 if (ret != NULL) return(ret);
1317 }
1318
1319 if (ctxt->varHash == NULL)
1320 return(NULL);
1321 if (name == NULL)
1322 return(NULL);
1323
1324 return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
1325}
1326
1327/**
1328 * xmlXPathRegisteredVariablesCleanup:
1329 * @ctxt: the XPath context
1330 *
1331 * Cleanup the XPath context data associated to registered variables
1332 */
1333void
1334xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
1335 if (ctxt == NULL)
1336 return;
1337
1338 xmlHashFree(ctxt->varHash, NULL);
1339 ctxt->varHash = NULL;
1340}
1341
1342/**
1343 * xmlXPathRegisterNs:
1344 * @ctxt: the XPath context
1345 * @prefix: the namespace prefix
1346 * @ns_uri: the namespace name
1347 *
1348 * Register a new namespace. If @ns_uri is NULL it unregisters
1349 * the namespace
1350 *
1351 * Returns 0 in case of success, -1 in case of error
1352 */
1353int
1354xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
1355 const xmlChar *ns_uri) {
1356 if (ctxt == NULL)
1357 return(-1);
1358 if (prefix == NULL)
1359 return(-1);
1360
1361 if (ctxt->nsHash == NULL)
1362 ctxt->nsHash = xmlHashCreate(10);
1363 if (ctxt->nsHash == NULL)
1364 return(-1);
1365 return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
1366 (xmlHashDeallocator)xmlFree));
1367}
1368
1369/**
1370 * xmlXPathNsLookup:
1371 * @ctxt: the XPath context
1372 * @prefix: the namespace prefix value
1373 *
1374 * Search in the namespace declaration array of the context for the given
1375 * namespace name associated to the given prefix
1376 *
1377 * Returns the value or NULL if not found
1378 */
1379const xmlChar *
1380xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
1381 if (ctxt == NULL)
1382 return(NULL);
1383 if (prefix == NULL)
1384 return(NULL);
1385
1386#ifdef XML_XML_NAMESPACE
1387 if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
1388 return(XML_XML_NAMESPACE);
1389#endif
1390
1391 if (ctxt->nsHash == NULL)
1392 return(NULL);
1393
1394 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
1395}
1396
1397/**
1398 * xmlXPathRegisteredVariablesCleanup:
1399 * @ctxt: the XPath context
1400 *
1401 * Cleanup the XPath context data associated to registered variables
1402 */
1403void
1404xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
1405 if (ctxt == NULL)
1406 return;
1407
1408 xmlHashFree(ctxt->nsHash, NULL);
1409 ctxt->nsHash = NULL;
1410}
1411
1412/************************************************************************
1413 * *
1414 * Routines to handle Values *
1415 * *
1416 ************************************************************************/
1417
1418/* Allocations are terrible, one need to optimize all this !!! */
1419
1420/**
1421 * xmlXPathNewFloat:
1422 * @val: the double value
1423 *
1424 * Create a new xmlXPathObjectPtr of type double and of value @val
1425 *
1426 * Returns the newly created object.
1427 */
1428xmlXPathObjectPtr
1429xmlXPathNewFloat(double val) {
1430 xmlXPathObjectPtr ret;
1431
1432 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1433 if (ret == NULL) {
1434 xmlGenericError(xmlGenericErrorContext,
1435 "xmlXPathNewFloat: out of memory\n");
1436 return(NULL);
1437 }
1438 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1439 ret->type = XPATH_NUMBER;
1440 ret->floatval = val;
1441 return(ret);
1442}
1443
1444/**
1445 * xmlXPathNewBoolean:
1446 * @val: the boolean value
1447 *
1448 * Create a new xmlXPathObjectPtr of type boolean and of value @val
1449 *
1450 * Returns the newly created object.
1451 */
1452xmlXPathObjectPtr
1453xmlXPathNewBoolean(int val) {
1454 xmlXPathObjectPtr ret;
1455
1456 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1457 if (ret == NULL) {
1458 xmlGenericError(xmlGenericErrorContext,
1459 "xmlXPathNewBoolean: out of memory\n");
1460 return(NULL);
1461 }
1462 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1463 ret->type = XPATH_BOOLEAN;
1464 ret->boolval = (val != 0);
1465 return(ret);
1466}
1467
1468/**
1469 * xmlXPathNewString:
1470 * @val: the xmlChar * value
1471 *
1472 * Create a new xmlXPathObjectPtr of type string and of value @val
1473 *
1474 * Returns the newly created object.
1475 */
1476xmlXPathObjectPtr
1477xmlXPathNewString(const xmlChar *val) {
1478 xmlXPathObjectPtr ret;
1479
1480 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1481 if (ret == NULL) {
1482 xmlGenericError(xmlGenericErrorContext,
1483 "xmlXPathNewString: out of memory\n");
1484 return(NULL);
1485 }
1486 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1487 ret->type = XPATH_STRING;
1488 if (val != NULL)
1489 ret->stringval = xmlStrdup(val);
1490 else
1491 ret->stringval = xmlStrdup((const xmlChar *)"");
1492 return(ret);
1493}
1494
1495/**
1496 * xmlXPathNewCString:
1497 * @val: the char * value
1498 *
1499 * Create a new xmlXPathObjectPtr of type string and of value @val
1500 *
1501 * Returns the newly created object.
1502 */
1503xmlXPathObjectPtr
1504xmlXPathNewCString(const char *val) {
1505 xmlXPathObjectPtr ret;
1506
1507 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1508 if (ret == NULL) {
1509 xmlGenericError(xmlGenericErrorContext,
1510 "xmlXPathNewCString: out of memory\n");
1511 return(NULL);
1512 }
1513 memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
1514 ret->type = XPATH_STRING;
1515 ret->stringval = xmlStrdup(BAD_CAST val);
1516 return(ret);
1517}
1518
1519/**
1520 * xmlXPathObjectCopy:
1521 * @val: the original object
1522 *
1523 * allocate a new copy of a given object
1524 *
1525 * Returns the newly created object.
1526 */
1527xmlXPathObjectPtr
1528xmlXPathObjectCopy(xmlXPathObjectPtr val) {
1529 xmlXPathObjectPtr ret;
1530
1531 if (val == NULL)
1532 return(NULL);
1533
1534 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
1535 if (ret == NULL) {
1536 xmlGenericError(xmlGenericErrorContext,
1537 "xmlXPathObjectCopy: out of memory\n");
1538 return(NULL);
1539 }
1540 memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
1541 switch (val->type) {
1542 case XPATH_BOOLEAN:
1543 case XPATH_NUMBER:
1544 case XPATH_POINT:
1545 case XPATH_RANGE:
1546 break;
1547 case XPATH_STRING:
1548 ret->stringval = xmlStrdup(val->stringval);
1549 break;
1550 case XPATH_XSLT_TREE:
1551 if ((val->nodesetval != NULL) &&
1552 (val->nodesetval->nodeTab != NULL))
1553 ret->nodesetval = xmlXPathNodeSetCreate(
1554 xmlCopyNode(val->nodesetval->nodeTab[0], 1));
1555 else
1556 ret->nodesetval = xmlXPathNodeSetCreate(NULL);
1557 break;
1558 case XPATH_NODESET:
1559 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
1560 break;
1561 case XPATH_LOCATIONSET:
1562#ifdef LIBXML_XPTR_ENABLED
1563 {
1564 xmlLocationSetPtr loc = val->user;
1565 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
1566 break;
1567 }
1568#endif
1569 case XPATH_UNDEFINED:
1570 case XPATH_USERS:
1571 xmlGenericError(xmlGenericErrorContext,
1572 "xmlXPathObjectCopy: unsupported type %d\n",
1573 val->type);
1574 break;
1575 }
1576 return(ret);
1577}
1578
1579/**
1580 * xmlXPathFreeObject:
1581 * @obj: the object to free
1582 *
1583 * Free up an xmlXPathObjectPtr object.
1584 */
1585void
1586xmlXPathFreeObject(xmlXPathObjectPtr obj) {
1587 if (obj == NULL) return;
1588 if (obj->type == XPATH_NODESET) {
1589 if (obj->nodesetval != NULL)
1590 xmlXPathFreeNodeSet(obj->nodesetval);
1591#ifdef LIBXML_XPTR_ENABLED
1592 } else if (obj->type == XPATH_LOCATIONSET) {
1593 if (obj->user != NULL)
1594 xmlXPtrFreeLocationSet(obj->user);
1595#endif
1596 } else if (obj->type == XPATH_STRING) {
1597 if (obj->stringval != NULL)
1598 xmlFree(obj->stringval);
1599 } else if (obj->type == XPATH_XSLT_TREE) {
1600 if (obj->nodesetval != NULL)
1601 xmlXPathFreeValueTree(obj->nodesetval);
1602 }
1603
Daniel Veillard48b2f892001-02-25 16:11:03 +00001604 MEM_CLEANUP(obj, (size_t) sizeof(xmlXPathObject));
Owen Taylor3473f882001-02-23 17:55:21 +00001605 xmlFree(obj);
1606}
1607
1608/************************************************************************
1609 * *
1610 * Routines to handle XPath contexts *
1611 * *
1612 ************************************************************************/
1613
1614/**
1615 * xmlXPathNewContext:
1616 * @doc: the XML document
1617 *
1618 * Create a new xmlXPathContext
1619 *
1620 * Returns the xmlXPathContext just allocated.
1621 */
1622xmlXPathContextPtr
1623xmlXPathNewContext(xmlDocPtr doc) {
1624 xmlXPathContextPtr ret;
1625
1626 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
1627 if (ret == NULL) {
1628 xmlGenericError(xmlGenericErrorContext,
1629 "xmlXPathNewContext: out of memory\n");
1630 return(NULL);
1631 }
1632 memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
1633 ret->doc = doc;
1634 ret->node = NULL;
1635
1636 ret->varHash = NULL;
1637
1638 ret->nb_types = 0;
1639 ret->max_types = 0;
1640 ret->types = NULL;
1641
1642 ret->funcHash = xmlHashCreate(0);
1643
1644 ret->nb_axis = 0;
1645 ret->max_axis = 0;
1646 ret->axis = NULL;
1647
1648 ret->nsHash = NULL;
1649 ret->user = NULL;
1650
1651 ret->contextSize = -1;
1652 ret->proximityPosition = -1;
1653
1654 xmlXPathRegisterAllFunctions(ret);
1655
1656 return(ret);
1657}
1658
1659/**
1660 * xmlXPathFreeContext:
1661 * @ctxt: the context to free
1662 *
1663 * Free up an xmlXPathContext
1664 */
1665void
1666xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
1667 xmlXPathRegisteredNsCleanup(ctxt);
1668 xmlXPathRegisteredFuncsCleanup(ctxt);
1669 xmlXPathRegisteredVariablesCleanup(ctxt);
Daniel Veillard48b2f892001-02-25 16:11:03 +00001670 MEM_CLEANUP(ctxt, (size_t) sizeof(xmlXPathContext));
Owen Taylor3473f882001-02-23 17:55:21 +00001671 xmlFree(ctxt);
1672}
1673
1674/************************************************************************
1675 * *
1676 * Routines to handle XPath parser contexts *
1677 * *
1678 ************************************************************************/
1679
1680#define CHECK_CTXT(ctxt) \
1681 if (ctxt == NULL) { \
1682 xmlGenericError(xmlGenericErrorContext, \
1683 "%s:%d Internal error: ctxt == NULL\n", \
1684 __FILE__, __LINE__); \
1685 } \
1686
1687
1688#define CHECK_CONTEXT(ctxt) \
1689 if (ctxt == NULL) { \
1690 xmlGenericError(xmlGenericErrorContext, \
1691 "%s:%d Internal error: no context\n", \
1692 __FILE__, __LINE__); \
1693 } \
1694 else if (ctxt->doc == NULL) { \
1695 xmlGenericError(xmlGenericErrorContext, \
1696 "%s:%d Internal error: no document\n", \
1697 __FILE__, __LINE__); \
1698 } \
1699 else if (ctxt->doc->children == NULL) { \
1700 xmlGenericError(xmlGenericErrorContext, \
1701 "%s:%d Internal error: document without root\n", \
1702 __FILE__, __LINE__); \
1703 } \
1704
1705
1706/**
1707 * xmlXPathNewParserContext:
1708 * @str: the XPath expression
1709 * @ctxt: the XPath context
1710 *
1711 * Create a new xmlXPathParserContext
1712 *
1713 * Returns the xmlXPathParserContext just allocated.
1714 */
1715xmlXPathParserContextPtr
1716xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
1717 xmlXPathParserContextPtr ret;
1718
1719 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
1720 if (ret == NULL) {
1721 xmlGenericError(xmlGenericErrorContext,
1722 "xmlXPathNewParserContext: out of memory\n");
1723 return(NULL);
1724 }
1725 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
1726 ret->cur = ret->base = str;
1727 ret->context = ctxt;
1728
1729 /* Allocate the value stack */
1730 ret->valueTab = (xmlXPathObjectPtr *)
1731 xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
1732 ret->valueNr = 0;
1733 ret->valueMax = 10;
1734 ret->value = NULL;
1735 return(ret);
1736}
1737
1738/**
1739 * xmlXPathFreeParserContext:
1740 * @ctxt: the context to free
1741 *
1742 * Free up an xmlXPathParserContext
1743 */
1744void
1745xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
1746 if (ctxt->valueTab != NULL) {
Daniel Veillard48b2f892001-02-25 16:11:03 +00001747 MEM_CLEANUP(ctxt->valueTab, 10 * (size_t) sizeof(xmlXPathObjectPtr));
Owen Taylor3473f882001-02-23 17:55:21 +00001748 xmlFree(ctxt->valueTab);
1749 }
Daniel Veillard48b2f892001-02-25 16:11:03 +00001750 MEM_CLEANUP(ctxt, (size_t) sizeof(xmlXPathParserContext));
Owen Taylor3473f882001-02-23 17:55:21 +00001751 xmlFree(ctxt);
1752}
1753
1754/************************************************************************
1755 * *
1756 * The implicit core function library *
1757 * *
1758 ************************************************************************/
1759
1760/*
1761 * Auto-pop and cast to a number
1762 */
1763void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
1764
1765
1766#define POP_FLOAT \
1767 arg = valuePop(ctxt); \
1768 if (arg == NULL) { \
1769 XP_ERROR(XPATH_INVALID_OPERAND); \
1770 } \
1771 if (arg->type != XPATH_NUMBER) { \
1772 valuePush(ctxt, arg); \
1773 xmlXPathNumberFunction(ctxt, 1); \
1774 arg = valuePop(ctxt); \
1775 }
1776
1777/**
1778 * xmlXPathCompareNodeSetFloat:
1779 * @ctxt: the XPath Parser context
1780 * @inf: less than (1) or greater than (0)
1781 * @strict: is the comparison strict
1782 * @arg: the node set
1783 * @f: the value
1784 *
1785 * Implement the compare operation between a nodeset and a number
1786 * @ns < @val (1, 1, ...
1787 * @ns <= @val (1, 0, ...
1788 * @ns > @val (0, 1, ...
1789 * @ns >= @val (0, 0, ...
1790 *
1791 * If one object to be compared is a node-set and the other is a number,
1792 * then the comparison will be true if and only if there is a node in the
1793 * node-set such that the result of performing the comparison on the number
1794 * to be compared and on the result of converting the string-value of that
1795 * node to a number using the number function is true.
1796 *
1797 * Returns 0 or 1 depending on the results of the test.
1798 */
1799int
1800xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
1801 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
1802 int i, ret = 0;
1803 xmlNodeSetPtr ns;
1804 xmlChar *str2;
1805
1806 if ((f == NULL) || (arg == NULL) ||
1807 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
1808 xmlXPathFreeObject(arg);
1809 xmlXPathFreeObject(f);
1810 return(0);
1811 }
1812 ns = arg->nodesetval;
1813 for (i = 0;i < ns->nodeNr;i++) {
1814 str2 = xmlNodeGetContent(ns->nodeTab[i]);
1815 if (str2 != NULL) {
1816 valuePush(ctxt,
1817 xmlXPathNewString(str2));
1818 xmlFree(str2);
1819 xmlXPathNumberFunction(ctxt, 1);
1820 valuePush(ctxt, xmlXPathObjectCopy(f));
1821 ret = xmlXPathCompareValues(ctxt, inf, strict);
1822 if (ret)
1823 break;
1824 }
1825 }
1826 xmlXPathFreeObject(arg);
1827 xmlXPathFreeObject(f);
1828 return(ret);
1829}
1830
1831/**
1832 * xmlXPathCompareNodeSetString:
1833 * @ctxt: the XPath Parser context
1834 * @inf: less than (1) or greater than (0)
1835 * @strict: is the comparison strict
1836 * @arg: the node set
1837 * @s: the value
1838 *
1839 * Implement the compare operation between a nodeset and a string
1840 * @ns < @val (1, 1, ...
1841 * @ns <= @val (1, 0, ...
1842 * @ns > @val (0, 1, ...
1843 * @ns >= @val (0, 0, ...
1844 *
1845 * If one object to be compared is a node-set and the other is a string,
1846 * then the comparison will be true if and only if there is a node in
1847 * the node-set such that the result of performing the comparison on the
1848 * string-value of the node and the other string is true.
1849 *
1850 * Returns 0 or 1 depending on the results of the test.
1851 */
1852int
1853xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
1854 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
1855 int i, ret = 0;
1856 xmlNodeSetPtr ns;
1857 xmlChar *str2;
1858
1859 if ((s == NULL) || (arg == NULL) ||
1860 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
1861 xmlXPathFreeObject(arg);
1862 xmlXPathFreeObject(s);
1863 return(0);
1864 }
1865 ns = arg->nodesetval;
1866 for (i = 0;i < ns->nodeNr;i++) {
1867 str2 = xmlNodeGetContent(ns->nodeTab[i]);
1868 if (str2 != NULL) {
1869 valuePush(ctxt,
1870 xmlXPathNewString(str2));
1871 xmlFree(str2);
1872 valuePush(ctxt, xmlXPathObjectCopy(s));
1873 ret = xmlXPathCompareValues(ctxt, inf, strict);
1874 if (ret)
1875 break;
1876 }
1877 }
1878 xmlXPathFreeObject(arg);
1879 xmlXPathFreeObject(s);
1880 return(ret);
1881}
1882
1883/**
1884 * xmlXPathCompareNodeSets:
1885 * @ctxt: the XPath Parser context
1886 * @op: less than (-1), equal (0) or greater than (1)
1887 * @strict: is the comparison strict
1888 * @arg1: the fist node set object
1889 * @arg2: the second node set object
1890 *
1891 * Implement the compare operation on nodesets:
1892 *
1893 * If both objects to be compared are node-sets, then the comparison
1894 * will be true if and only if there is a node in the first node-set
1895 * and a node in the second node-set such that the result of performing
1896 * the comparison on the string-values of the two nodes is true.
1897 * ....
1898 * When neither object to be compared is a node-set and the operator
1899 * is <=, <, >= or >, then the objects are compared by converting both
1900 * objects to numbers and comparing the numbers according to IEEE 754.
1901 * ....
1902 * The number function converts its argument to a number as follows:
1903 * - a string that consists of optional whitespace followed by an
1904 * optional minus sign followed by a Number followed by whitespace
1905 * is converted to the IEEE 754 number that is nearest (according
1906 * to the IEEE 754 round-to-nearest rule) to the mathematical value
1907 * represented by the string; any other string is converted to NaN
1908 *
1909 * Conclusion all nodes need to be converted first to their string value
1910 * and then the comparison must be done when possible
1911 */
1912int
1913xmlXPathCompareNodeSets(xmlXPathParserContextPtr ctxt, int inf, int strict,
1914 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
1915 int i, j, init = 0;
1916 double val1;
1917 double *values2;
1918 int ret = 0;
1919 xmlChar *str;
1920 xmlNodeSetPtr ns1;
1921 xmlNodeSetPtr ns2;
1922
1923 if ((arg1 == NULL) ||
1924 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
1925 return(0);
1926 if ((arg2 == NULL) ||
1927 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
1928 return(0);
1929
1930 ns1 = arg1->nodesetval;
1931 ns2 = arg2->nodesetval;
1932
1933 if (ns1->nodeNr <= 0)
1934 return(0);
1935 if (ns2->nodeNr <= 0)
1936 return(0);
1937
1938 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
1939 if (values2 == NULL) {
1940 return(0);
1941 }
1942 for (i = 0;i < ns1->nodeNr;i++) {
1943 str = xmlNodeGetContent(ns1->nodeTab[i]);
1944 if (str == NULL)
1945 continue;
1946 val1 = xmlXPathStringEvalNumber(str);
1947 xmlFree(str);
1948 if (isnan(val1))
1949 continue;
1950 for (j = 0;j < ns2->nodeNr;j++) {
1951 if (init == 0) {
1952 str = xmlNodeGetContent(ns2->nodeTab[j]);
1953 if (str == NULL) {
1954 values2[j] = xmlXPathNAN;
1955 } else {
1956 values2[j] = xmlXPathStringEvalNumber(str);
1957 xmlFree(str);
1958 }
1959 }
1960 if (isnan(values2[j]))
1961 continue;
1962 if (inf && strict)
1963 ret = (val1 < values2[j]);
1964 else if (inf && !strict)
1965 ret = (val1 <= values2[j]);
1966 else if (!inf && strict)
1967 ret = (val1 > values2[j]);
1968 else if (!inf && !strict)
1969 ret = (val1 >= values2[j]);
1970 if (ret)
1971 break;
1972 }
1973 if (ret)
1974 break;
1975 init = 1;
1976 }
1977 xmlFree(values2);
1978 return(ret);
1979 return(0);
1980}
1981
1982/**
1983 * xmlXPathCompareNodeSetValue:
1984 * @ctxt: the XPath Parser context
1985 * @inf: less than (1) or greater than (0)
1986 * @strict: is the comparison strict
1987 * @arg: the node set
1988 * @val: the value
1989 *
1990 * Implement the compare operation between a nodeset and a value
1991 * @ns < @val (1, 1, ...
1992 * @ns <= @val (1, 0, ...
1993 * @ns > @val (0, 1, ...
1994 * @ns >= @val (0, 0, ...
1995 *
1996 * If one object to be compared is a node-set and the other is a boolean,
1997 * then the comparison will be true if and only if the result of performing
1998 * the comparison on the boolean and on the result of converting
1999 * the node-set to a boolean using the boolean function is true.
2000 *
2001 * Returns 0 or 1 depending on the results of the test.
2002 */
2003int
2004xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
2005 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
2006 if ((val == NULL) || (arg == NULL) ||
2007 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2008 return(0);
2009
2010 switch(val->type) {
2011 case XPATH_NUMBER:
2012 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
2013 case XPATH_NODESET:
2014 case XPATH_XSLT_TREE:
2015 return(xmlXPathCompareNodeSets(ctxt, inf, strict, arg, val));
2016 case XPATH_STRING:
2017 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
2018 case XPATH_BOOLEAN:
2019 valuePush(ctxt, arg);
2020 xmlXPathBooleanFunction(ctxt, 1);
2021 valuePush(ctxt, val);
2022 return(xmlXPathCompareValues(ctxt, inf, strict));
2023 default:
2024 TODO
2025 return(0);
2026 }
2027 return(0);
2028}
2029
2030/**
2031 * xmlXPathEqualNodeSetString
2032 * @arg: the nodeset object argument
2033 * @str: the string to compare to.
2034 *
2035 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2036 * If one object to be compared is a node-set and the other is a string,
2037 * then the comparison will be true if and only if there is a node in
2038 * the node-set such that the result of performing the comparison on the
2039 * string-value of the node and the other string is true.
2040 *
2041 * Returns 0 or 1 depending on the results of the test.
2042 */
2043int
2044xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
2045 int i;
2046 xmlNodeSetPtr ns;
2047 xmlChar *str2;
2048
2049 if ((str == NULL) || (arg == NULL) ||
2050 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2051 return(0);
2052 ns = arg->nodesetval;
2053 if (ns->nodeNr <= 0)
2054 return(0);
2055 for (i = 0;i < ns->nodeNr;i++) {
2056 str2 = xmlNodeGetContent(ns->nodeTab[i]);
2057 if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
2058 xmlFree(str2);
2059 return(1);
2060 }
2061 if (str2 != NULL)
2062 xmlFree(str2);
2063 }
2064 return(0);
2065}
2066
2067/**
2068 * xmlXPathEqualNodeSetFloat
2069 * @arg: the nodeset object argument
2070 * @f: the float to compare to
2071 *
2072 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2073 * If one object to be compared is a node-set and the other is a number,
2074 * then the comparison will be true if and only if there is a node in
2075 * the node-set such that the result of performing the comparison on the
2076 * number to be compared and on the result of converting the string-value
2077 * of that node to a number using the number function is true.
2078 *
2079 * Returns 0 or 1 depending on the results of the test.
2080 */
2081int
2082xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
2083 char buf[100] = "";
2084
2085 if ((arg == NULL) ||
2086 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
2087 return(0);
2088
2089 if (isnan(f))
2090 sprintf(buf, "NaN");
2091 else if (isinf(f) > 0)
2092 sprintf(buf, "+Infinity");
2093 else if (isinf(f) < 0)
2094 sprintf(buf, "-Infinity");
2095 else
2096 sprintf(buf, "%0g", f);
2097
2098 return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
2099}
2100
2101
2102/**
2103 * xmlXPathEqualNodeSets
2104 * @arg1: first nodeset object argument
2105 * @arg2: second nodeset object argument
2106 *
2107 * Implement the equal operation on XPath nodesets: @arg1 == @arg2
2108 * If both objects to be compared are node-sets, then the comparison
2109 * will be true if and only if there is a node in the first node-set and
2110 * a node in the second node-set such that the result of performing the
2111 * comparison on the string-values of the two nodes is true.
2112 *
2113 * (needless to say, this is a costly operation)
2114 *
2115 * Returns 0 or 1 depending on the results of the test.
2116 */
2117int
2118xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
2119 int i, j;
2120 xmlChar **values1;
2121 xmlChar **values2;
2122 int ret = 0;
2123 xmlNodeSetPtr ns1;
2124 xmlNodeSetPtr ns2;
2125
2126 if ((arg1 == NULL) ||
2127 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
2128 return(0);
2129 if ((arg2 == NULL) ||
2130 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
2131 return(0);
2132
2133 ns1 = arg1->nodesetval;
2134 ns2 = arg2->nodesetval;
2135
2136 if (ns1->nodeNr <= 0)
2137 return(0);
2138 if (ns2->nodeNr <= 0)
2139 return(0);
2140
2141 /*
2142 * check if there is a node pertaining to both sets
2143 */
2144 for (i = 0;i < ns1->nodeNr;i++)
2145 for (j = 0;j < ns2->nodeNr;j++)
2146 if (ns1->nodeTab[i] == ns2->nodeTab[j])
2147 return(1);
2148
2149 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
2150 if (values1 == NULL)
2151 return(0);
2152 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
2153 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
2154 if (values2 == NULL) {
2155 xmlFree(values1);
2156 return(0);
2157 }
2158 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
2159 for (i = 0;i < ns1->nodeNr;i++) {
2160 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
2161 for (j = 0;j < ns2->nodeNr;j++) {
2162 if (i == 0)
2163 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
2164 ret = xmlStrEqual(values1[i], values2[j]);
2165 if (ret)
2166 break;
2167 }
2168 if (ret)
2169 break;
2170 }
2171 for (i = 0;i < ns1->nodeNr;i++)
2172 if (values1[i] != NULL)
2173 xmlFree(values1[i]);
2174 for (j = 0;j < ns2->nodeNr;j++)
2175 if (values2[j] != NULL)
2176 xmlFree(values2[j]);
2177 xmlFree(values1);
2178 xmlFree(values2);
2179 return(ret);
2180}
2181
2182/**
2183 * xmlXPathEqualValues:
2184 * @ctxt: the XPath Parser context
2185 *
2186 * Implement the equal operation on XPath objects content: @arg1 == @arg2
2187 *
2188 * Returns 0 or 1 depending on the results of the test.
2189 */
2190int
2191xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
2192 xmlXPathObjectPtr arg1, arg2;
2193 int ret = 0;
2194
2195 arg1 = valuePop(ctxt);
2196 if (arg1 == NULL)
2197 XP_ERROR0(XPATH_INVALID_OPERAND);
2198
2199 arg2 = valuePop(ctxt);
2200 if (arg2 == NULL) {
2201 xmlXPathFreeObject(arg1);
2202 XP_ERROR0(XPATH_INVALID_OPERAND);
2203 }
2204
2205 if (arg1 == arg2) {
2206#ifdef DEBUG_EXPR
2207 xmlGenericError(xmlGenericErrorContext,
2208 "Equal: by pointer\n");
2209#endif
2210 return(1);
2211 }
2212
2213 switch (arg1->type) {
2214 case XPATH_UNDEFINED:
2215#ifdef DEBUG_EXPR
2216 xmlGenericError(xmlGenericErrorContext,
2217 "Equal: undefined\n");
2218#endif
2219 break;
2220 case XPATH_XSLT_TREE:
2221 case XPATH_NODESET:
2222 switch (arg2->type) {
2223 case XPATH_UNDEFINED:
2224#ifdef DEBUG_EXPR
2225 xmlGenericError(xmlGenericErrorContext,
2226 "Equal: undefined\n");
2227#endif
2228 break;
2229 case XPATH_XSLT_TREE:
2230 case XPATH_NODESET:
2231 ret = xmlXPathEqualNodeSets(arg1, arg2);
2232 break;
2233 case XPATH_BOOLEAN:
2234 if ((arg1->nodesetval == NULL) ||
2235 (arg1->nodesetval->nodeNr == 0)) ret = 0;
2236 else
2237 ret = 1;
2238 ret = (ret == arg2->boolval);
2239 break;
2240 case XPATH_NUMBER:
2241 ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
2242 break;
2243 case XPATH_STRING:
2244 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
2245 break;
2246 case XPATH_USERS:
2247 case XPATH_POINT:
2248 case XPATH_RANGE:
2249 case XPATH_LOCATIONSET:
2250 TODO
2251 break;
2252 }
2253 break;
2254 case XPATH_BOOLEAN:
2255 switch (arg2->type) {
2256 case XPATH_UNDEFINED:
2257#ifdef DEBUG_EXPR
2258 xmlGenericError(xmlGenericErrorContext,
2259 "Equal: undefined\n");
2260#endif
2261 break;
2262 case XPATH_NODESET:
2263 case XPATH_XSLT_TREE:
2264 if ((arg2->nodesetval == NULL) ||
2265 (arg2->nodesetval->nodeNr == 0)) ret = 0;
2266 else
2267 ret = 1;
2268 break;
2269 case XPATH_BOOLEAN:
2270#ifdef DEBUG_EXPR
2271 xmlGenericError(xmlGenericErrorContext,
2272 "Equal: %d boolean %d \n",
2273 arg1->boolval, arg2->boolval);
2274#endif
2275 ret = (arg1->boolval == arg2->boolval);
2276 break;
2277 case XPATH_NUMBER:
2278 if (arg2->floatval) ret = 1;
2279 else ret = 0;
2280 ret = (arg1->boolval == ret);
2281 break;
2282 case XPATH_STRING:
2283 if ((arg2->stringval == NULL) ||
2284 (arg2->stringval[0] == 0)) ret = 0;
2285 else
2286 ret = 1;
2287 ret = (arg1->boolval == ret);
2288 break;
2289 case XPATH_USERS:
2290 case XPATH_POINT:
2291 case XPATH_RANGE:
2292 case XPATH_LOCATIONSET:
2293 TODO
2294 break;
2295 }
2296 break;
2297 case XPATH_NUMBER:
2298 switch (arg2->type) {
2299 case XPATH_UNDEFINED:
2300#ifdef DEBUG_EXPR
2301 xmlGenericError(xmlGenericErrorContext,
2302 "Equal: undefined\n");
2303#endif
2304 break;
2305 case XPATH_NODESET:
2306 case XPATH_XSLT_TREE:
2307 ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
2308 break;
2309 case XPATH_BOOLEAN:
2310 if (arg1->floatval) ret = 1;
2311 else ret = 0;
2312 ret = (arg2->boolval == ret);
2313 break;
2314 case XPATH_STRING:
2315 valuePush(ctxt, arg2);
2316 xmlXPathNumberFunction(ctxt, 1);
2317 arg2 = valuePop(ctxt);
2318 /* no break on purpose */
2319 case XPATH_NUMBER:
2320 ret = (arg1->floatval == arg2->floatval);
2321 break;
2322 case XPATH_USERS:
2323 case XPATH_POINT:
2324 case XPATH_RANGE:
2325 case XPATH_LOCATIONSET:
2326 TODO
2327 break;
2328 }
2329 break;
2330 case XPATH_STRING:
2331 switch (arg2->type) {
2332 case XPATH_UNDEFINED:
2333#ifdef DEBUG_EXPR
2334 xmlGenericError(xmlGenericErrorContext,
2335 "Equal: undefined\n");
2336#endif
2337 break;
2338 case XPATH_NODESET:
2339 case XPATH_XSLT_TREE:
2340 ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
2341 break;
2342 case XPATH_BOOLEAN:
2343 if ((arg1->stringval == NULL) ||
2344 (arg1->stringval[0] == 0)) ret = 0;
2345 else
2346 ret = 1;
2347 ret = (arg2->boolval == ret);
2348 break;
2349 case XPATH_STRING:
2350 ret = xmlStrEqual(arg1->stringval, arg2->stringval);
2351 break;
2352 case XPATH_NUMBER:
2353 valuePush(ctxt, arg1);
2354 xmlXPathNumberFunction(ctxt, 1);
2355 arg1 = valuePop(ctxt);
2356 ret = (arg1->floatval == arg2->floatval);
2357 break;
2358 case XPATH_USERS:
2359 case XPATH_POINT:
2360 case XPATH_RANGE:
2361 case XPATH_LOCATIONSET:
2362 TODO
2363 break;
2364 }
2365 break;
2366 case XPATH_USERS:
2367 case XPATH_POINT:
2368 case XPATH_RANGE:
2369 case XPATH_LOCATIONSET:
2370 TODO
2371 break;
2372 }
2373 xmlXPathFreeObject(arg1);
2374 xmlXPathFreeObject(arg2);
2375 return(ret);
2376}
2377
2378
2379/**
2380 * xmlXPathCompareValues:
2381 * @ctxt: the XPath Parser context
2382 * @inf: less than (1) or greater than (0)
2383 * @strict: is the comparison strict
2384 *
2385 * Implement the compare operation on XPath objects:
2386 * @arg1 < @arg2 (1, 1, ...
2387 * @arg1 <= @arg2 (1, 0, ...
2388 * @arg1 > @arg2 (0, 1, ...
2389 * @arg1 >= @arg2 (0, 0, ...
2390 *
2391 * When neither object to be compared is a node-set and the operator is
2392 * <=, <, >=, >, then the objects are compared by converted both objects
2393 * to numbers and comparing the numbers according to IEEE 754. The <
2394 * comparison will be true if and only if the first number is less than the
2395 * second number. The <= comparison will be true if and only if the first
2396 * number is less than or equal to the second number. The > comparison
2397 * will be true if and only if the first number is greater than the second
2398 * number. The >= comparison will be true if and only if the first number
2399 * is greater than or equal to the second number.
2400 *
2401 * Returns 1 if the comparaison succeeded, 0 if it failed
2402 */
2403int
2404xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
2405 int ret = 0;
2406 xmlXPathObjectPtr arg1, arg2;
2407
2408 arg2 = valuePop(ctxt);
2409 if (arg2 == NULL) {
2410 XP_ERROR0(XPATH_INVALID_OPERAND);
2411 }
2412
2413 arg1 = valuePop(ctxt);
2414 if (arg1 == NULL) {
2415 xmlXPathFreeObject(arg2);
2416 XP_ERROR0(XPATH_INVALID_OPERAND);
2417 }
2418
2419 if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
2420 if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
2421 ret = xmlXPathCompareNodeSets(ctxt, inf, strict, arg1, arg2);
2422 } else {
2423 if (arg1->type == XPATH_NODESET) {
2424 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict, arg1, arg2);
2425 } else {
Daniel Veillardbbd51d52001-02-24 03:07:03 +00002426 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, !strict, arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00002427 }
2428 }
2429 return(ret);
2430 }
2431
2432 if (arg1->type != XPATH_NUMBER) {
2433 valuePush(ctxt, arg1);
2434 xmlXPathNumberFunction(ctxt, 1);
2435 arg1 = valuePop(ctxt);
2436 }
2437 if (arg1->type != XPATH_NUMBER) {
2438 xmlXPathFreeObject(arg1);
2439 xmlXPathFreeObject(arg2);
2440 XP_ERROR0(XPATH_INVALID_OPERAND);
2441 }
2442 if (arg2->type != XPATH_NUMBER) {
2443 valuePush(ctxt, arg2);
2444 xmlXPathNumberFunction(ctxt, 1);
2445 arg2 = valuePop(ctxt);
2446 }
2447 if (arg2->type != XPATH_NUMBER) {
2448 xmlXPathFreeObject(arg1);
2449 xmlXPathFreeObject(arg2);
2450 XP_ERROR0(XPATH_INVALID_OPERAND);
2451 }
2452 /*
2453 * Add tests for infinity and nan
2454 * => feedback on 3.4 for Inf and NaN
2455 */
2456 if (inf && strict)
2457 ret = (arg1->floatval < arg2->floatval);
2458 else if (inf && !strict)
2459 ret = (arg1->floatval <= arg2->floatval);
2460 else if (!inf && strict)
2461 ret = (arg1->floatval > arg2->floatval);
2462 else if (!inf && !strict)
2463 ret = (arg1->floatval >= arg2->floatval);
2464 xmlXPathFreeObject(arg1);
2465 xmlXPathFreeObject(arg2);
2466 return(ret);
2467}
2468
2469/**
2470 * xmlXPathValueFlipSign:
2471 * @ctxt: the XPath Parser context
2472 *
2473 * Implement the unary - operation on an XPath object
2474 * The numeric operators convert their operands to numbers as if
2475 * by calling the number function.
2476 */
2477void
2478xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
2479 xmlXPathObjectPtr arg;
2480
2481 POP_FLOAT
2482 arg->floatval = -arg->floatval;
2483 valuePush(ctxt, arg);
2484}
2485
2486/**
2487 * xmlXPathAddValues:
2488 * @ctxt: the XPath Parser context
2489 *
2490 * Implement the add operation on XPath objects:
2491 * The numeric operators convert their operands to numbers as if
2492 * by calling the number function.
2493 */
2494void
2495xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
2496 xmlXPathObjectPtr arg;
2497 double val;
2498
2499 POP_FLOAT
2500 val = arg->floatval;
2501 xmlXPathFreeObject(arg);
2502
2503 POP_FLOAT
2504 arg->floatval += val;
2505 valuePush(ctxt, arg);
2506}
2507
2508/**
2509 * xmlXPathSubValues:
2510 * @ctxt: the XPath Parser context
2511 *
2512 * Implement the substraction operation on XPath objects:
2513 * The numeric operators convert their operands to numbers as if
2514 * by calling the number function.
2515 */
2516void
2517xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
2518 xmlXPathObjectPtr arg;
2519 double val;
2520
2521 POP_FLOAT
2522 val = arg->floatval;
2523 xmlXPathFreeObject(arg);
2524
2525 POP_FLOAT
2526 arg->floatval -= val;
2527 valuePush(ctxt, arg);
2528}
2529
2530/**
2531 * xmlXPathMultValues:
2532 * @ctxt: the XPath Parser context
2533 *
2534 * Implement the multiply operation on XPath objects:
2535 * The numeric operators convert their operands to numbers as if
2536 * by calling the number function.
2537 */
2538void
2539xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
2540 xmlXPathObjectPtr arg;
2541 double val;
2542
2543 POP_FLOAT
2544 val = arg->floatval;
2545 xmlXPathFreeObject(arg);
2546
2547 POP_FLOAT
2548 arg->floatval *= val;
2549 valuePush(ctxt, arg);
2550}
2551
2552/**
2553 * xmlXPathDivValues:
2554 * @ctxt: the XPath Parser context
2555 *
2556 * Implement the div operation on XPath objects @arg1 / @arg2:
2557 * The numeric operators convert their operands to numbers as if
2558 * by calling the number function.
2559 */
2560void
2561xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
2562 xmlXPathObjectPtr arg;
2563 double val;
2564
2565 POP_FLOAT
2566 val = arg->floatval;
2567 xmlXPathFreeObject(arg);
2568
2569 POP_FLOAT
2570 arg->floatval /= val;
2571 valuePush(ctxt, arg);
2572}
2573
2574/**
2575 * xmlXPathModValues:
2576 * @ctxt: the XPath Parser context
2577 *
2578 * Implement the mod operation on XPath objects: @arg1 / @arg2
2579 * The numeric operators convert their operands to numbers as if
2580 * by calling the number function.
2581 */
2582void
2583xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
2584 xmlXPathObjectPtr arg;
2585 int arg1, arg2;
2586
2587 POP_FLOAT
2588 arg2 = (int) arg->floatval;
2589 xmlXPathFreeObject(arg);
2590
2591 POP_FLOAT
2592 arg1 = (int) arg->floatval;
2593 arg->floatval = arg1 % arg2;
2594 valuePush(ctxt, arg);
2595}
2596
2597/************************************************************************
2598 * *
2599 * The traversal functions *
2600 * *
2601 ************************************************************************/
2602
2603typedef enum {
2604 AXIS_ANCESTOR = 1,
2605 AXIS_ANCESTOR_OR_SELF,
2606 AXIS_ATTRIBUTE,
2607 AXIS_CHILD,
2608 AXIS_DESCENDANT,
2609 AXIS_DESCENDANT_OR_SELF,
2610 AXIS_FOLLOWING,
2611 AXIS_FOLLOWING_SIBLING,
2612 AXIS_NAMESPACE,
2613 AXIS_PARENT,
2614 AXIS_PRECEDING,
2615 AXIS_PRECEDING_SIBLING,
2616 AXIS_SELF
2617} xmlXPathAxisVal;
2618
2619/*
2620 * A traversal function enumerates nodes along an axis.
2621 * Initially it must be called with NULL, and it indicates
2622 * termination on the axis by returning NULL.
2623 */
2624typedef xmlNodePtr (*xmlXPathTraversalFunction)
2625 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
2626
2627/**
2628 * xmlXPathNextSelf:
2629 * @ctxt: the XPath Parser context
2630 * @cur: the current node in the traversal
2631 *
2632 * Traversal function for the "self" direction
2633 * The self axis contains just the context node itself
2634 *
2635 * Returns the next element following that axis
2636 */
2637xmlNodePtr
2638xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2639 if (cur == NULL)
2640 return(ctxt->context->node);
2641 return(NULL);
2642}
2643
2644/**
2645 * xmlXPathNextChild:
2646 * @ctxt: the XPath Parser context
2647 * @cur: the current node in the traversal
2648 *
2649 * Traversal function for the "child" direction
2650 * The child axis contains the children of the context node in document order.
2651 *
2652 * Returns the next element following that axis
2653 */
2654xmlNodePtr
2655xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2656 if (cur == NULL) {
2657 if (ctxt->context->node == NULL) return(NULL);
2658 switch (ctxt->context->node->type) {
2659 case XML_ELEMENT_NODE:
2660 case XML_TEXT_NODE:
2661 case XML_CDATA_SECTION_NODE:
2662 case XML_ENTITY_REF_NODE:
2663 case XML_ENTITY_NODE:
2664 case XML_PI_NODE:
2665 case XML_COMMENT_NODE:
2666 case XML_NOTATION_NODE:
2667 case XML_DTD_NODE:
2668 return(ctxt->context->node->children);
2669 case XML_DOCUMENT_NODE:
2670 case XML_DOCUMENT_TYPE_NODE:
2671 case XML_DOCUMENT_FRAG_NODE:
2672 case XML_HTML_DOCUMENT_NODE:
2673#ifdef LIBXML_SGML_ENABLED
2674 case XML_SGML_DOCUMENT_NODE:
2675#endif
2676 return(((xmlDocPtr) ctxt->context->node)->children);
2677 case XML_ELEMENT_DECL:
2678 case XML_ATTRIBUTE_DECL:
2679 case XML_ENTITY_DECL:
2680 case XML_ATTRIBUTE_NODE:
2681 case XML_NAMESPACE_DECL:
2682 case XML_XINCLUDE_START:
2683 case XML_XINCLUDE_END:
2684 return(NULL);
2685 }
2686 return(NULL);
2687 }
2688 if ((cur->type == XML_DOCUMENT_NODE) ||
2689 (cur->type == XML_HTML_DOCUMENT_NODE))
2690 return(NULL);
2691 return(cur->next);
2692}
2693
2694/**
2695 * xmlXPathNextDescendant:
2696 * @ctxt: the XPath Parser context
2697 * @cur: the current node in the traversal
2698 *
2699 * Traversal function for the "descendant" direction
2700 * the descendant axis contains the descendants of the context node in document
2701 * order; a descendant is a child or a child of a child and so on.
2702 *
2703 * Returns the next element following that axis
2704 */
2705xmlNodePtr
2706xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2707 if (cur == NULL) {
2708 if (ctxt->context->node == NULL)
2709 return(NULL);
2710 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
2711 (ctxt->context->node->type == XML_NAMESPACE_DECL))
2712 return(NULL);
2713
2714 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
2715 return(ctxt->context->doc->children);
2716 return(ctxt->context->node->children);
2717 }
2718
2719 if (cur->children != NULL)
2720 {
2721 if (cur->children->type != XML_ENTITY_DECL)
2722 return(cur->children);
2723 }
2724 if (cur->next != NULL) return(cur->next);
2725
2726 do {
2727 cur = cur->parent;
2728 if (cur == NULL) return(NULL);
2729 if (cur == ctxt->context->node) return(NULL);
2730 if (cur->next != NULL) {
2731 cur = cur->next;
2732 return(cur);
2733 }
2734 } while (cur != NULL);
2735 return(cur);
2736}
2737
2738/**
2739 * xmlXPathNextDescendantOrSelf:
2740 * @ctxt: the XPath Parser context
2741 * @cur: the current node in the traversal
2742 *
2743 * Traversal function for the "descendant-or-self" direction
2744 * the descendant-or-self axis contains the context node and the descendants
2745 * of the context node in document order; thus the context node is the first
2746 * node on the axis, and the first child of the context node is the second node
2747 * on the axis
2748 *
2749 * Returns the next element following that axis
2750 */
2751xmlNodePtr
2752xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2753 if (cur == NULL) {
2754 if (ctxt->context->node == NULL)
2755 return(NULL);
2756 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
2757 (ctxt->context->node->type == XML_NAMESPACE_DECL))
2758 return(NULL);
2759 return(ctxt->context->node);
2760 }
2761
2762 return(xmlXPathNextDescendant(ctxt, cur));
2763}
2764
2765/**
2766 * xmlXPathNextParent:
2767 * @ctxt: the XPath Parser context
2768 * @cur: the current node in the traversal
2769 *
2770 * Traversal function for the "parent" direction
2771 * The parent axis contains the parent of the context node, if there is one.
2772 *
2773 * Returns the next element following that axis
2774 */
2775xmlNodePtr
2776xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2777 /*
2778 * the parent of an attribute or namespace node is the element
2779 * to which the attribute or namespace node is attached
2780 * Namespace handling !!!
2781 */
2782 if (cur == NULL) {
2783 if (ctxt->context->node == NULL) return(NULL);
2784 switch (ctxt->context->node->type) {
2785 case XML_ELEMENT_NODE:
2786 case XML_TEXT_NODE:
2787 case XML_CDATA_SECTION_NODE:
2788 case XML_ENTITY_REF_NODE:
2789 case XML_ENTITY_NODE:
2790 case XML_PI_NODE:
2791 case XML_COMMENT_NODE:
2792 case XML_NOTATION_NODE:
2793 case XML_DTD_NODE:
2794 case XML_ELEMENT_DECL:
2795 case XML_ATTRIBUTE_DECL:
2796 case XML_XINCLUDE_START:
2797 case XML_XINCLUDE_END:
2798 case XML_ENTITY_DECL:
2799 if (ctxt->context->node->parent == NULL)
2800 return((xmlNodePtr) ctxt->context->doc);
2801 return(ctxt->context->node->parent);
2802 case XML_ATTRIBUTE_NODE: {
2803 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
2804
2805 return(att->parent);
2806 }
2807 case XML_DOCUMENT_NODE:
2808 case XML_DOCUMENT_TYPE_NODE:
2809 case XML_DOCUMENT_FRAG_NODE:
2810 case XML_HTML_DOCUMENT_NODE:
2811#ifdef LIBXML_SGML_ENABLED
2812 case XML_SGML_DOCUMENT_NODE:
2813#endif
2814 return(NULL);
2815 case XML_NAMESPACE_DECL:
2816 /*
2817 * TODO !!! may require extending struct _xmlNs with
2818 * parent field
2819 * C.f. Infoset case...
2820 */
2821 return(NULL);
2822 }
2823 }
2824 return(NULL);
2825}
2826
2827/**
2828 * xmlXPathNextAncestor:
2829 * @ctxt: the XPath Parser context
2830 * @cur: the current node in the traversal
2831 *
2832 * Traversal function for the "ancestor" direction
2833 * the ancestor axis contains the ancestors of the context node; the ancestors
2834 * of the context node consist of the parent of context node and the parent's
2835 * parent and so on; the nodes are ordered in reverse document order; thus the
2836 * parent is the first node on the axis, and the parent's parent is the second
2837 * node on the axis
2838 *
2839 * Returns the next element following that axis
2840 */
2841xmlNodePtr
2842xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2843 /*
2844 * the parent of an attribute or namespace node is the element
2845 * to which the attribute or namespace node is attached
2846 * !!!!!!!!!!!!!
2847 */
2848 if (cur == NULL) {
2849 if (ctxt->context->node == NULL) return(NULL);
2850 switch (ctxt->context->node->type) {
2851 case XML_ELEMENT_NODE:
2852 case XML_TEXT_NODE:
2853 case XML_CDATA_SECTION_NODE:
2854 case XML_ENTITY_REF_NODE:
2855 case XML_ENTITY_NODE:
2856 case XML_PI_NODE:
2857 case XML_COMMENT_NODE:
2858 case XML_DTD_NODE:
2859 case XML_ELEMENT_DECL:
2860 case XML_ATTRIBUTE_DECL:
2861 case XML_ENTITY_DECL:
2862 case XML_NOTATION_NODE:
2863 case XML_XINCLUDE_START:
2864 case XML_XINCLUDE_END:
2865 if (ctxt->context->node->parent == NULL)
2866 return((xmlNodePtr) ctxt->context->doc);
2867 return(ctxt->context->node->parent);
2868 case XML_ATTRIBUTE_NODE: {
2869 xmlAttrPtr cur = (xmlAttrPtr) ctxt->context->node;
2870
2871 return(cur->parent);
2872 }
2873 case XML_DOCUMENT_NODE:
2874 case XML_DOCUMENT_TYPE_NODE:
2875 case XML_DOCUMENT_FRAG_NODE:
2876 case XML_HTML_DOCUMENT_NODE:
2877#ifdef LIBXML_SGML_ENABLED
2878 case XML_SGML_DOCUMENT_NODE:
2879#endif
2880 return(NULL);
2881 case XML_NAMESPACE_DECL:
2882 /*
2883 * TODO !!! may require extending struct _xmlNs with
2884 * parent field
2885 * C.f. Infoset case...
2886 */
2887 return(NULL);
2888 }
2889 return(NULL);
2890 }
2891 if (cur == ctxt->context->doc->children)
2892 return((xmlNodePtr) ctxt->context->doc);
2893 if (cur == (xmlNodePtr) ctxt->context->doc)
2894 return(NULL);
2895 switch (cur->type) {
2896 case XML_ELEMENT_NODE:
2897 case XML_TEXT_NODE:
2898 case XML_CDATA_SECTION_NODE:
2899 case XML_ENTITY_REF_NODE:
2900 case XML_ENTITY_NODE:
2901 case XML_PI_NODE:
2902 case XML_COMMENT_NODE:
2903 case XML_NOTATION_NODE:
2904 case XML_DTD_NODE:
2905 case XML_ELEMENT_DECL:
2906 case XML_ATTRIBUTE_DECL:
2907 case XML_ENTITY_DECL:
2908 case XML_XINCLUDE_START:
2909 case XML_XINCLUDE_END:
2910 return(cur->parent);
2911 case XML_ATTRIBUTE_NODE: {
2912 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
2913
2914 return(att->parent);
2915 }
2916 case XML_DOCUMENT_NODE:
2917 case XML_DOCUMENT_TYPE_NODE:
2918 case XML_DOCUMENT_FRAG_NODE:
2919 case XML_HTML_DOCUMENT_NODE:
2920#ifdef LIBXML_SGML_ENABLED
2921 case XML_SGML_DOCUMENT_NODE:
2922#endif
2923 return(NULL);
2924 case XML_NAMESPACE_DECL:
2925 /*
2926 * TODO !!! may require extending struct _xmlNs with
2927 * parent field
2928 * C.f. Infoset case...
2929 */
2930 return(NULL);
2931 }
2932 return(NULL);
2933}
2934
2935/**
2936 * xmlXPathNextAncestorOrSelf:
2937 * @ctxt: the XPath Parser context
2938 * @cur: the current node in the traversal
2939 *
2940 * Traversal function for the "ancestor-or-self" direction
2941 * he ancestor-or-self axis contains the context node and ancestors of
2942 * the context node in reverse document order; thus the context node is
2943 * the first node on the axis, and the context node's parent the second;
2944 * parent here is defined the same as with the parent axis.
2945 *
2946 * Returns the next element following that axis
2947 */
2948xmlNodePtr
2949xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2950 if (cur == NULL)
2951 return(ctxt->context->node);
2952 return(xmlXPathNextAncestor(ctxt, cur));
2953}
2954
2955/**
2956 * xmlXPathNextFollowingSibling:
2957 * @ctxt: the XPath Parser context
2958 * @cur: the current node in the traversal
2959 *
2960 * Traversal function for the "following-sibling" direction
2961 * The following-sibling axis contains the following siblings of the context
2962 * node in document order.
2963 *
2964 * Returns the next element following that axis
2965 */
2966xmlNodePtr
2967xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2968 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
2969 (ctxt->context->node->type == XML_NAMESPACE_DECL))
2970 return(NULL);
2971 if (cur == (xmlNodePtr) ctxt->context->doc)
2972 return(NULL);
2973 if (cur == NULL)
2974 return(ctxt->context->node->next);
2975 return(cur->next);
2976}
2977
2978/**
2979 * xmlXPathNextPrecedingSibling:
2980 * @ctxt: the XPath Parser context
2981 * @cur: the current node in the traversal
2982 *
2983 * Traversal function for the "preceding-sibling" direction
2984 * The preceding-sibling axis contains the preceding siblings of the context
2985 * node in reverse document order; the first preceding sibling is first on the
2986 * axis; the sibling preceding that node is the second on the axis and so on.
2987 *
2988 * Returns the next element following that axis
2989 */
2990xmlNodePtr
2991xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2992 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
2993 (ctxt->context->node->type == XML_NAMESPACE_DECL))
2994 return(NULL);
2995 if (cur == (xmlNodePtr) ctxt->context->doc)
2996 return(NULL);
2997 if (cur == NULL)
2998 return(ctxt->context->node->prev);
2999 return(cur->prev);
3000}
3001
3002/**
3003 * xmlXPathNextFollowing:
3004 * @ctxt: the XPath Parser context
3005 * @cur: the current node in the traversal
3006 *
3007 * Traversal function for the "following" direction
3008 * The following axis contains all nodes in the same document as the context
3009 * node that are after the context node in document order, excluding any
3010 * descendants and excluding attribute nodes and namespace nodes; the nodes
3011 * are ordered in document order
3012 *
3013 * Returns the next element following that axis
3014 */
3015xmlNodePtr
3016xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3017 if (cur != NULL && cur->children != NULL)
3018 return cur->children ;
3019 if (cur == NULL) cur = ctxt->context->node;
3020 if (cur == NULL) return(NULL) ; /* ERROR */
3021 if (cur->next != NULL) return(cur->next) ;
3022 do {
3023 cur = cur->parent;
3024 if (cur == NULL) return(NULL);
3025 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3026 if (cur->next != NULL) return(cur->next);
3027 } while (cur != NULL);
3028 return(cur);
3029}
3030
3031/*
3032 * xmlXPathIsAncestor:
3033 * @ancestor: the ancestor node
3034 * @node: the current node
3035 *
3036 * Check that @ancestor is a @node's ancestor
3037 *
3038 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3039 */
3040static int
3041xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3042 if ((ancestor == NULL) || (node == NULL)) return(0);
3043 /* nodes need to be in the same document */
3044 if (ancestor->doc != node->doc) return(0);
3045 /* avoid searching if ancestor or node is the root node */
3046 if (ancestor == (xmlNodePtr) node->doc) return(1);
3047 if (node == (xmlNodePtr) ancestor->doc) return(0);
3048 while (node->parent != NULL) {
3049 if (node->parent == ancestor)
3050 return(1);
3051 node = node->parent;
3052 }
3053 return(0);
3054}
3055
3056/**
3057 * xmlXPathNextPreceding:
3058 * @ctxt: the XPath Parser context
3059 * @cur: the current node in the traversal
3060 *
3061 * Traversal function for the "preceding" direction
3062 * the preceding axis contains all nodes in the same document as the context
3063 * node that are before the context node in document order, excluding any
3064 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3065 * ordered in reverse document order
3066 *
3067 * Returns the next element following that axis
3068 */
3069xmlNodePtr
3070xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3071 if (cur == NULL)
3072 cur = ctxt->context->node ;
3073 do {
3074 if (cur->prev != NULL) {
3075 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3076 ;
3077 return(cur) ;
3078 }
3079
3080 cur = cur->parent;
3081 if (cur == NULL) return(NULL);
3082 if (cur == ctxt->context->doc->children) return(NULL);
3083 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3084 return(cur);
3085}
3086
3087/**
3088 * xmlXPathNextNamespace:
3089 * @ctxt: the XPath Parser context
3090 * @cur: the current attribute in the traversal
3091 *
3092 * Traversal function for the "namespace" direction
3093 * the namespace axis contains the namespace nodes of the context node;
3094 * the order of nodes on this axis is implementation-defined; the axis will
3095 * be empty unless the context node is an element
3096 *
3097 * Returns the next element following that axis
3098 */
3099xmlNodePtr
3100xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3101 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3102 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3103 if (ctxt->context->namespaces != NULL)
3104 xmlFree(ctxt->context->namespaces);
3105 ctxt->context->namespaces =
3106 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3107 if (ctxt->context->namespaces == NULL) return(NULL);
3108 ctxt->context->nsNr = 0;
3109 }
3110 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3111}
3112
3113/**
3114 * xmlXPathNextAttribute:
3115 * @ctxt: the XPath Parser context
3116 * @cur: the current attribute in the traversal
3117 *
3118 * Traversal function for the "attribute" direction
3119 * TODO: support DTD inherited default attributes
3120 *
3121 * Returns the next element following that axis
3122 */
3123xmlNodePtr
3124xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3125 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3126 if (cur == NULL) {
3127 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3128 return(NULL);
3129 return((xmlNodePtr)ctxt->context->node->properties);
3130 }
3131 return((xmlNodePtr)cur->next);
3132}
3133
3134/************************************************************************
3135 * *
3136 * NodeTest Functions *
3137 * *
3138 ************************************************************************/
3139
3140typedef enum {
3141 NODE_TEST_NONE = 0,
3142 NODE_TEST_TYPE = 1,
3143 NODE_TEST_PI = 2,
3144 NODE_TEST_ALL = 3,
3145 NODE_TEST_NS = 4,
3146 NODE_TEST_NAME = 5
3147} xmlXPathTestVal;
3148
3149typedef enum {
3150 NODE_TYPE_NODE = 0,
3151 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
3152 NODE_TYPE_TEXT = XML_TEXT_NODE,
3153 NODE_TYPE_PI = XML_PI_NODE
3154} xmlXPathTypeVal;
3155
3156#define IS_FUNCTION 200
3157
3158/**
3159 * xmlXPathNodeCollectAndTest:
3160 * @ctxt: the XPath Parser context
3161 * @axis: the XPath axis
3162 * @test: the XPath test
3163 * @type: the XPath type
3164 * @prefix: the namesapce prefix if any
3165 * @name: the name used in the search if any
3166 *
3167 * This is the function implementing a step: based on the current list
3168 * of nodes, it builds up a new list, looking at all nodes under that
3169 * axis and selecting them.
3170 *
3171 * Returns the new NodeSet resulting from the search.
3172 */
3173void
3174xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis,
3175 xmlXPathTestVal test, xmlXPathTypeVal type,
3176 const xmlChar *prefix, const xmlChar *name) {
3177#ifdef DEBUG_STEP
3178 int n = 0, t = 0;
3179#endif
3180 int i;
3181 xmlNodeSetPtr ret;
3182 xmlXPathTraversalFunction next = NULL;
3183 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
3184 xmlNodePtr cur = NULL;
3185 xmlXPathObjectPtr obj;
3186 xmlNodeSetPtr nodelist;
3187
3188 CHECK_TYPE(XPATH_NODESET);
3189 obj = valuePop(ctxt);
3190 addNode = xmlXPathNodeSetAdd;
3191
3192#ifdef DEBUG_STEP
3193 xmlGenericError(xmlGenericErrorContext,
3194 "new step : ");
3195#endif
3196 switch (axis) {
3197 case AXIS_ANCESTOR:
3198#ifdef DEBUG_STEP
3199 xmlGenericError(xmlGenericErrorContext,
3200 "axis 'ancestors' ");
3201#endif
3202 next = xmlXPathNextAncestor; break;
3203 case AXIS_ANCESTOR_OR_SELF:
3204#ifdef DEBUG_STEP
3205 xmlGenericError(xmlGenericErrorContext,
3206 "axis 'ancestors-or-self' ");
3207#endif
3208 next = xmlXPathNextAncestorOrSelf; break;
3209 case AXIS_ATTRIBUTE:
3210#ifdef DEBUG_STEP
3211 xmlGenericError(xmlGenericErrorContext,
3212 "axis 'attributes' ");
3213#endif
3214 next = xmlXPathNextAttribute; break;
3215 break;
3216 case AXIS_CHILD:
3217#ifdef DEBUG_STEP
3218 xmlGenericError(xmlGenericErrorContext,
3219 "axis 'child' ");
3220#endif
3221 next = xmlXPathNextChild; break;
3222 case AXIS_DESCENDANT:
3223#ifdef DEBUG_STEP
3224 xmlGenericError(xmlGenericErrorContext,
3225 "axis 'descendant' ");
3226#endif
3227 next = xmlXPathNextDescendant; break;
3228 case AXIS_DESCENDANT_OR_SELF:
3229#ifdef DEBUG_STEP
3230 xmlGenericError(xmlGenericErrorContext,
3231 "axis 'descendant-or-self' ");
3232#endif
3233 next = xmlXPathNextDescendantOrSelf; break;
3234 case AXIS_FOLLOWING:
3235#ifdef DEBUG_STEP
3236 xmlGenericError(xmlGenericErrorContext,
3237 "axis 'following' ");
3238#endif
3239 next = xmlXPathNextFollowing; break;
3240 case AXIS_FOLLOWING_SIBLING:
3241#ifdef DEBUG_STEP
3242 xmlGenericError(xmlGenericErrorContext,
3243 "axis 'following-siblings' ");
3244#endif
3245 next = xmlXPathNextFollowingSibling; break;
3246 case AXIS_NAMESPACE:
3247#ifdef DEBUG_STEP
3248 xmlGenericError(xmlGenericErrorContext,
3249 "axis 'namespace' ");
3250#endif
3251 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
3252 break;
3253 case AXIS_PARENT:
3254#ifdef DEBUG_STEP
3255 xmlGenericError(xmlGenericErrorContext,
3256 "axis 'parent' ");
3257#endif
3258 next = xmlXPathNextParent; break;
3259 case AXIS_PRECEDING:
3260#ifdef DEBUG_STEP
3261 xmlGenericError(xmlGenericErrorContext,
3262 "axis 'preceding' ");
3263#endif
3264 next = xmlXPathNextPreceding; break;
3265 case AXIS_PRECEDING_SIBLING:
3266#ifdef DEBUG_STEP
3267 xmlGenericError(xmlGenericErrorContext,
3268 "axis 'preceding-sibling' ");
3269#endif
3270 next = xmlXPathNextPrecedingSibling; break;
3271 case AXIS_SELF:
3272#ifdef DEBUG_STEP
3273 xmlGenericError(xmlGenericErrorContext,
3274 "axis 'self' ");
3275#endif
3276 next = xmlXPathNextSelf; break;
3277 }
3278 if (next == NULL)
3279 return;
3280
3281 nodelist = obj->nodesetval;
3282 if ((nodelist != NULL) &&
3283 (nodelist->nodeNr <= 1))
3284 addNode = xmlXPathNodeSetAddUnique;
3285 else
3286 addNode = xmlXPathNodeSetAdd;
3287 ret = xmlXPathNodeSetCreate(NULL);
3288#ifdef DEBUG_STEP
3289 xmlGenericError(xmlGenericErrorContext,
3290 " context contains %d nodes\n",
3291 nodelist->nodeNr);
3292 switch (test) {
3293 case NODE_TEST_NODE:
3294 xmlGenericError(xmlGenericErrorContext,
3295 " searching all nodes\n");
3296 break;
3297 case NODE_TEST_NONE:
3298 xmlGenericError(xmlGenericErrorContext,
3299 " searching for none !!!\n");
3300 break;
3301 case NODE_TEST_TYPE:
3302 xmlGenericError(xmlGenericErrorContext,
3303 " searching for type %d\n", type);
3304 break;
3305 case NODE_TEST_PI:
3306 xmlGenericError(xmlGenericErrorContext,
3307 " searching for PI !!!\n");
3308 break;
3309 case NODE_TEST_ALL:
3310 xmlGenericError(xmlGenericErrorContext,
3311 " searching for *\n");
3312 break;
3313 case NODE_TEST_NS:
3314 xmlGenericError(xmlGenericErrorContext,
3315 " searching for namespace %s\n",
3316 prefix);
3317 break;
3318 case NODE_TEST_NAME:
3319 xmlGenericError(xmlGenericErrorContext,
3320 " searching for name %s\n", name);
3321 if (prefix != NULL)
3322 xmlGenericError(xmlGenericErrorContext,
3323 " with namespace %s\n",
3324 prefix);
3325 break;
3326 }
3327 xmlGenericError(xmlGenericErrorContext, "Testing : ");
3328#endif
3329 /*
3330 * 2.3 Node Tests
3331 * - For the attribute axis, the principal node type is attribute.
3332 * - For the namespace axis, the principal node type is namespace.
3333 * - For other axes, the principal node type is element.
3334 *
3335 * A node test * is true for any node of the
3336 * principal node type. For example, child::* willi
3337 * select all element children of the context node
3338 */
3339 for (i = 0;i < nodelist->nodeNr; i++) {
3340 ctxt->context->node = nodelist->nodeTab[i];
3341
3342 cur = NULL;
3343 do {
3344 cur = next(ctxt, cur);
3345 if (cur == NULL) break;
3346#ifdef DEBUG_STEP
3347 t++;
3348 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
3349#endif
3350 switch (test) {
3351 case NODE_TEST_NONE:
3352 STRANGE
3353 return;
3354 case NODE_TEST_TYPE:
3355 if ((cur->type == type) ||
3356 ((type == NODE_TYPE_NODE) &&
3357 ((cur->type == XML_DOCUMENT_NODE) ||
3358 (cur->type == XML_HTML_DOCUMENT_NODE) ||
3359 (cur->type == XML_ELEMENT_NODE) ||
3360 (cur->type == XML_PI_NODE) ||
3361 (cur->type == XML_COMMENT_NODE) ||
3362 (cur->type == XML_CDATA_SECTION_NODE) ||
3363 (cur->type == XML_TEXT_NODE)))) {
3364#ifdef DEBUG_STEP
3365 n++;
3366#endif
3367 addNode(ret, cur);
3368 }
3369 break;
3370 case NODE_TEST_PI:
3371 if (cur->type == XML_PI_NODE) {
3372 if ((name != NULL) &&
3373 (!xmlStrEqual(name, cur->name)))
3374 break;
3375#ifdef DEBUG_STEP
3376 n++;
3377#endif
3378 addNode(ret, cur);
3379 }
3380 break;
3381 case NODE_TEST_ALL:
3382 if (axis == AXIS_ATTRIBUTE) {
3383 if (cur->type == XML_ATTRIBUTE_NODE) {
3384#ifdef DEBUG_STEP
3385 n++;
3386#endif
3387 addNode(ret, cur);
3388 }
3389 } else if (axis == AXIS_NAMESPACE) {
3390 if (cur->type == XML_NAMESPACE_DECL) {
3391#ifdef DEBUG_STEP
3392 n++;
3393#endif
3394 addNode(ret, cur);
3395 }
3396 } else {
3397 if ((cur->type == XML_ELEMENT_NODE) ||
3398 (cur->type == XML_DOCUMENT_NODE) ||
3399 (cur->type == XML_HTML_DOCUMENT_NODE)) {
3400 if (prefix == NULL) {
3401#ifdef DEBUG_STEP
3402 n++;
3403#endif
3404 addNode(ret, cur);
3405 } else if ((cur->ns != NULL) &&
3406 (xmlStrEqual(prefix,
3407 cur->ns->href))) {
3408#ifdef DEBUG_STEP
3409 n++;
3410#endif
3411 addNode(ret, cur);
3412 }
3413 }
3414 }
3415 break;
3416 case NODE_TEST_NS: {
3417 TODO;
3418 break;
3419 }
3420 case NODE_TEST_NAME:
3421 switch (cur->type) {
3422 case XML_ELEMENT_NODE:
3423 if (xmlStrEqual(name, cur->name)) {
3424 if (prefix == NULL) {
3425 if ((cur->ns == NULL) ||
3426 (cur->ns->prefix == NULL)) {
3427#ifdef DEBUG_STEP
3428 n++;
3429#endif
3430 addNode(ret, cur);
3431 }
3432 } else {
3433 if ((cur->ns != NULL) &&
3434 (xmlStrEqual(prefix,
3435 cur->ns->href))) {
3436#ifdef DEBUG_STEP
3437 n++;
3438#endif
3439 addNode(ret, cur);
3440 }
3441 }
3442 }
3443 break;
3444 case XML_ATTRIBUTE_NODE: {
3445 xmlAttrPtr attr = (xmlAttrPtr) cur;
3446 if (xmlStrEqual(name, attr->name)) {
3447 if (prefix == NULL) {
3448 if ((attr->ns == NULL) ||
3449 (attr->ns->prefix == NULL)) {
3450#ifdef DEBUG_STEP
3451 n++;
3452#endif
3453 addNode(ret, (xmlNodePtr) attr);
3454 }
3455 } else {
3456 if ((attr->ns != NULL) &&
3457 (xmlStrEqual(prefix,
3458 attr->ns->href))) {
3459#ifdef DEBUG_STEP
3460 n++;
3461#endif
3462 addNode(ret, (xmlNodePtr) attr);
3463 }
3464 }
3465 }
3466 break;
3467 }
3468 case XML_NAMESPACE_DECL: {
3469 TODO;
3470 break;
3471 }
3472 default:
3473 break;
3474 }
3475 break;
3476 }
3477 } while (cur != NULL);
3478 }
3479#ifdef DEBUG_STEP
3480 xmlGenericError(xmlGenericErrorContext,
3481 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
3482#endif
3483 xmlXPathFreeObject(obj);
3484 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
3485}
3486
3487
3488/************************************************************************
3489 * *
3490 * Implicit tree core function library *
3491 * *
3492 ************************************************************************/
3493
3494/**
3495 * xmlXPathRoot:
3496 * @ctxt: the XPath Parser context
3497 *
3498 * Initialize the context to the root of the document
3499 */
3500void
3501xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3502 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3503 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3504}
3505
3506/************************************************************************
3507 * *
3508 * The explicit core function library *
3509 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3510 * *
3511 ************************************************************************/
3512
3513
3514/**
3515 * xmlXPathLastFunction:
3516 * @ctxt: the XPath Parser context
3517 * @nargs: the number of arguments
3518 *
3519 * Implement the last() XPath function
3520 * number last()
3521 * The last function returns the number of nodes in the context node list.
3522 */
3523void
3524xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3525 CHECK_ARITY(0);
3526 if (ctxt->context->contextSize >= 0) {
3527 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3528#ifdef DEBUG_EXPR
3529 xmlGenericError(xmlGenericErrorContext,
3530 "last() : %d\n", ctxt->context->contextSize);
3531#endif
3532 } else {
3533 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3534 }
3535}
3536
3537/**
3538 * xmlXPathPositionFunction:
3539 * @ctxt: the XPath Parser context
3540 * @nargs: the number of arguments
3541 *
3542 * Implement the position() XPath function
3543 * number position()
3544 * The position function returns the position of the context node in the
3545 * context node list. The first position is 1, and so the last positionr
3546 * will be equal to last().
3547 */
3548void
3549xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3550 CHECK_ARITY(0);
3551 if (ctxt->context->proximityPosition >= 0) {
3552 valuePush(ctxt,
3553 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3554#ifdef DEBUG_EXPR
3555 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3556 ctxt->context->proximityPosition);
3557#endif
3558 } else {
3559 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3560 }
3561}
3562
3563/**
3564 * xmlXPathCountFunction:
3565 * @ctxt: the XPath Parser context
3566 * @nargs: the number of arguments
3567 *
3568 * Implement the count() XPath function
3569 * number count(node-set)
3570 */
3571void
3572xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3573 xmlXPathObjectPtr cur;
3574
3575 CHECK_ARITY(1);
3576 if ((ctxt->value == NULL) ||
3577 ((ctxt->value->type != XPATH_NODESET) &&
3578 (ctxt->value->type != XPATH_XSLT_TREE)))
3579 XP_ERROR(XPATH_INVALID_TYPE);
3580 cur = valuePop(ctxt);
3581
3582 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
3583 xmlXPathFreeObject(cur);
3584}
3585
3586/**
3587 * xmlXPathIdFunction:
3588 * @ctxt: the XPath Parser context
3589 * @nargs: the number of arguments
3590 *
3591 * Implement the id() XPath function
3592 * node-set id(object)
3593 * The id function selects elements by their unique ID
3594 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3595 * then the result is the union of the result of applying id to the
3596 * string value of each of the nodes in the argument node-set. When the
3597 * argument to id is of any other type, the argument is converted to a
3598 * string as if by a call to the string function; the string is split
3599 * into a whitespace-separated list of tokens (whitespace is any sequence
3600 * of characters matching the production S); the result is a node-set
3601 * containing the elements in the same document as the context node that
3602 * have a unique ID equal to any of the tokens in the list.
3603 */
3604void
3605xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3606 const xmlChar *tokens;
3607 const xmlChar *cur;
3608 xmlChar *ID;
3609 xmlAttrPtr attr;
3610 xmlNodePtr elem = NULL;
3611 xmlXPathObjectPtr ret, obj;
3612
3613 CHECK_ARITY(1);
3614 obj = valuePop(ctxt);
3615 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3616 if (obj->type == XPATH_NODESET) {
3617 xmlXPathObjectPtr newobj;
3618 int i;
3619
3620 ret = xmlXPathNewNodeSet(NULL);
3621
3622 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3623 valuePush(ctxt,
3624 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3625 xmlXPathStringFunction(ctxt, 1);
3626 xmlXPathIdFunction(ctxt, 1);
3627 newobj = valuePop(ctxt);
3628 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3629 newobj->nodesetval);
3630 xmlXPathFreeObject(newobj);
3631 }
3632
3633 xmlXPathFreeObject(obj);
3634 valuePush(ctxt, ret);
3635 return;
3636 }
3637 if (obj->type != XPATH_STRING) {
3638 valuePush(ctxt, obj);
3639 xmlXPathStringFunction(ctxt, 1);
3640 obj = valuePop(ctxt);
3641 if (obj->type != XPATH_STRING) {
3642 xmlXPathFreeObject(obj);
3643 return;
3644 }
3645 }
3646 tokens = obj->stringval;
3647
3648 ret = xmlXPathNewNodeSet(NULL);
3649 valuePush(ctxt, ret);
3650 if (tokens == NULL) {
3651 xmlXPathFreeObject(obj);
3652 return;
3653 }
3654
3655 cur = tokens;
3656
3657 while (IS_BLANK(*cur)) cur++;
3658 while (*cur != 0) {
3659 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3660 (*cur == '.') || (*cur == '-') ||
3661 (*cur == '_') || (*cur == ':') ||
3662 (IS_COMBINING(*cur)) ||
3663 (IS_EXTENDER(*cur)))
3664 cur++;
3665
3666 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3667
3668 ID = xmlStrndup(tokens, cur - tokens);
3669 attr = xmlGetID(ctxt->context->doc, ID);
3670 if (attr != NULL) {
3671 elem = attr->parent;
3672 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3673 }
3674 if (ID != NULL)
3675 xmlFree(ID);
3676
3677 while (IS_BLANK(*cur)) cur++;
3678 tokens = cur;
3679 }
3680 xmlXPathFreeObject(obj);
3681 return;
3682}
3683
3684/**
3685 * xmlXPathLocalNameFunction:
3686 * @ctxt: the XPath Parser context
3687 * @nargs: the number of arguments
3688 *
3689 * Implement the local-name() XPath function
3690 * string local-name(node-set?)
3691 * The local-name function returns a string containing the local part
3692 * of the name of the node in the argument node-set that is first in
3693 * document order. If the node-set is empty or the first node has no
3694 * name, an empty string is returned. If the argument is omitted it
3695 * defaults to the context node.
3696 */
3697void
3698xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3699 xmlXPathObjectPtr cur;
3700
3701 if (nargs == 0) {
3702 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3703 nargs = 1;
3704 }
3705
3706 CHECK_ARITY(1);
3707 if ((ctxt->value == NULL) ||
3708 ((ctxt->value->type != XPATH_NODESET) &&
3709 (ctxt->value->type != XPATH_XSLT_TREE)))
3710 XP_ERROR(XPATH_INVALID_TYPE);
3711 cur = valuePop(ctxt);
3712
3713 if (cur->nodesetval->nodeNr == 0) {
3714 valuePush(ctxt, xmlXPathNewCString(""));
3715 } else {
3716 int i = 0; /* Should be first in document order !!!!! */
3717 switch (cur->nodesetval->nodeTab[i]->type) {
3718 case XML_ELEMENT_NODE:
3719 case XML_ATTRIBUTE_NODE:
3720 case XML_PI_NODE:
3721 valuePush(ctxt,
3722 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
3723 break;
3724 case XML_NAMESPACE_DECL:
3725 valuePush(ctxt, xmlXPathNewString(
3726 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
3727 break;
3728 default:
3729 valuePush(ctxt, xmlXPathNewCString(""));
3730 }
3731 }
3732 xmlXPathFreeObject(cur);
3733}
3734
3735/**
3736 * xmlXPathNamespaceURIFunction:
3737 * @ctxt: the XPath Parser context
3738 * @nargs: the number of arguments
3739 *
3740 * Implement the namespace-uri() XPath function
3741 * string namespace-uri(node-set?)
3742 * The namespace-uri function returns a string containing the
3743 * namespace URI of the expanded name of the node in the argument
3744 * node-set that is first in document order. If the node-set is empty,
3745 * the first node has no name, or the expanded name has no namespace
3746 * URI, an empty string is returned. If the argument is omitted it
3747 * defaults to the context node.
3748 */
3749void
3750xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3751 xmlXPathObjectPtr cur;
3752
3753 if (nargs == 0) {
3754 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3755 nargs = 1;
3756 }
3757 CHECK_ARITY(1);
3758 if ((ctxt->value == NULL) ||
3759 ((ctxt->value->type != XPATH_NODESET) &&
3760 (ctxt->value->type != XPATH_XSLT_TREE)))
3761 XP_ERROR(XPATH_INVALID_TYPE);
3762 cur = valuePop(ctxt);
3763
3764 if (cur->nodesetval->nodeNr == 0) {
3765 valuePush(ctxt, xmlXPathNewCString(""));
3766 } else {
3767 int i = 0; /* Should be first in document order !!!!! */
3768 switch (cur->nodesetval->nodeTab[i]->type) {
3769 case XML_ELEMENT_NODE:
3770 case XML_ATTRIBUTE_NODE:
3771 if (cur->nodesetval->nodeTab[i]->ns == NULL)
3772 valuePush(ctxt, xmlXPathNewCString(""));
3773 else
3774 valuePush(ctxt, xmlXPathNewString(
3775 cur->nodesetval->nodeTab[i]->ns->href));
3776 break;
3777 default:
3778 valuePush(ctxt, xmlXPathNewCString(""));
3779 }
3780 }
3781 xmlXPathFreeObject(cur);
3782}
3783
3784/**
3785 * xmlXPathNameFunction:
3786 * @ctxt: the XPath Parser context
3787 * @nargs: the number of arguments
3788 *
3789 * Implement the name() XPath function
3790 * string name(node-set?)
3791 * The name function returns a string containing a QName representing
3792 * the name of the node in the argument node-set that is first in documenti
3793 * order. The QName must represent the name with respect to the namespace
3794 * declarations in effect on the node whose name is being represented.
3795 * Typically, this will be the form in which the name occurred in the XML
3796 * source. This need not be the case if there are namespace declarations
3797 * in effect on the node that associate multiple prefixes with the same
3798 * namespace. However, an implementation may include information about
3799 * the original prefix in its representation of nodes; in this case, an
3800 * implementation can ensure that the returned string is always the same
3801 * as the QName used in the XML source. If the argument it omitted it
3802 * defaults to the context node.
3803 * Libxml keep the original prefix so the "real qualified name" used is
3804 * returned.
3805 */
3806void
3807xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3808 xmlXPathObjectPtr cur;
3809
3810 if (nargs == 0) {
3811 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3812 nargs = 1;
3813 }
3814
3815 CHECK_ARITY(1);
3816 if ((ctxt->value == NULL) ||
3817 ((ctxt->value->type != XPATH_NODESET) &&
3818 (ctxt->value->type != XPATH_XSLT_TREE)))
3819 XP_ERROR(XPATH_INVALID_TYPE);
3820 cur = valuePop(ctxt);
3821
3822 if (cur->nodesetval->nodeNr == 0) {
3823 valuePush(ctxt, xmlXPathNewCString(""));
3824 } else {
3825 int i = 0; /* Should be first in document order !!!!! */
3826
3827 switch (cur->nodesetval->nodeTab[i]->type) {
3828 case XML_ELEMENT_NODE:
3829 case XML_ATTRIBUTE_NODE:
3830 if (cur->nodesetval->nodeTab[i]->ns == NULL)
3831 valuePush(ctxt, xmlXPathNewString(
3832 cur->nodesetval->nodeTab[i]->name));
3833
3834 else {
3835 char name[2000];
3836#ifdef HAVE_SNPRINTF
3837 snprintf(name, sizeof(name), "%s:%s",
3838 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
3839 (char *) cur->nodesetval->nodeTab[i]->name);
3840#else
3841 sprintf(name, "%s:%s",
3842 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
3843 (char *) cur->nodesetval->nodeTab[i]->name);
3844#endif
3845 name[sizeof(name) - 1] = 0;
3846 valuePush(ctxt, xmlXPathNewCString(name));
3847 }
3848 break;
3849 default:
3850 valuePush(ctxt,
3851 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
3852 xmlXPathLocalNameFunction(ctxt, 1);
3853 }
3854 }
3855 xmlXPathFreeObject(cur);
3856}
3857
3858/**
3859 * xmlXPathStringFunction:
3860 * @ctxt: the XPath Parser context
3861 * @nargs: the number of arguments
3862 *
3863 * Implement the string() XPath function
3864 * string string(object?)
3865 * he string function converts an object to a string as follows:
3866 * - A node-set is converted to a string by returning the value of
3867 * the node in the node-set that is first in document order.
3868 * If the node-set is empty, an empty string is returned.
3869 * - A number is converted to a string as follows
3870 * + NaN is converted to the string NaN
3871 * + positive zero is converted to the string 0
3872 * + negative zero is converted to the string 0
3873 * + positive infinity is converted to the string Infinity
3874 * + negative infinity is converted to the string -Infinity
3875 * + if the number is an integer, the number is represented in
3876 * decimal form as a Number with no decimal point and no leading
3877 * zeros, preceded by a minus sign (-) if the number is negative
3878 * + otherwise, the number is represented in decimal form as a
3879 * Number including a decimal point with at least one digit
3880 * before the decimal point and at least one digit after the
3881 * decimal point, preceded by a minus sign (-) if the number
3882 * is negative; there must be no leading zeros before the decimal
3883 * point apart possibly from the one required digit immediatelyi
3884 * before the decimal point; beyond the one required digit
3885 * after the decimal point there must be as many, but only as
3886 * many, more digits as are needed to uniquely distinguish the
3887 * number from all other IEEE 754 numeric values.
3888 * - The boolean false value is converted to the string false.
3889 * The boolean true value is converted to the string true.
3890 *
3891 * If the argument is omitted, it defaults to a node-set with the
3892 * context node as its only member.
3893 */
3894void
3895xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3896 xmlXPathObjectPtr cur;
3897
3898 if (nargs == 0) {
3899 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3900 nargs = 1;
3901 }
3902
3903 CHECK_ARITY(1);
3904 cur = valuePop(ctxt);
3905 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3906 switch (cur->type) {
3907 case XPATH_UNDEFINED:
3908#ifdef DEBUG_EXPR
3909 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
3910#endif
3911 valuePush(ctxt, xmlXPathNewCString(""));
3912 break;
3913 case XPATH_XSLT_TREE:
3914 case XPATH_NODESET:
3915 if (cur->nodesetval == NULL)
3916 valuePush(ctxt, xmlXPathNewCString(""));
3917 else if (cur->nodesetval->nodeNr == 0) {
3918 valuePush(ctxt, xmlXPathNewCString(""));
3919 } else {
3920 xmlChar *res;
3921 int i = 0; /* Should be first in document order !!!!! */
3922 res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
3923 valuePush(ctxt, xmlXPathNewString(res));
3924 if (res != NULL)
3925 xmlFree(res);
3926 }
3927 xmlXPathFreeObject(cur);
3928 return;
3929 case XPATH_STRING:
3930 valuePush(ctxt, cur);
3931 return;
3932 case XPATH_BOOLEAN:
3933 if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true"));
3934 else valuePush(ctxt, xmlXPathNewCString("false"));
3935 xmlXPathFreeObject(cur);
3936 return;
3937 case XPATH_NUMBER: {
3938 char buf[100];
3939
3940 if (isnan(cur->floatval))
3941 sprintf(buf, "NaN");
3942 else if (isinf(cur->floatval) > 0)
3943 sprintf(buf, "+Infinity");
3944 else if (isinf(cur->floatval) < 0)
3945 sprintf(buf, "-Infinity");
3946 else
3947 sprintf(buf, "%0g", cur->floatval);
3948 valuePush(ctxt, xmlXPathNewCString(buf));
3949 xmlXPathFreeObject(cur);
3950 return;
3951 }
3952 case XPATH_USERS:
3953 case XPATH_POINT:
3954 case XPATH_RANGE:
3955 case XPATH_LOCATIONSET:
3956 TODO
3957 valuePush(ctxt, xmlXPathNewCString(""));
3958 break;
3959 }
3960 STRANGE
3961}
3962
3963/**
3964 * xmlXPathStringLengthFunction:
3965 * @ctxt: the XPath Parser context
3966 * @nargs: the number of arguments
3967 *
3968 * Implement the string-length() XPath function
3969 * number string-length(string?)
3970 * The string-length returns the number of characters in the string
3971 * (see [3.6 Strings]). If the argument is omitted, it defaults to
3972 * the context node converted to a string, in other words the value
3973 * of the context node.
3974 */
3975void
3976xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3977 xmlXPathObjectPtr cur;
3978
3979 if (nargs == 0) {
3980 if (ctxt->context->node == NULL) {
3981 valuePush(ctxt, xmlXPathNewFloat(0));
3982 } else {
3983 xmlChar *content;
3984
3985 content = xmlNodeGetContent(ctxt->context->node);
3986 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
3987 xmlFree(content);
3988 }
3989 return;
3990 }
3991 CHECK_ARITY(1);
3992 CAST_TO_STRING;
3993 CHECK_TYPE(XPATH_STRING);
3994 cur = valuePop(ctxt);
3995 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
3996 xmlXPathFreeObject(cur);
3997}
3998
3999/**
4000 * xmlXPathConcatFunction:
4001 * @ctxt: the XPath Parser context
4002 * @nargs: the number of arguments
4003 *
4004 * Implement the concat() XPath function
4005 * string concat(string, string, string*)
4006 * The concat function returns the concatenation of its arguments.
4007 */
4008void
4009xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4010 xmlXPathObjectPtr cur, newobj;
4011 xmlChar *tmp;
4012
4013 if (nargs < 2) {
4014 CHECK_ARITY(2);
4015 }
4016
4017 CAST_TO_STRING;
4018 cur = valuePop(ctxt);
4019 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4020 xmlXPathFreeObject(cur);
4021 return;
4022 }
4023 nargs--;
4024
4025 while (nargs > 0) {
4026 CAST_TO_STRING;
4027 newobj = valuePop(ctxt);
4028 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4029 xmlXPathFreeObject(newobj);
4030 xmlXPathFreeObject(cur);
4031 XP_ERROR(XPATH_INVALID_TYPE);
4032 }
4033 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4034 newobj->stringval = cur->stringval;
4035 cur->stringval = tmp;
4036
4037 xmlXPathFreeObject(newobj);
4038 nargs--;
4039 }
4040 valuePush(ctxt, cur);
4041}
4042
4043/**
4044 * xmlXPathContainsFunction:
4045 * @ctxt: the XPath Parser context
4046 * @nargs: the number of arguments
4047 *
4048 * Implement the contains() XPath function
4049 * boolean contains(string, string)
4050 * The contains function returns true if the first argument string
4051 * contains the second argument string, and otherwise returns false.
4052 */
4053void
4054xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4055 xmlXPathObjectPtr hay, needle;
4056
4057 CHECK_ARITY(2);
4058 CAST_TO_STRING;
4059 CHECK_TYPE(XPATH_STRING);
4060 needle = valuePop(ctxt);
4061 CAST_TO_STRING;
4062 hay = valuePop(ctxt);
4063 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4064 xmlXPathFreeObject(hay);
4065 xmlXPathFreeObject(needle);
4066 XP_ERROR(XPATH_INVALID_TYPE);
4067 }
4068 if (xmlStrstr(hay->stringval, needle->stringval))
4069 valuePush(ctxt, xmlXPathNewBoolean(1));
4070 else
4071 valuePush(ctxt, xmlXPathNewBoolean(0));
4072 xmlXPathFreeObject(hay);
4073 xmlXPathFreeObject(needle);
4074}
4075
4076/**
4077 * xmlXPathStartsWithFunction:
4078 * @ctxt: the XPath Parser context
4079 * @nargs: the number of arguments
4080 *
4081 * Implement the starts-with() XPath function
4082 * boolean starts-with(string, string)
4083 * The starts-with function returns true if the first argument string
4084 * starts with the second argument string, and otherwise returns false.
4085 */
4086void
4087xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4088 xmlXPathObjectPtr hay, needle;
4089 int n;
4090
4091 CHECK_ARITY(2);
4092 CAST_TO_STRING;
4093 CHECK_TYPE(XPATH_STRING);
4094 needle = valuePop(ctxt);
4095 CAST_TO_STRING;
4096 hay = valuePop(ctxt);
4097 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4098 xmlXPathFreeObject(hay);
4099 xmlXPathFreeObject(needle);
4100 XP_ERROR(XPATH_INVALID_TYPE);
4101 }
4102 n = xmlStrlen(needle->stringval);
4103 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4104 valuePush(ctxt, xmlXPathNewBoolean(0));
4105 else
4106 valuePush(ctxt, xmlXPathNewBoolean(1));
4107 xmlXPathFreeObject(hay);
4108 xmlXPathFreeObject(needle);
4109}
4110
4111/**
4112 * xmlXPathSubstringFunction:
4113 * @ctxt: the XPath Parser context
4114 * @nargs: the number of arguments
4115 *
4116 * Implement the substring() XPath function
4117 * string substring(string, number, number?)
4118 * The substring function returns the substring of the first argument
4119 * starting at the position specified in the second argument with
4120 * length specified in the third argument. For example,
4121 * substring("12345",2,3) returns "234". If the third argument is not
4122 * specified, it returns the substring starting at the position specified
4123 * in the second argument and continuing to the end of the string. For
4124 * example, substring("12345",2) returns "2345". More precisely, each
4125 * character in the string (see [3.6 Strings]) is considered to have a
4126 * numeric position: the position of the first character is 1, the position
4127 * of the second character is 2 and so on. The returned substring contains
4128 * those characters for which the position of the character is greater than
4129 * or equal to the second argument and, if the third argument is specified,
4130 * less than the sum of the second and third arguments; the comparisons
4131 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4132 * - substring("12345", 1.5, 2.6) returns "234"
4133 * - substring("12345", 0, 3) returns "12"
4134 * - substring("12345", 0 div 0, 3) returns ""
4135 * - substring("12345", 1, 0 div 0) returns ""
4136 * - substring("12345", -42, 1 div 0) returns "12345"
4137 * - substring("12345", -1 div 0, 1 div 0) returns ""
4138 */
4139void
4140xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4141 xmlXPathObjectPtr str, start, len;
4142 double le, in;
4143 int i, l;
4144 xmlChar *ret;
4145
4146 /*
4147 * Conformance needs to be checked !!!!!
4148 */
4149 if (nargs < 2) {
4150 CHECK_ARITY(2);
4151 }
4152 if (nargs > 3) {
4153 CHECK_ARITY(3);
4154 }
4155 if (nargs == 3) {
4156 CAST_TO_NUMBER;
4157 CHECK_TYPE(XPATH_NUMBER);
4158 len = valuePop(ctxt);
4159 le = len->floatval;
4160 xmlXPathFreeObject(len);
4161 } else {
4162 le = 2000000000;
4163 }
4164 CAST_TO_NUMBER;
4165 CHECK_TYPE(XPATH_NUMBER);
4166 start = valuePop(ctxt);
4167 in = start->floatval;
4168 xmlXPathFreeObject(start);
4169 CAST_TO_STRING;
4170 CHECK_TYPE(XPATH_STRING);
4171 str = valuePop(ctxt);
4172 le += in;
4173
4174 /* integer index of the first char */
4175 i = (int) in;
4176 if (((double)i) != in) i++;
4177
4178 /* integer index of the last char */
4179 l = (int) le;
4180 if (((double)l) != le) l++;
4181
4182 /* back to a zero based len */
4183 i--;
4184 l--;
4185
4186 /* check against the string len */
4187 if (l > 1024) {
4188 l = xmlStrlen(str->stringval);
4189 }
4190 if (i < 0) {
4191 i = 0;
4192 }
4193
4194 /* number of chars to copy */
4195 l -= i;
4196
4197 ret = xmlStrsub(str->stringval, i, l);
4198 if (ret == NULL)
4199 valuePush(ctxt, xmlXPathNewCString(""));
4200 else {
4201 valuePush(ctxt, xmlXPathNewString(ret));
4202 xmlFree(ret);
4203 }
4204 xmlXPathFreeObject(str);
4205}
4206
4207/**
4208 * xmlXPathSubstringBeforeFunction:
4209 * @ctxt: the XPath Parser context
4210 * @nargs: the number of arguments
4211 *
4212 * Implement the substring-before() XPath function
4213 * string substring-before(string, string)
4214 * The substring-before function returns the substring of the first
4215 * argument string that precedes the first occurrence of the second
4216 * argument string in the first argument string, or the empty string
4217 * if the first argument string does not contain the second argument
4218 * string. For example, substring-before("1999/04/01","/") returns 1999.
4219 */
4220void
4221xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4222 xmlXPathObjectPtr str;
4223 xmlXPathObjectPtr find;
4224 xmlBufferPtr target;
4225 const xmlChar *point;
4226 int offset;
4227
4228 CHECK_ARITY(2);
4229 CAST_TO_STRING;
4230 find = valuePop(ctxt);
4231 CAST_TO_STRING;
4232 str = valuePop(ctxt);
4233
4234 target = xmlBufferCreate();
4235 if (target) {
4236 point = xmlStrstr(str->stringval, find->stringval);
4237 if (point) {
4238 offset = (int)(point - str->stringval);
4239 xmlBufferAdd(target, str->stringval, offset);
4240 }
4241 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4242 xmlBufferFree(target);
4243 }
4244
4245 xmlXPathFreeObject(str);
4246 xmlXPathFreeObject(find);
4247}
4248
4249/**
4250 * xmlXPathSubstringAfterFunction:
4251 * @ctxt: the XPath Parser context
4252 * @nargs: the number of arguments
4253 *
4254 * Implement the substring-after() XPath function
4255 * string substring-after(string, string)
4256 * The substring-after function returns the substring of the first
4257 * argument string that follows the first occurrence of the second
4258 * argument string in the first argument string, or the empty stringi
4259 * if the first argument string does not contain the second argument
4260 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4261 * and substring-after("1999/04/01","19") returns 99/04/01.
4262 */
4263void
4264xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4265 xmlXPathObjectPtr str;
4266 xmlXPathObjectPtr find;
4267 xmlBufferPtr target;
4268 const xmlChar *point;
4269 int offset;
4270
4271 CHECK_ARITY(2);
4272 CAST_TO_STRING;
4273 find = valuePop(ctxt);
4274 CAST_TO_STRING;
4275 str = valuePop(ctxt);
4276
4277 target = xmlBufferCreate();
4278 if (target) {
4279 point = xmlStrstr(str->stringval, find->stringval);
4280 if (point) {
4281 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4282 xmlBufferAdd(target, &str->stringval[offset],
4283 xmlStrlen(str->stringval) - offset);
4284 }
4285 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4286 xmlBufferFree(target);
4287 }
4288
4289 xmlXPathFreeObject(str);
4290 xmlXPathFreeObject(find);
4291}
4292
4293/**
4294 * xmlXPathNormalizeFunction:
4295 * @ctxt: the XPath Parser context
4296 * @nargs: the number of arguments
4297 *
4298 * Implement the normalize-space() XPath function
4299 * string normalize-space(string?)
4300 * The normalize-space function returns the argument string with white
4301 * space normalized by stripping leading and trailing whitespace
4302 * and replacing sequences of whitespace characters by a single
4303 * space. Whitespace characters are the same allowed by the S production
4304 * in XML. If the argument is omitted, it defaults to the context
4305 * node converted to a string, in other words the value of the context node.
4306 */
4307void
4308xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4309 xmlXPathObjectPtr obj = NULL;
4310 xmlChar *source = NULL;
4311 xmlBufferPtr target;
4312 xmlChar blank;
4313
4314 if (nargs == 0) {
4315 /* Use current context node */
4316 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4317 xmlXPathStringFunction(ctxt, 1);
4318 nargs = 1;
4319 }
4320
4321 CHECK_ARITY(1);
4322 CAST_TO_STRING;
4323 CHECK_TYPE(XPATH_STRING);
4324 obj = valuePop(ctxt);
4325 source = obj->stringval;
4326
4327 target = xmlBufferCreate();
4328 if (target && source) {
4329
4330 /* Skip leading whitespaces */
4331 while (IS_BLANK(*source))
4332 source++;
4333
4334 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4335 blank = 0;
4336 while (*source) {
4337 if (IS_BLANK(*source)) {
4338 blank = *source;
4339 } else {
4340 if (blank) {
4341 xmlBufferAdd(target, &blank, 1);
4342 blank = 0;
4343 }
4344 xmlBufferAdd(target, source, 1);
4345 }
4346 source++;
4347 }
4348
4349 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4350 xmlBufferFree(target);
4351 }
4352 xmlXPathFreeObject(obj);
4353}
4354
4355/**
4356 * xmlXPathTranslateFunction:
4357 * @ctxt: the XPath Parser context
4358 * @nargs: the number of arguments
4359 *
4360 * Implement the translate() XPath function
4361 * string translate(string, string, string)
4362 * The translate function returns the first argument string with
4363 * occurrences of characters in the second argument string replaced
4364 * by the character at the corresponding position in the third argument
4365 * string. For example, translate("bar","abc","ABC") returns the string
4366 * BAr. If there is a character in the second argument string with no
4367 * character at a corresponding position in the third argument string
4368 * (because the second argument string is longer than the third argument
4369 * string), then occurrences of that character in the first argument
4370 * string are removed. For example, translate("--aaa--","abc-","ABC")
4371 * returns "AAA". If a character occurs more than once in second
4372 * argument string, then the first occurrence determines the replacement
4373 * character. If the third argument string is longer than the second
4374 * argument string, then excess characters are ignored.
4375 */
4376void
4377xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4378 xmlXPathObjectPtr str;
4379 xmlXPathObjectPtr from;
4380 xmlXPathObjectPtr to;
4381 xmlBufferPtr target;
4382 int i, offset, max;
4383 xmlChar ch;
4384 const xmlChar *point;
4385
4386 CHECK_ARITY(3);
4387
4388 CAST_TO_STRING;
4389 to = valuePop(ctxt);
4390 CAST_TO_STRING;
4391 from = valuePop(ctxt);
4392 CAST_TO_STRING;
4393 str = valuePop(ctxt);
4394
4395 target = xmlBufferCreate();
4396 if (target) {
4397 max = xmlStrlen(to->stringval);
4398 for (i = 0; (ch = str->stringval[i]); i++) {
4399 point = xmlStrchr(from->stringval, ch);
4400 if (point) {
4401 /* Warning: This may not work with UTF-8 */
4402 offset = (int)(point - from->stringval);
4403 if (offset < max)
4404 xmlBufferAdd(target, &to->stringval[offset], 1);
4405 } else
4406 xmlBufferAdd(target, &ch, 1);
4407 }
4408 }
4409 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4410 xmlBufferFree(target);
4411 xmlXPathFreeObject(str);
4412 xmlXPathFreeObject(from);
4413 xmlXPathFreeObject(to);
4414}
4415
4416/**
4417 * xmlXPathBooleanFunction:
4418 * @ctxt: the XPath Parser context
4419 * @nargs: the number of arguments
4420 *
4421 * Implement the boolean() XPath function
4422 * boolean boolean(object)
4423 * he boolean function converts its argument to a boolean as follows:
4424 * - a number is true if and only if it is neither positive or
4425 * negative zero nor NaN
4426 * - a node-set is true if and only if it is non-empty
4427 * - a string is true if and only if its length is non-zero
4428 */
4429void
4430xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4431 xmlXPathObjectPtr cur;
4432 int res = 0;
4433
4434 CHECK_ARITY(1);
4435 cur = valuePop(ctxt);
4436 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
4437 switch (cur->type) {
4438 case XPATH_NODESET:
4439 case XPATH_XSLT_TREE:
4440 if ((cur->nodesetval == NULL) ||
4441 (cur->nodesetval->nodeNr == 0)) res = 0;
4442 else
4443 res = 1;
4444 break;
4445 case XPATH_STRING:
4446 if ((cur->stringval == NULL) ||
4447 (cur->stringval[0] == 0)) res = 0;
4448 else
4449 res = 1;
4450 break;
4451 case XPATH_BOOLEAN:
4452 valuePush(ctxt, cur);
4453 return;
4454 case XPATH_NUMBER:
4455 if (cur->floatval) res = 1;
4456 break;
4457 default:
4458 STRANGE
4459 }
4460 xmlXPathFreeObject(cur);
4461 valuePush(ctxt, xmlXPathNewBoolean(res));
4462}
4463
4464/**
4465 * xmlXPathNotFunction:
4466 * @ctxt: the XPath Parser context
4467 * @nargs: the number of arguments
4468 *
4469 * Implement the not() XPath function
4470 * boolean not(boolean)
4471 * The not function returns true if its argument is false,
4472 * and false otherwise.
4473 */
4474void
4475xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4476 CHECK_ARITY(1);
4477 CAST_TO_BOOLEAN;
4478 CHECK_TYPE(XPATH_BOOLEAN);
4479 ctxt->value->boolval = ! ctxt->value->boolval;
4480}
4481
4482/**
4483 * xmlXPathTrueFunction:
4484 * @ctxt: the XPath Parser context
4485 * @nargs: the number of arguments
4486 *
4487 * Implement the true() XPath function
4488 * boolean true()
4489 */
4490void
4491xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4492 CHECK_ARITY(0);
4493 valuePush(ctxt, xmlXPathNewBoolean(1));
4494}
4495
4496/**
4497 * xmlXPathFalseFunction:
4498 * @ctxt: the XPath Parser context
4499 * @nargs: the number of arguments
4500 *
4501 * Implement the false() XPath function
4502 * boolean false()
4503 */
4504void
4505xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4506 CHECK_ARITY(0);
4507 valuePush(ctxt, xmlXPathNewBoolean(0));
4508}
4509
4510/**
4511 * xmlXPathLangFunction:
4512 * @ctxt: the XPath Parser context
4513 * @nargs: the number of arguments
4514 *
4515 * Implement the lang() XPath function
4516 * boolean lang(string)
4517 * The lang function returns true or false depending on whether the
4518 * language of the context node as specified by xml:lang attributes
4519 * is the same as or is a sublanguage of the language specified by
4520 * the argument string. The language of the context node is determined
4521 * by the value of the xml:lang attribute on the context node, or, if
4522 * the context node has no xml:lang attribute, by the value of the
4523 * xml:lang attribute on the nearest ancestor of the context node that
4524 * has an xml:lang attribute. If there is no such attribute, then lang
4525 * returns false. If there is such an attribute, then lang returns
4526 * true if the attribute value is equal to the argument ignoring case,
4527 * or if there is some suffix starting with - such that the attribute
4528 * value is equal to the argument ignoring that suffix of the attribute
4529 * value and ignoring case.
4530 */
4531void
4532xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4533 xmlXPathObjectPtr val;
4534 const xmlChar *theLang;
4535 const xmlChar *lang;
4536 int ret = 0;
4537 int i;
4538
4539 CHECK_ARITY(1);
4540 CAST_TO_STRING;
4541 CHECK_TYPE(XPATH_STRING);
4542 val = valuePop(ctxt);
4543 lang = val->stringval;
4544 theLang = xmlNodeGetLang(ctxt->context->node);
4545 if ((theLang != NULL) && (lang != NULL)) {
4546 for (i = 0;lang[i] != 0;i++)
4547 if (toupper(lang[i]) != toupper(theLang[i]))
4548 goto not_equal;
4549 ret = 1;
4550 }
4551not_equal:
4552 xmlXPathFreeObject(val);
4553 valuePush(ctxt, xmlXPathNewBoolean(ret));
4554}
4555
4556/**
4557 * xmlXPathNumberFunction:
4558 * @ctxt: the XPath Parser context
4559 * @nargs: the number of arguments
4560 *
4561 * Implement the number() XPath function
4562 * number number(object?)
4563 */
4564void
4565xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4566 xmlXPathObjectPtr cur;
4567 double res;
4568
4569 if (nargs == 0) {
4570 if (ctxt->context->node == NULL) {
4571 valuePush(ctxt, xmlXPathNewFloat(0.0));
4572 } else {
4573 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4574
4575 res = xmlXPathStringEvalNumber(content);
4576 valuePush(ctxt, xmlXPathNewFloat(res));
4577 xmlFree(content);
4578 }
4579 return;
4580 }
4581
4582 CHECK_ARITY(1);
4583 cur = valuePop(ctxt);
4584 switch (cur->type) {
4585 case XPATH_UNDEFINED:
4586#ifdef DEBUG_EXPR
4587 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4588#endif
4589 valuePush(ctxt, xmlXPathNewFloat(0.0));
4590 break;
4591 case XPATH_XSLT_TREE:
4592 case XPATH_NODESET:
4593 valuePush(ctxt, cur);
4594 xmlXPathStringFunction(ctxt, 1);
4595 cur = valuePop(ctxt);
4596 case XPATH_STRING:
4597 res = xmlXPathStringEvalNumber(cur->stringval);
4598 valuePush(ctxt, xmlXPathNewFloat(res));
4599 xmlXPathFreeObject(cur);
4600 return;
4601 case XPATH_BOOLEAN:
4602 if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
4603 else valuePush(ctxt, xmlXPathNewFloat(0.0));
4604 xmlXPathFreeObject(cur);
4605 return;
4606 case XPATH_NUMBER:
4607 valuePush(ctxt, cur);
4608 return;
4609 case XPATH_USERS:
4610 case XPATH_POINT:
4611 case XPATH_RANGE:
4612 case XPATH_LOCATIONSET:
4613 TODO
4614 valuePush(ctxt, xmlXPathNewFloat(0.0));
4615 break;
4616 }
4617 STRANGE
4618}
4619
4620/**
4621 * xmlXPathSumFunction:
4622 * @ctxt: the XPath Parser context
4623 * @nargs: the number of arguments
4624 *
4625 * Implement the sum() XPath function
4626 * number sum(node-set)
4627 * The sum function returns the sum of the values of the nodes in
4628 * the argument node-set.
4629 */
4630void
4631xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4632 xmlXPathObjectPtr cur;
4633 int i;
4634
4635 CHECK_ARITY(1);
4636 if ((ctxt->value == NULL) ||
4637 ((ctxt->value->type != XPATH_NODESET) &&
4638 (ctxt->value->type != XPATH_XSLT_TREE)))
4639 XP_ERROR(XPATH_INVALID_TYPE);
4640 cur = valuePop(ctxt);
4641
4642 if (cur->nodesetval->nodeNr == 0) {
4643 valuePush(ctxt, xmlXPathNewFloat(0.0));
4644 } else {
4645 valuePush(ctxt,
4646 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4647 xmlXPathNumberFunction(ctxt, 1);
4648 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4649 valuePush(ctxt,
4650 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4651 xmlXPathAddValues(ctxt);
4652 }
4653 }
4654 xmlXPathFreeObject(cur);
4655}
4656
4657/**
4658 * xmlXPathFloorFunction:
4659 * @ctxt: the XPath Parser context
4660 * @nargs: the number of arguments
4661 *
4662 * Implement the floor() XPath function
4663 * number floor(number)
4664 * The floor function returns the largest (closest to positive infinity)
4665 * number that is not greater than the argument and that is an integer.
4666 */
4667void
4668xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4669 CHECK_ARITY(1);
4670 CAST_TO_NUMBER;
4671 CHECK_TYPE(XPATH_NUMBER);
4672#if 0
4673 ctxt->value->floatval = floor(ctxt->value->floatval);
4674#else
4675 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
4676 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
4677#endif
4678}
4679
4680/**
4681 * xmlXPathCeilingFunction:
4682 * @ctxt: the XPath Parser context
4683 * @nargs: the number of arguments
4684 *
4685 * Implement the ceiling() XPath function
4686 * number ceiling(number)
4687 * The ceiling function returns the smallest (closest to negative infinity)
4688 * number that is not less than the argument and that is an integer.
4689 */
4690void
4691xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4692 double f;
4693
4694 CHECK_ARITY(1);
4695 CAST_TO_NUMBER;
4696 CHECK_TYPE(XPATH_NUMBER);
4697
4698#if 0
4699 ctxt->value->floatval = ceil(ctxt->value->floatval);
4700#else
4701 f = (double)((int) ctxt->value->floatval);
4702 if (f != ctxt->value->floatval)
4703 ctxt->value->floatval = f + 1;
4704#endif
4705}
4706
4707/**
4708 * xmlXPathRoundFunction:
4709 * @ctxt: the XPath Parser context
4710 * @nargs: the number of arguments
4711 *
4712 * Implement the round() XPath function
4713 * number round(number)
4714 * The round function returns the number that is closest to the
4715 * argument and that is an integer. If there are two such numbers,
4716 * then the one that is even is returned.
4717 */
4718void
4719xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4720 double f;
4721
4722 CHECK_ARITY(1);
4723 CAST_TO_NUMBER;
4724 CHECK_TYPE(XPATH_NUMBER);
4725
4726 if ((ctxt->value->floatval == xmlXPathNAN) ||
4727 (ctxt->value->floatval == xmlXPathPINF) ||
4728 (ctxt->value->floatval == xmlXPathNINF) ||
4729 (ctxt->value->floatval == 0.0))
4730 return;
4731
4732#if 0
4733 f = floor(ctxt->value->floatval);
4734#else
4735 f = (double)((int) ctxt->value->floatval);
4736#endif
4737 if (ctxt->value->floatval < f + 0.5)
4738 ctxt->value->floatval = f;
4739 else
4740 ctxt->value->floatval = f + 1;
4741}
4742
4743/************************************************************************
4744 * *
4745 * The Parser *
4746 * *
4747 ************************************************************************/
4748
4749/*
4750 * a couple of forward declarations since we use a recursive call based
4751 * implementation.
4752 */
4753void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
4754void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
4755void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
4756#ifdef VMS
4757void xmlXPathEvalRelLocationPath(xmlXPathParserContextPtr ctxt);
4758#define xmlXPathEvalRelativeLocationPath xmlXPathEvalRelLocationPath
4759#else
4760void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
4761#endif
4762
4763/**
4764 * xmlXPathParseNCName:
4765 * @ctxt: the XPath Parser context
4766 *
4767 * parse an XML namespace non qualified name.
4768 *
4769 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
4770 *
4771 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
4772 * CombiningChar | Extender
4773 *
4774 * Returns the namespace name or NULL
4775 */
4776
4777xmlChar *
4778xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
4779 const xmlChar *q;
4780 xmlChar *ret = NULL;
4781
4782 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
4783 q = NEXT;
4784
4785 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
4786 (CUR == '.') || (CUR == '-') ||
4787 (CUR == '_') ||
4788 (IS_COMBINING(CUR)) ||
4789 (IS_EXTENDER(CUR)))
4790 NEXT;
4791
4792 ret = xmlStrndup(q, CUR_PTR - q);
4793
4794 return(ret);
4795}
4796
4797/**
4798 * xmlXPathParseQName:
4799 * @ctxt: the XPath Parser context
4800 * @prefix: a xmlChar **
4801 *
4802 * parse an XML qualified name
4803 *
4804 * [NS 5] QName ::= (Prefix ':')? LocalPart
4805 *
4806 * [NS 6] Prefix ::= NCName
4807 *
4808 * [NS 7] LocalPart ::= NCName
4809 *
4810 * Returns the function returns the local part, and prefix is updated
4811 * to get the Prefix if any.
4812 */
4813
4814xmlChar *
4815xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
4816 xmlChar *ret = NULL;
4817
4818 *prefix = NULL;
4819 ret = xmlXPathParseNCName(ctxt);
4820 if (CUR == ':') {
4821 *prefix = ret;
4822 NEXT;
4823 ret = xmlXPathParseNCName(ctxt);
4824 }
4825 return(ret);
4826}
4827
4828/**
4829 * xmlXPathParseName:
4830 * @ctxt: the XPath Parser context
4831 *
4832 * parse an XML name
4833 *
4834 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
4835 * CombiningChar | Extender
4836 *
4837 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
4838 *
4839 * Returns the namespace name or NULL
4840 */
4841
4842xmlChar *
4843xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
4844 const xmlChar *q;
4845 xmlChar *ret = NULL;
4846
4847 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
4848 q = NEXT;
4849
4850 /* TODO Make this UTF8 compliant !!! */
4851 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
4852 (CUR == '.') || (CUR == '-') ||
4853 (CUR == '_') || (CUR == ':') ||
4854 (IS_COMBINING(CUR)) ||
4855 (IS_EXTENDER(CUR)))
4856 NEXT;
4857
4858 ret = xmlStrndup(q, CUR_PTR - q);
4859
4860 return(ret);
4861}
4862
4863/**
4864 * xmlXPathStringEvalNumber:
4865 * @str: A string to scan
4866 *
4867 * [30] Number ::= Digits ('.' Digits?)?
4868 * | '.' Digits
4869 * [31] Digits ::= [0-9]+
4870 *
4871 * Parse and evaluate a Number in the string
4872 * In complement of the Number expression, this function also handles
4873 * negative values : '-' Number.
4874 *
4875 * Returns the double value.
4876 */
4877double
4878xmlXPathStringEvalNumber(const xmlChar *str) {
4879 const xmlChar *cur = str;
4880 double ret = 0.0;
4881 double mult = 1;
4882 int ok = 0;
4883 int isneg = 0;
4884
4885 while (IS_BLANK(*cur)) cur++;
4886 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
4887 return(xmlXPathNAN);
4888 }
4889 if (*cur == '-') {
4890 isneg = 1;
4891 cur++;
4892 }
4893 while ((*cur >= '0') && (*cur <= '9')) {
4894 ret = ret * 10 + (*cur - '0');
4895 ok = 1;
4896 cur++;
4897 }
4898 if (*cur == '.') {
4899 cur++;
4900 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
4901 return(xmlXPathNAN);
4902 }
4903 while ((*cur >= '0') && (*cur <= '9')) {
4904 mult /= 10;
4905 ret = ret + (*cur - '0') * mult;
4906 cur++;
4907 }
4908 }
4909 while (IS_BLANK(*cur)) cur++;
4910 if (*cur != 0) return(xmlXPathNAN);
4911 if (isneg) ret = -ret;
4912 return(ret);
4913}
4914
4915/**
4916 * xmlXPathEvalNumber:
4917 * @ctxt: the XPath Parser context
4918 *
4919 * [30] Number ::= Digits ('.' Digits?)?
4920 * | '.' Digits
4921 * [31] Digits ::= [0-9]+
4922 *
4923 * Parse and evaluate a Number, then push it on the stack
4924 *
4925 */
4926void
4927xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
4928 double ret = 0.0;
4929 double mult = 1;
4930 int ok = 0;
4931
4932 CHECK_ERROR;
4933 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
4934 XP_ERROR(XPATH_NUMBER_ERROR);
4935 }
4936 while ((CUR >= '0') && (CUR <= '9')) {
4937 ret = ret * 10 + (CUR - '0');
4938 ok = 1;
4939 NEXT;
4940 }
4941 if (CUR == '.') {
4942 NEXT;
4943 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
4944 XP_ERROR(XPATH_NUMBER_ERROR);
4945 }
4946 while ((CUR >= '0') && (CUR <= '9')) {
4947 mult /= 10;
4948 ret = ret + (CUR - '0') * mult;
4949 NEXT;
4950 }
4951 }
4952 valuePush(ctxt, xmlXPathNewFloat(ret));
4953}
4954
4955/**
4956 * xmlXPathEvalLiteral:
4957 * @ctxt: the XPath Parser context
4958 *
4959 * Parse a Literal and push it on the stack.
4960 *
4961 * [29] Literal ::= '"' [^"]* '"'
4962 * | "'" [^']* "'"
4963 *
4964 * TODO: xmlXPathEvalLiteral memory allocation could be improved.
4965 */
4966void
4967xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
4968 const xmlChar *q;
4969 xmlChar *ret = NULL;
4970
4971 if (CUR == '"') {
4972 NEXT;
4973 q = CUR_PTR;
4974 while ((IS_CHAR(CUR)) && (CUR != '"'))
4975 NEXT;
4976 if (!IS_CHAR(CUR)) {
4977 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
4978 } else {
4979 ret = xmlStrndup(q, CUR_PTR - q);
4980 NEXT;
4981 }
4982 } else if (CUR == '\'') {
4983 NEXT;
4984 q = CUR_PTR;
4985 while ((IS_CHAR(CUR)) && (CUR != '\''))
4986 NEXT;
4987 if (!IS_CHAR(CUR)) {
4988 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
4989 } else {
4990 ret = xmlStrndup(q, CUR_PTR - q);
4991 NEXT;
4992 }
4993 } else {
4994 XP_ERROR(XPATH_START_LITERAL_ERROR);
4995 }
4996 if (ret == NULL) return;
4997 valuePush(ctxt, xmlXPathNewString(ret));
4998 xmlFree(ret);
4999}
5000
5001/**
5002 * xmlXPathEvalVariableReference:
5003 * @ctxt: the XPath Parser context
5004 *
5005 * Parse a VariableReference, evaluate it and push it on the stack.
5006 *
5007 * The variable bindings consist of a mapping from variable names
5008 * to variable values. The value of a variable is an object, which
5009 * of any of the types that are possible for the value of an expression,
5010 * and may also be of additional types not specified here.
5011 *
5012 * Early evaluation is possible since:
5013 * The variable bindings [...] used to evaluate a subexpression are
5014 * always the same as those used to evaluate the containing expression.
5015 *
5016 * [36] VariableReference ::= '$' QName
5017 */
5018void
5019xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
5020 xmlChar *name;
5021 xmlChar *prefix;
5022 xmlXPathObjectPtr value;
5023
5024 SKIP_BLANKS;
5025 if (CUR != '$') {
5026 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5027 }
5028 NEXT;
5029 name = xmlXPathParseQName(ctxt, &prefix);
5030 if (name == NULL) {
5031 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5032 }
5033 if (prefix == NULL) {
5034 value = xmlXPathVariableLookup(ctxt->context, name);
5035 } else {
5036 TODO;
5037 value = NULL;
5038 }
5039 xmlFree(name);
5040 if (prefix != NULL) xmlFree(prefix);
5041 if (value == NULL) {
5042 XP_ERROR(XPATH_UNDEF_VARIABLE_ERROR);
5043 }
5044 valuePush(ctxt, value);
5045 SKIP_BLANKS;
5046}
5047
5048/**
5049 * xmlXPathIsNodeType:
5050 * @ctxt: the XPath Parser context
5051 * @name: a name string
5052 *
5053 * Is the name given a NodeType one.
5054 *
5055 * [38] NodeType ::= 'comment'
5056 * | 'text'
5057 * | 'processing-instruction'
5058 * | 'node'
5059 *
5060 * Returns 1 if true 0 otherwise
5061 */
5062int
5063xmlXPathIsNodeType(const xmlChar *name) {
5064 if (name == NULL)
5065 return(0);
5066
5067 if (xmlStrEqual(name, BAD_CAST "comment"))
5068 return(1);
5069 if (xmlStrEqual(name, BAD_CAST "text"))
5070 return(1);
5071 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5072 return(1);
5073 if (xmlStrEqual(name, BAD_CAST "node"))
5074 return(1);
5075 return(0);
5076}
5077
5078/**
5079 * xmlXPathEvalFunctionCall:
5080 * @ctxt: the XPath Parser context
5081 *
5082 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5083 * [17] Argument ::= Expr
5084 *
5085 * Parse and evaluate a function call, the evaluation of all arguments are
5086 * pushed on the stack
5087 */
5088void
5089xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
5090 xmlChar *name;
5091 xmlChar *prefix;
5092 xmlXPathFunction func;
5093 int nbargs = 0;
5094
5095 name = xmlXPathParseQName(ctxt, &prefix);
5096 if (name == NULL) {
5097 XP_ERROR(XPATH_EXPR_ERROR);
5098 }
5099 SKIP_BLANKS;
5100 if (prefix == NULL) {
5101 func = xmlXPathFunctionLookup(ctxt->context, name);
5102 } else {
5103 TODO;
5104 func = NULL;
5105 }
5106 if (func == NULL) {
5107 xmlFree(name);
5108 if (prefix != NULL) xmlFree(prefix);
5109 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
5110 }
5111#ifdef DEBUG_EXPR
5112 if (prefix == NULL)
5113 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5114 name);
5115 else
5116 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5117 prefix, name);
5118#endif
5119
5120 xmlFree(name);
5121 if (prefix != NULL) xmlFree(prefix);
5122
5123 if (CUR != '(') {
5124 XP_ERROR(XPATH_EXPR_ERROR);
5125 }
5126 NEXT;
5127 SKIP_BLANKS;
5128
5129 while (CUR != ')') {
5130 xmlXPathEvalExpr(ctxt);
5131 nbargs++;
5132 if (CUR == ')') break;
5133 if (CUR != ',') {
5134 XP_ERROR(XPATH_EXPR_ERROR);
5135 }
5136 NEXT;
5137 SKIP_BLANKS;
5138 }
5139 NEXT;
5140 SKIP_BLANKS;
5141 func(ctxt, nbargs);
5142}
5143
5144/**
5145 * xmlXPathEvalPrimaryExpr:
5146 * @ctxt: the XPath Parser context
5147 *
5148 * [15] PrimaryExpr ::= VariableReference
5149 * | '(' Expr ')'
5150 * | Literal
5151 * | Number
5152 * | FunctionCall
5153 *
5154 * Parse and evaluate a primary expression, then push the result on the stack
5155 */
5156void
5157xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
5158 SKIP_BLANKS;
5159 if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
5160 else if (CUR == '(') {
5161 NEXT;
5162 SKIP_BLANKS;
5163 xmlXPathEvalExpr(ctxt);
5164 if (CUR != ')') {
5165 XP_ERROR(XPATH_EXPR_ERROR);
5166 }
5167 NEXT;
5168 SKIP_BLANKS;
5169 } else if (IS_DIGIT(CUR)) {
5170 xmlXPathEvalNumber(ctxt);
5171 } else if ((CUR == '\'') || (CUR == '"')) {
5172 xmlXPathEvalLiteral(ctxt);
5173 } else {
5174 xmlXPathEvalFunctionCall(ctxt);
5175 }
5176 SKIP_BLANKS;
5177}
5178
5179/**
5180 * xmlXPathEvalFilterExpr:
5181 * @ctxt: the XPath Parser context
5182 *
5183 * [20] FilterExpr ::= PrimaryExpr
5184 * | FilterExpr Predicate
5185 *
5186 * Parse and evaluate a filter expression, then push the result on the stack
5187 * Square brackets are used to filter expressions in the same way that
5188 * they are used in location paths. It is an error if the expression to
5189 * be filtered does not evaluate to a node-set. The context node list
5190 * used for evaluating the expression in square brackets is the node-set
5191 * to be filtered listed in document order.
5192 */
5193
5194void
5195xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
5196 xmlXPathEvalPrimaryExpr(ctxt);
5197 CHECK_ERROR;
5198 SKIP_BLANKS;
5199
5200 while (CUR == '[') {
5201 if ((ctxt->value == NULL) ||
5202 ((ctxt->value->type != XPATH_NODESET) &&
5203 (ctxt->value->type != XPATH_LOCATIONSET)))
5204 XP_ERROR(XPATH_INVALID_TYPE)
5205
5206 if (ctxt->value->type == XPATH_NODESET)
5207 xmlXPathEvalPredicate(ctxt);
5208 else
5209 xmlXPtrEvalRangePredicate(ctxt);
5210 SKIP_BLANKS;
5211 }
5212
5213
5214}
5215
5216/**
5217 * xmlXPathScanName:
5218 * @ctxt: the XPath Parser context
5219 *
5220 * Trickery: parse an XML name but without consuming the input flow
5221 * Needed to avoid insanity in the parser state.
5222 *
5223 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5224 * CombiningChar | Extender
5225 *
5226 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5227 *
5228 * [6] Names ::= Name (S Name)*
5229 *
5230 * Returns the Name parsed or NULL
5231 */
5232
5233xmlChar *
5234xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5235 xmlChar buf[XML_MAX_NAMELEN];
5236 int len = 0;
5237
5238 SKIP_BLANKS;
5239 if (!IS_LETTER(CUR) && (CUR != '_') &&
5240 (CUR != ':')) {
5241 return(NULL);
5242 }
5243
5244 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5245 (NXT(len) == '.') || (NXT(len) == '-') ||
5246 (NXT(len) == '_') || (NXT(len) == ':') ||
5247 (IS_COMBINING(NXT(len))) ||
5248 (IS_EXTENDER(NXT(len)))) {
5249 buf[len] = NXT(len);
5250 len++;
5251 if (len >= XML_MAX_NAMELEN) {
5252 xmlGenericError(xmlGenericErrorContext,
5253 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5254 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5255 (NXT(len) == '.') || (NXT(len) == '-') ||
5256 (NXT(len) == '_') || (NXT(len) == ':') ||
5257 (IS_COMBINING(NXT(len))) ||
5258 (IS_EXTENDER(NXT(len))))
5259 len++;
5260 break;
5261 }
5262 }
5263 return(xmlStrndup(buf, len));
5264}
5265
5266/**
5267 * xmlXPathEvalPathExpr:
5268 * @ctxt: the XPath Parser context
5269 *
5270 * [19] PathExpr ::= LocationPath
5271 * | FilterExpr
5272 * | FilterExpr '/' RelativeLocationPath
5273 * | FilterExpr '//' RelativeLocationPath
5274 *
5275 * Parse and evaluate a path expression, then push the result on the stack
5276 * The / operator and // operators combine an arbitrary expression
5277 * and a relative location path. It is an error if the expression
5278 * does not evaluate to a node-set.
5279 * The / operator does composition in the same way as when / is
5280 * used in a location path. As in location paths, // is short for
5281 * /descendant-or-self::node()/.
5282 */
5283
5284void
5285xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
5286 int lc = 1; /* Should we branch to LocationPath ? */
5287 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5288
5289 SKIP_BLANKS;
5290 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5291 (CUR == '\'') || (CUR == '"')) {
5292 lc = 0;
5293 } else if (CUR == '*') {
5294 /* relative or absolute location path */
5295 lc = 1;
5296 } else if (CUR == '/') {
5297 /* relative or absolute location path */
5298 lc = 1;
5299 } else if (CUR == '@') {
5300 /* relative abbreviated attribute location path */
5301 lc = 1;
5302 } else if (CUR == '.') {
5303 /* relative abbreviated attribute location path */
5304 lc = 1;
5305 } else {
5306 /*
5307 * Problem is finding if we have a name here whether it's:
5308 * - a nodetype
5309 * - a function call in which case it's followed by '('
5310 * - an axis in which case it's followed by ':'
5311 * - a element name
5312 * We do an a priori analysis here rather than having to
5313 * maintain parsed token content through the recursive function
5314 * calls. This looks uglier but makes the code quite easier to
5315 * read/write/debug.
5316 */
5317 SKIP_BLANKS;
5318 name = xmlXPathScanName(ctxt);
5319 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5320#ifdef DEBUG_STEP
5321 xmlGenericError(xmlGenericErrorContext,
5322 "PathExpr: Axis\n");
5323#endif
5324 lc = 1;
5325 xmlFree(name);
5326 } else if (name != NULL) {
5327 int len =xmlStrlen(name);
5328 int blank = 0;
5329
5330
5331 while (NXT(len) != 0) {
5332 if (NXT(len) == '/') {
5333 /* element name */
5334#ifdef DEBUG_STEP
5335 xmlGenericError(xmlGenericErrorContext,
5336 "PathExpr: AbbrRelLocation\n");
5337#endif
5338 lc = 1;
5339 break;
5340 } else if (IS_BLANK(NXT(len))) {
5341 /* skip to next */
5342 blank = 1;
5343 } else if (NXT(len) == ':') {
5344#ifdef DEBUG_STEP
5345 xmlGenericError(xmlGenericErrorContext,
5346 "PathExpr: AbbrRelLocation\n");
5347#endif
5348 lc = 1;
5349 break;
5350 } else if ((NXT(len) == '(')) {
5351 /* Note Type or Function */
5352 if (xmlXPathIsNodeType(name)) {
5353#ifdef DEBUG_STEP
5354 xmlGenericError(xmlGenericErrorContext,
5355 "PathExpr: Type search\n");
5356#endif
5357 lc = 1;
5358 } else {
5359#ifdef DEBUG_STEP
5360 xmlGenericError(xmlGenericErrorContext,
5361 "PathExpr: function call\n");
5362#endif
5363 lc = 0;
5364 }
5365 break;
5366 } else if ((NXT(len) == '[')) {
5367 /* element name */
5368#ifdef DEBUG_STEP
5369 xmlGenericError(xmlGenericErrorContext,
5370 "PathExpr: AbbrRelLocation\n");
5371#endif
5372 lc = 1;
5373 break;
5374 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5375 (NXT(len) == '=')) {
5376 lc = 1;
5377 break;
5378 } else {
5379 lc = 1;
5380 break;
5381 }
5382 len++;
5383 }
5384 if (NXT(len) == 0) {
5385#ifdef DEBUG_STEP
5386 xmlGenericError(xmlGenericErrorContext,
5387 "PathExpr: AbbrRelLocation\n");
5388#endif
5389 /* element name */
5390 lc = 1;
5391 }
5392 xmlFree(name);
5393 } else {
5394 /* make sure all cases are covered explicitely */
5395 XP_ERROR(XPATH_EXPR_ERROR);
5396 }
5397 }
5398
5399 if (lc) {
5400 if (CUR == '/')
5401 xmlXPathRoot(ctxt);
5402 else {
5403 /* TAG:9999 */
5404 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5405 }
5406 xmlXPathEvalLocationPath(ctxt);
5407 } else {
5408 xmlXPathEvalFilterExpr(ctxt);
5409 CHECK_ERROR;
5410 if ((CUR == '/') && (NXT(1) == '/')) {
5411 SKIP(2);
5412 SKIP_BLANKS;
5413 xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
5414 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5415 ctxt->context->node = NULL;
5416 xmlXPathEvalRelativeLocationPath(ctxt);
5417 } else if (CUR == '/') {
5418 xmlXPathEvalRelativeLocationPath(ctxt);
5419 }
5420 }
5421 SKIP_BLANKS;
5422}
5423
5424/**
5425 * xmlXPathEvalUnionExpr:
5426 * @ctxt: the XPath Parser context
5427 *
5428 * [18] UnionExpr ::= PathExpr
5429 * | UnionExpr '|' PathExpr
5430 *
5431 * Parse and evaluate an union expression, then push the result on the stack
5432 */
5433
5434void
5435xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
5436 int sort = 0;
5437 xmlXPathEvalPathExpr(ctxt);
5438 CHECK_ERROR;
5439 SKIP_BLANKS;
5440 while (CUR == '|') {
5441 xmlXPathObjectPtr obj1,obj2, tmp;
5442
5443 sort = 1;
5444 CHECK_TYPE(XPATH_NODESET);
5445 obj1 = valuePop(ctxt);
5446 tmp = xmlXPathNewNodeSet(ctxt->context->node);
5447 valuePush(ctxt, tmp);
5448
5449 NEXT;
5450 SKIP_BLANKS;
5451 xmlXPathEvalPathExpr(ctxt);
5452
5453 CHECK_TYPE(XPATH_NODESET);
5454 obj2 = valuePop(ctxt);
5455 obj1->nodesetval = xmlXPathNodeSetMerge(obj1->nodesetval,
5456 obj2->nodesetval);
5457 if (ctxt->value == tmp) {
5458 tmp = valuePop(ctxt);
5459 xmlXPathFreeObject(tmp);
5460 }
5461 valuePush(ctxt, obj1);
5462 xmlXPathFreeObject(obj2);
5463 SKIP_BLANKS;
5464 }
5465 if (sort) {
5466 }
5467}
5468
5469/**
5470 * xmlXPathEvalUnaryExpr:
5471 * @ctxt: the XPath Parser context
5472 *
5473 * [27] UnaryExpr ::= UnionExpr
5474 * | '-' UnaryExpr
5475 *
5476 * Parse and evaluate an unary expression, then push the result on the stack
5477 */
5478
5479void
5480xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
5481 int minus = 0;
5482
5483 SKIP_BLANKS;
5484 if (CUR == '-') {
5485 minus = 1;
5486 NEXT;
5487 SKIP_BLANKS;
5488 }
5489 xmlXPathEvalUnionExpr(ctxt);
5490 CHECK_ERROR;
5491 if (minus) {
5492 xmlXPathValueFlipSign(ctxt);
5493 }
5494}
5495
5496/**
5497 * xmlXPathEvalMultiplicativeExpr:
5498 * @ctxt: the XPath Parser context
5499 *
5500 * [26] MultiplicativeExpr ::= UnaryExpr
5501 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5502 * | MultiplicativeExpr 'div' UnaryExpr
5503 * | MultiplicativeExpr 'mod' UnaryExpr
5504 * [34] MultiplyOperator ::= '*'
5505 *
5506 * Parse and evaluate an Additive expression, then push the result on the stack
5507 */
5508
5509void
5510xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5511 xmlXPathEvalUnaryExpr(ctxt);
5512 CHECK_ERROR;
5513 SKIP_BLANKS;
5514 while ((CUR == '*') ||
5515 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5516 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5517 int op = -1;
5518
5519 if (CUR == '*') {
5520 op = 0;
5521 NEXT;
5522 } else if (CUR == 'd') {
5523 op = 1;
5524 SKIP(3);
5525 } else if (CUR == 'm') {
5526 op = 2;
5527 SKIP(3);
5528 }
5529 SKIP_BLANKS;
5530 xmlXPathEvalUnaryExpr(ctxt);
5531 CHECK_ERROR;
5532 switch (op) {
5533 case 0:
5534 xmlXPathMultValues(ctxt);
5535 break;
5536 case 1:
5537 xmlXPathDivValues(ctxt);
5538 break;
5539 case 2:
5540 xmlXPathModValues(ctxt);
5541 break;
5542 }
5543 SKIP_BLANKS;
5544 }
5545}
5546
5547/**
5548 * xmlXPathEvalAdditiveExpr:
5549 * @ctxt: the XPath Parser context
5550 *
5551 * [25] AdditiveExpr ::= MultiplicativeExpr
5552 * | AdditiveExpr '+' MultiplicativeExpr
5553 * | AdditiveExpr '-' MultiplicativeExpr
5554 *
5555 * Parse and evaluate an Additive expression, then push the result on the stack
5556 */
5557
5558void
5559xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
5560 xmlXPathEvalMultiplicativeExpr(ctxt);
5561 CHECK_ERROR;
5562 SKIP_BLANKS;
5563 while ((CUR == '+') || (CUR == '-')) {
5564 int plus;
5565
5566 if (CUR == '+') plus = 1;
5567 else plus = 0;
5568 NEXT;
5569 SKIP_BLANKS;
5570 xmlXPathEvalMultiplicativeExpr(ctxt);
5571 CHECK_ERROR;
5572 if (plus) xmlXPathAddValues(ctxt);
5573 else xmlXPathSubValues(ctxt);
5574 SKIP_BLANKS;
5575 }
5576}
5577
5578/**
5579 * xmlXPathEvalRelationalExpr:
5580 * @ctxt: the XPath Parser context
5581 *
5582 * [24] RelationalExpr ::= AdditiveExpr
5583 * | RelationalExpr '<' AdditiveExpr
5584 * | RelationalExpr '>' AdditiveExpr
5585 * | RelationalExpr '<=' AdditiveExpr
5586 * | RelationalExpr '>=' AdditiveExpr
5587 *
5588 * A <= B > C is allowed ? Answer from James, yes with
5589 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5590 * which is basically what got implemented.
5591 *
5592 * Parse and evaluate a Relational expression, then push the result
5593 * on the stack
5594 */
5595
5596void
5597xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
5598 xmlXPathEvalAdditiveExpr(ctxt);
5599 CHECK_ERROR;
5600 SKIP_BLANKS;
5601 while ((CUR == '<') ||
5602 (CUR == '>') ||
5603 ((CUR == '<') && (NXT(1) == '=')) ||
5604 ((CUR == '>') && (NXT(1) == '='))) {
5605 int inf, strict, ret;
5606
5607 if (CUR == '<') inf = 1;
5608 else inf = 0;
5609 if (NXT(1) == '=') strict = 0;
5610 else strict = 1;
5611 NEXT;
5612 if (!strict) NEXT;
5613 SKIP_BLANKS;
5614 xmlXPathEvalAdditiveExpr(ctxt);
5615 CHECK_ERROR;
5616 ret = xmlXPathCompareValues(ctxt, inf, strict);
5617 valuePush(ctxt, xmlXPathNewBoolean(ret));
5618 SKIP_BLANKS;
5619 }
5620}
5621
5622/**
5623 * xmlXPathEvalEqualityExpr:
5624 * @ctxt: the XPath Parser context
5625 *
5626 * [23] EqualityExpr ::= RelationalExpr
5627 * | EqualityExpr '=' RelationalExpr
5628 * | EqualityExpr '!=' RelationalExpr
5629 *
5630 * A != B != C is allowed ? Answer from James, yes with
5631 * (RelationalExpr = RelationalExpr) = RelationalExpr
5632 * (RelationalExpr != RelationalExpr) != RelationalExpr
5633 * which is basically what got implemented.
5634 *
5635 * Parse and evaluate an Equality expression, then push the result on the stack
5636 *
5637 */
5638void
5639xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
5640 xmlXPathEvalRelationalExpr(ctxt);
5641 CHECK_ERROR;
5642 SKIP_BLANKS;
5643 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
5644 xmlXPathObjectPtr res;
5645 int eq, equal;
5646
5647 if (CUR == '=') eq = 1;
5648 else eq = 0;
5649 NEXT;
5650 if (!eq) NEXT;
5651 SKIP_BLANKS;
5652 xmlXPathEvalRelationalExpr(ctxt);
5653 CHECK_ERROR;
5654 equal = xmlXPathEqualValues(ctxt);
5655 if (eq) res = xmlXPathNewBoolean(equal);
5656 else res = xmlXPathNewBoolean(!equal);
5657 valuePush(ctxt, res);
5658 SKIP_BLANKS;
5659 }
5660}
5661
5662/**
5663 * xmlXPathEvalAndExpr:
5664 * @ctxt: the XPath Parser context
5665 *
5666 * [22] AndExpr ::= EqualityExpr
5667 * | AndExpr 'and' EqualityExpr
5668 *
5669 * Parse and evaluate an AND expression, then push the result on the stack
5670 *
5671 */
5672void
5673xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
5674 xmlXPathEvalEqualityExpr(ctxt);
5675 CHECK_ERROR;
5676 SKIP_BLANKS;
5677 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
5678 xmlXPathObjectPtr arg1, arg2;
5679
5680 SKIP(3);
5681 SKIP_BLANKS;
5682 xmlXPathEvalEqualityExpr(ctxt);
5683 CHECK_ERROR;
5684 xmlXPathBooleanFunction(ctxt, 1);
5685 arg2 = valuePop(ctxt);
5686 xmlXPathBooleanFunction(ctxt, 1);
5687 arg1 = valuePop(ctxt);
5688 arg1->boolval &= arg2->boolval;
5689 valuePush(ctxt, arg1);
5690 xmlXPathFreeObject(arg2);
5691 SKIP_BLANKS;
5692 }
5693}
5694
5695/**
5696 * xmlXPathEvalExpr:
5697 * @ctxt: the XPath Parser context
5698 *
5699 * [14] Expr ::= OrExpr
5700 * [21] OrExpr ::= AndExpr
5701 * | OrExpr 'or' AndExpr
5702 *
5703 * Parse and evaluate an expression, then push the result on the stack
5704 *
5705 */
5706void
5707xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
5708 xmlXPathEvalAndExpr(ctxt);
5709 CHECK_ERROR;
5710 SKIP_BLANKS;
5711 while ((CUR == 'o') && (NXT(1) == 'r')) {
5712 xmlXPathObjectPtr arg1, arg2;
5713
5714 SKIP(2);
5715 SKIP_BLANKS;
5716 xmlXPathEvalAndExpr(ctxt);
5717 CHECK_ERROR;
5718 xmlXPathBooleanFunction(ctxt, 1);
5719 arg2 = valuePop(ctxt);
5720 xmlXPathBooleanFunction(ctxt, 1);
5721 arg1 = valuePop(ctxt);
5722 arg1->boolval |= arg2->boolval;
5723 valuePush(ctxt, arg1);
5724 xmlXPathFreeObject(arg2);
5725 SKIP_BLANKS;
5726 }
5727 if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET) &&
5728 (ctxt->value->nodesetval != NULL))
5729 xmlXPathNodeSetSort(ctxt->value->nodesetval);
5730}
5731
5732/**
5733 * xmlXPathEvaluatePredicateResult:
5734 * @ctxt: the XPath Parser context
5735 * @res: the Predicate Expression evaluation result
5736 *
5737 * Evaluate a predicate result for the current node.
5738 * A PredicateExpr is evaluated by evaluating the Expr and converting
5739 * the result to a boolean. If the result is a number, the result will
5740 * be converted to true if the number is equal to the position of the
5741 * context node in the context node list (as returned by the position
5742 * function) and will be converted to false otherwise; if the result
5743 * is not a number, then the result will be converted as if by a call
5744 * to the boolean function.
5745 *
5746 * Return 1 if predicate is true, 0 otherwise
5747 */
5748int
5749xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
5750 xmlXPathObjectPtr res) {
5751 if (res == NULL) return(0);
5752 switch (res->type) {
5753 case XPATH_BOOLEAN:
5754 return(res->boolval);
5755 case XPATH_NUMBER:
5756 return(res->floatval == ctxt->context->proximityPosition);
5757 case XPATH_NODESET:
5758 case XPATH_XSLT_TREE:
5759 return(res->nodesetval->nodeNr != 0);
5760 case XPATH_STRING:
5761 return((res->stringval != NULL) &&
5762 (xmlStrlen(res->stringval) != 0));
5763 default:
5764 STRANGE
5765 }
5766 return(0);
5767}
5768
5769/**
5770 * xmlXPathEvalPredicate:
5771 * @ctxt: the XPath Parser context
5772 *
5773 * [8] Predicate ::= '[' PredicateExpr ']'
5774 * [9] PredicateExpr ::= Expr
5775 *
5776 * ---------------------
5777 * For each node in the node-set to be filtered, the PredicateExpr is
5778 * evaluated with that node as the context node, with the number of nodes
5779 * in the node-set as the context size, and with the proximity position
5780 * of the node in the node-set with respect to the axis as the context
5781 * position; if PredicateExpr evaluates to true for that node, the node
5782 * is included in the new node-set; otherwise, it is not included.
5783 * ---------------------
5784 *
5785 * Parse and evaluate a predicate for all the elements of the
5786 * current node list. Then refine the list by removing all
5787 * nodes where the predicate is false.
5788 */
5789void
5790xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
5791 const xmlChar *cur;
5792 xmlXPathObjectPtr res;
5793 xmlXPathObjectPtr obj, tmp;
5794 xmlNodeSetPtr newset = NULL;
5795 xmlNodeSetPtr oldset;
5796 xmlNodePtr oldnode;
5797 int i;
5798
5799 SKIP_BLANKS;
5800 if (CUR != '[') {
5801 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
5802 }
5803 NEXT;
5804 SKIP_BLANKS;
5805
5806 /*
5807 * Extract the old set, and then evaluate the result of the
5808 * expression for all the element in the set. use it to grow
5809 * up a new set.
5810 */
5811 CHECK_TYPE(XPATH_NODESET);
5812 obj = valuePop(ctxt);
5813 oldset = obj->nodesetval;
5814 oldnode = ctxt->context->node;
5815 ctxt->context->node = NULL;
5816
5817 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
5818 ctxt->context->contextSize = 0;
5819 ctxt->context->proximityPosition = 0;
5820 xmlXPathEvalExpr(ctxt);
5821 res = valuePop(ctxt);
5822 if (res != NULL)
5823 xmlXPathFreeObject(res);
5824 valuePush(ctxt, obj);
5825 CHECK_ERROR;
5826 } else {
5827 /*
5828 * Save the expression pointer since we will have to evaluate
5829 * it multiple times. Initialize the new set.
5830 */
5831 cur = ctxt->cur;
5832 newset = xmlXPathNodeSetCreate(NULL);
5833
5834 for (i = 0; i < oldset->nodeNr; i++) {
5835 ctxt->cur = cur;
5836
5837 /*
5838 * Run the evaluation with a node list made of a single item
5839 * in the nodeset.
5840 */
5841 ctxt->context->node = oldset->nodeTab[i];
5842 tmp = xmlXPathNewNodeSet(ctxt->context->node);
5843 valuePush(ctxt, tmp);
5844 ctxt->context->contextSize = oldset->nodeNr;
5845 ctxt->context->proximityPosition = i + 1;
5846
5847 xmlXPathEvalExpr(ctxt);
5848 CHECK_ERROR;
5849
5850 /*
5851 * The result of the evaluation need to be tested to
5852 * decided whether the filter succeeded or not
5853 */
5854 res = valuePop(ctxt);
5855 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
5856 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
5857 }
5858
5859 /*
5860 * Cleanup
5861 */
5862 if (res != NULL)
5863 xmlXPathFreeObject(res);
5864 if (ctxt->value == tmp) {
5865 res = valuePop(ctxt);
5866 xmlXPathFreeObject(res);
5867 }
5868
5869 ctxt->context->node = NULL;
5870 }
5871
5872 /*
5873 * The result is used as the new evaluation set.
5874 */
5875 xmlXPathFreeObject(obj);
5876 ctxt->context->node = NULL;
5877 ctxt->context->contextSize = -1;
5878 ctxt->context->proximityPosition = -1;
5879 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
5880 }
5881 if (CUR != ']') {
5882 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
5883 }
5884
5885 NEXT;
5886 SKIP_BLANKS;
5887#ifdef DEBUG_STEP
5888 xmlGenericError(xmlGenericErrorContext, "After predicate : ");
5889 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
5890 ctxt->value->nodesetval);
5891#endif
5892 ctxt->context->node = oldnode;
5893}
5894
5895/**
5896 * xmlXPathEvalNodeTest:
5897 * @ctxt: the XPath Parser context
5898 * @test: pointer to a xmlXPathTestVal
5899 * @type: pointer to a xmlXPathTypeVal
5900 * @prefix: placeholder for a possible name prefix
5901 *
5902 * [7] NodeTest ::= NameTest
5903 * | NodeType '(' ')'
5904 * | 'processing-instruction' '(' Literal ')'
5905 *
5906 * [37] NameTest ::= '*'
5907 * | NCName ':' '*'
5908 * | QName
5909 * [38] NodeType ::= 'comment'
5910 * | 'text'
5911 * | 'processing-instruction'
5912 * | 'node'
5913 *
5914 * Returns the name found and update @test, @type and @prefix appropriately
5915 */
5916xmlChar *
5917xmlXPathEvalNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
5918 xmlXPathTypeVal *type, const xmlChar **prefix, xmlChar *name) {
5919 int blanks;
5920
5921 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
5922 STRANGE;
5923 return(NULL);
5924 }
5925 *type = 0;
5926 *test = 0;
5927 *prefix = NULL;
5928 SKIP_BLANKS;
5929
5930 if ((name == NULL) && (CUR == '*')) {
5931 /*
5932 * All elements
5933 */
5934 NEXT;
5935 *test = NODE_TEST_ALL;
5936 return(NULL);
5937 }
5938
5939 if (name == NULL)
5940 name = xmlXPathParseNCName(ctxt);
5941 if (name == NULL) {
5942 XP_ERROR0(XPATH_EXPR_ERROR);
5943 }
5944
5945 blanks = IS_BLANK(CUR);
5946 SKIP_BLANKS;
5947 if (CUR == '(') {
5948 NEXT;
5949 /*
5950 * NodeType or PI search
5951 */
5952 if (xmlStrEqual(name, BAD_CAST "comment"))
5953 *type = NODE_TYPE_COMMENT;
5954 else if (xmlStrEqual(name, BAD_CAST "node"))
5955 *type = NODE_TYPE_NODE;
5956 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5957 *type = NODE_TYPE_PI;
5958 else if (xmlStrEqual(name, BAD_CAST "text"))
5959 *type = NODE_TYPE_TEXT;
5960 else {
5961 if (name != NULL)
5962 xmlFree(name);
5963 XP_ERROR0(XPATH_EXPR_ERROR);
5964 }
5965
5966 *test = NODE_TEST_TYPE;
5967
5968 SKIP_BLANKS;
5969 if (*type == NODE_TYPE_PI) {
5970 /*
5971 * Specific case: search a PI by name.
5972 */
5973 xmlXPathObjectPtr cur;
5974
5975 if (name != NULL)
5976 xmlFree(name);
5977
5978 xmlXPathEvalLiteral(ctxt);
5979 CHECK_ERROR 0;
5980 xmlXPathStringFunction(ctxt, 1);
5981 CHECK_ERROR0;
5982 cur = valuePop(ctxt);
5983 name = xmlStrdup(cur->stringval);
5984 xmlXPathFreeObject(cur);
5985 SKIP_BLANKS;
5986 }
5987 if (CUR != ')') {
5988 if (name != NULL)
5989 xmlFree(name);
5990 XP_ERROR0(XPATH_UNCLOSED_ERROR);
5991 }
5992 NEXT;
5993 return(name);
5994 }
5995 *test = NODE_TEST_NAME;
5996 if ((!blanks) && (CUR == ':')) {
5997 NEXT;
5998
5999 /*
6000 * get the namespace name for this prefix
6001 */
6002 *prefix = xmlXPathNsLookup(ctxt->context, name);
6003 if (name != NULL)
6004 xmlFree(name);
6005 if (*prefix == NULL) {
6006 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6007 }
6008
6009 if (CUR == '*') {
6010 /*
6011 * All elements
6012 */
6013 NEXT;
6014 *test = NODE_TEST_ALL;
6015 return(NULL);
6016 }
6017
6018 name = xmlXPathParseNCName(ctxt);
6019 if (name == NULL) {
6020 XP_ERROR0(XPATH_EXPR_ERROR);
6021 }
6022 }
6023 return(name);
6024}
6025
6026/**
6027 * xmlXPathIsAxisName:
6028 * @name: a preparsed name token
6029 *
6030 * [6] AxisName ::= 'ancestor'
6031 * | 'ancestor-or-self'
6032 * | 'attribute'
6033 * | 'child'
6034 * | 'descendant'
6035 * | 'descendant-or-self'
6036 * | 'following'
6037 * | 'following-sibling'
6038 * | 'namespace'
6039 * | 'parent'
6040 * | 'preceding'
6041 * | 'preceding-sibling'
6042 * | 'self'
6043 *
6044 * Returns the axis or 0
6045 */
6046xmlXPathAxisVal
6047xmlXPathIsAxisName(const xmlChar *name) {
6048 xmlXPathAxisVal ret = 0;
6049 switch (name[0]) {
6050 case 'a':
6051 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6052 ret = AXIS_ANCESTOR;
6053 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6054 ret = AXIS_ANCESTOR_OR_SELF;
6055 if (xmlStrEqual(name, BAD_CAST "attribute"))
6056 ret = AXIS_ATTRIBUTE;
6057 break;
6058 case 'c':
6059 if (xmlStrEqual(name, BAD_CAST "child"))
6060 ret = AXIS_CHILD;
6061 break;
6062 case 'd':
6063 if (xmlStrEqual(name, BAD_CAST "descendant"))
6064 ret = AXIS_DESCENDANT;
6065 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6066 ret = AXIS_DESCENDANT_OR_SELF;
6067 break;
6068 case 'f':
6069 if (xmlStrEqual(name, BAD_CAST "following"))
6070 ret = AXIS_FOLLOWING;
6071 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6072 ret = AXIS_FOLLOWING_SIBLING;
6073 break;
6074 case 'n':
6075 if (xmlStrEqual(name, BAD_CAST "namespace"))
6076 ret = AXIS_NAMESPACE;
6077 break;
6078 case 'p':
6079 if (xmlStrEqual(name, BAD_CAST "parent"))
6080 ret = AXIS_PARENT;
6081 if (xmlStrEqual(name, BAD_CAST "preceding"))
6082 ret = AXIS_PRECEDING;
6083 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6084 ret = AXIS_PRECEDING_SIBLING;
6085 break;
6086 case 's':
6087 if (xmlStrEqual(name, BAD_CAST "self"))
6088 ret = AXIS_SELF;
6089 break;
6090 }
6091 return(ret);
6092}
6093
6094/**
6095 * xmlXPathEvalAxisSpecifier:
6096 * @ctxt: the XPath Parser context
6097 *
6098 *
6099 * Returns the axis found
6100 */
6101xmlXPathAxisVal
6102xmlXPathEvalAxisSpecifier(xmlXPathParserContextPtr ctxt) {
6103 xmlXPathAxisVal ret = AXIS_CHILD;
6104 int blank = 0;
6105 xmlChar *name;
6106
6107 if (CUR == '@') {
6108 NEXT;
6109 return(AXIS_ATTRIBUTE);
6110 } else {
6111 name = xmlXPathParseNCName(ctxt);
6112 if (name == NULL) {
6113 XP_ERROR0(XPATH_EXPR_ERROR);
6114 }
6115 if (IS_BLANK(CUR))
6116 blank = 1;
6117 SKIP_BLANKS;
6118 if ((CUR == ':') && (NXT(1) == ':')) {
6119 ret = xmlXPathIsAxisName(name);
6120 } else if ((blank) && (CUR == ':'))
6121 XP_ERROR0(XPATH_EXPR_ERROR);
6122
6123 xmlFree(name);
6124 }
6125 return(ret);
6126}
6127
6128/**
6129 * xmlXPathEvalStep:
6130 * @ctxt: the XPath Parser context
6131 *
6132 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6133 * | AbbreviatedStep
6134 *
6135 * [12] AbbreviatedStep ::= '.' | '..'
6136 *
6137 * [5] AxisSpecifier ::= AxisName '::'
6138 * | AbbreviatedAxisSpecifier
6139 *
6140 * [13] AbbreviatedAxisSpecifier ::= '@'?
6141 *
6142 * Modified for XPtr range support as:
6143 *
6144 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6145 * | AbbreviatedStep
6146 * | 'range-to' '(' Expr ')' Predicate*
6147 *
6148 * Evaluate one step in a Location Path
6149 * A location step of . is short for self::node(). This is
6150 * particularly useful in conjunction with //. For example, the
6151 * location path .//para is short for
6152 * self::node()/descendant-or-self::node()/child::para
6153 * and so will select all para descendant elements of the context
6154 * node.
6155 * Similarly, a location step of .. is short for parent::node().
6156 * For example, ../title is short for parent::node()/child::title
6157 * and so will select the title children of the parent of the context
6158 * node.
6159 */
6160void
6161xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
6162 SKIP_BLANKS;
6163 if ((CUR == '.') && (NXT(1) == '.')) {
6164 SKIP(2);
6165 SKIP_BLANKS;
6166 xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
6167 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6168 } else if (CUR == '.') {
6169 NEXT;
6170 SKIP_BLANKS;
6171 } else {
6172 xmlChar *name = NULL;
6173 const xmlChar *prefix = NULL;
6174 xmlXPathTestVal test;
6175 xmlXPathAxisVal axis;
6176 xmlXPathTypeVal type;
6177
6178 /*
6179 * The modification needed for XPointer change to the production
6180 */
6181#ifdef LIBXML_XPTR_ENABLED
6182 if (ctxt->context->xptr) {
6183 name = xmlXPathParseNCName(ctxt);
6184 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
6185 xmlFree(name);
6186 SKIP_BLANKS;
6187 if (CUR != '(') {
6188 XP_ERROR(XPATH_EXPR_ERROR);
6189 }
6190 NEXT;
6191 SKIP_BLANKS;
6192
6193 xmlXPtrRangeToFunction(ctxt, 1);
6194 CHECK_ERROR;
6195
6196 SKIP_BLANKS;
6197 if (CUR != ')') {
6198 XP_ERROR(XPATH_EXPR_ERROR);
6199 }
6200 NEXT;
6201 goto eval_predicates;
6202 }
6203 }
6204#endif
6205 if (name == NULL)
6206 name = xmlXPathParseNCName(ctxt);
6207 if (name != NULL) {
6208 axis = xmlXPathIsAxisName(name);
6209 if (axis != 0) {
6210 SKIP_BLANKS;
6211 if ((CUR == ':') && (NXT(1) == ':')) {
6212 SKIP(2);
6213 xmlFree(name);
6214 name = NULL;
6215 } else {
6216 /* an element name can conflict with an axis one :-\ */
6217 axis = AXIS_CHILD;
6218 }
6219 } else {
6220 axis = AXIS_CHILD;
6221 }
6222 } else if (CUR == '@') {
6223 NEXT;
6224 axis = AXIS_ATTRIBUTE;
6225 } else {
6226 axis = AXIS_CHILD;
6227 }
6228
6229 CHECK_ERROR;
6230
6231 name = xmlXPathEvalNodeTest(ctxt, &test, &type, &prefix, name);
6232 if (test == 0)
6233 return;
6234
6235#ifdef DEBUG_STEP
6236 xmlGenericError(xmlGenericErrorContext,
6237 "Basis : computing new set\n");
6238#endif
6239 xmlXPathNodeCollectAndTest(ctxt, axis, test, type, prefix, name);
6240#ifdef DEBUG_STEP
6241 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6242 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6243#endif
6244 if (name != NULL)
6245 xmlFree(name);
6246
6247eval_predicates:
6248 SKIP_BLANKS;
6249 while (CUR == '[') {
6250 xmlXPathEvalPredicate(ctxt);
6251 }
6252 }
6253#ifdef DEBUG_STEP
6254 xmlGenericError(xmlGenericErrorContext, "Step : ");
6255 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6256 ctxt->value->nodesetval);
6257#endif
6258}
6259
6260/**
6261 * xmlXPathEvalRelativeLocationPath:
6262 * @ctxt: the XPath Parser context
6263 *
6264 * [3] RelativeLocationPath ::= Step
6265 * | RelativeLocationPath '/' Step
6266 * | AbbreviatedRelativeLocationPath
6267 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6268 *
6269 */
6270void
6271#ifdef VMS
6272xmlXPathEvalRelLocationPath
6273#else
6274xmlXPathEvalRelativeLocationPath
6275#endif
6276(xmlXPathParserContextPtr ctxt) {
6277 SKIP_BLANKS;
6278 if ((CUR == '/') && (NXT(1) == '/')) {
6279 SKIP(2);
6280 SKIP_BLANKS;
6281 xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
6282 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6283 } else if (CUR == '/') {
6284 NEXT;
6285 SKIP_BLANKS;
6286 }
6287 xmlXPathEvalStep(ctxt);
6288 SKIP_BLANKS;
6289 while (CUR == '/') {
6290 if ((CUR == '/') && (NXT(1) == '/')) {
6291 SKIP(2);
6292 SKIP_BLANKS;
6293 xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
6294 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6295 xmlXPathEvalStep(ctxt);
6296 } else if (CUR == '/') {
6297 NEXT;
6298 SKIP_BLANKS;
6299 xmlXPathEvalStep(ctxt);
6300 }
6301 SKIP_BLANKS;
6302 }
6303}
6304
6305/**
6306 * xmlXPathEvalLocationPath:
6307 * @ctxt: the XPath Parser context
6308 *
6309 * [1] LocationPath ::= RelativeLocationPath
6310 * | AbsoluteLocationPath
6311 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6312 * | AbbreviatedAbsoluteLocationPath
6313 * [10] AbbreviatedAbsoluteLocationPath ::=
6314 * '//' RelativeLocationPath
6315 *
6316 * // is short for /descendant-or-self::node()/. For example,
6317 * //para is short for /descendant-or-self::node()/child::para and
6318 * so will select any para element in the document (even a para element
6319 * that is a document element will be selected by //para since the
6320 * document element node is a child of the root node); div//para is
6321 * short for div/descendant-or-self::node()/child::para and so will
6322 * select all para descendants of div children.
6323 */
6324void
6325xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
6326 SKIP_BLANKS;
6327 if (CUR != '/') {
6328 xmlXPathEvalRelativeLocationPath(ctxt);
6329 } else {
6330 while (CUR == '/') {
6331 if ((CUR == '/') && (NXT(1) == '/')) {
6332 SKIP(2);
6333 SKIP_BLANKS;
6334 xmlXPathNodeCollectAndTest(ctxt,
6335 AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
6336 NODE_TYPE_NODE, NULL, NULL);
6337 xmlXPathEvalRelativeLocationPath(ctxt);
6338 } else if (CUR == '/') {
6339 NEXT;
6340 SKIP_BLANKS;
6341 if (CUR != 0)
6342 xmlXPathEvalRelativeLocationPath(ctxt);
6343 }
6344 }
6345 }
6346}
6347
6348/**
6349 * xmlXPathEval:
6350 * @str: the XPath expression
6351 * @ctx: the XPath context
6352 *
6353 * Evaluate the XPath Location Path in the given context.
6354 *
6355 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
6356 * the caller has to free the object.
6357 */
6358xmlXPathObjectPtr
6359xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
6360 xmlXPathParserContextPtr ctxt;
6361 xmlXPathObjectPtr res, tmp, init = NULL;
6362 int stack = 0;
6363
6364 xmlXPathInit();
6365
6366 CHECK_CONTEXT(ctx)
6367
6368 ctxt = xmlXPathNewParserContext(str, ctx);
6369 /**** TAG:9999
6370 if (ctx->node != NULL) {
6371 init = xmlXPathNewNodeSet(ctx->node);
6372 valuePush(ctxt, init);
6373 }
6374 ****/
6375 xmlXPathEvalExpr(ctxt);
6376
6377 if (ctxt->value == NULL) {
6378 xmlGenericError(xmlGenericErrorContext,
6379 "xmlXPathEval: evaluation failed\n");
6380 res = NULL;
6381 } else if (*ctxt->cur != 0) {
6382 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
6383 res = NULL;
6384 } else {
6385 res = valuePop(ctxt);
6386 }
6387
6388 do {
6389 tmp = valuePop(ctxt);
6390 if (tmp != NULL) {
6391 if (tmp != init)
6392 stack++;
6393 xmlXPathFreeObject(tmp);
6394 }
6395 } while (tmp != NULL);
6396 if ((stack != 0) && (res != NULL)) {
6397 xmlGenericError(xmlGenericErrorContext,
6398 "xmlXPathEval: %d object left on the stack\n",
6399 stack);
6400 }
6401 if (ctxt->error != XPATH_EXPRESSION_OK) {
6402 xmlXPathFreeObject(res);
6403 res = NULL;
6404 }
6405
6406 xmlXPathFreeParserContext(ctxt);
6407 return(res);
6408}
6409
6410/**
6411 * xmlXPathEvalExpression:
6412 * @str: the XPath expression
6413 * @ctxt: the XPath context
6414 *
6415 * Evaluate the XPath expression in the given context.
6416 *
6417 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
6418 * the caller has to free the object.
6419 */
6420xmlXPathObjectPtr
6421xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
6422 xmlXPathParserContextPtr pctxt;
6423 xmlXPathObjectPtr res, tmp;
6424 int stack = 0;
6425
6426 xmlXPathInit();
6427
6428 CHECK_CONTEXT(ctxt)
6429
6430 pctxt = xmlXPathNewParserContext(str, ctxt);
6431 xmlXPathEvalExpr(pctxt);
6432
6433 if (*pctxt->cur != 0) {
6434 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
6435 res = NULL;
6436 } else {
6437 res = valuePop(pctxt);
6438 }
6439 do {
6440 tmp = valuePop(pctxt);
6441 if (tmp != NULL) {
6442 xmlXPathFreeObject(tmp);
6443 stack++;
6444 }
6445 } while (tmp != NULL);
6446 if ((stack != 0) && (res != NULL)) {
6447 xmlGenericError(xmlGenericErrorContext,
6448 "xmlXPathEvalExpression: %d object left on the stack\n",
6449 stack);
6450 }
6451 xmlXPathFreeParserContext(pctxt);
6452 return(res);
6453}
6454
6455/**
6456 * xmlXPathRegisterAllFunctions:
6457 * @ctxt: the XPath context
6458 *
6459 * Registers all default XPath functions in this context
6460 */
6461void
6462xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
6463{
6464 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
6465 xmlXPathBooleanFunction);
6466 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
6467 xmlXPathCeilingFunction);
6468 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
6469 xmlXPathCountFunction);
6470 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
6471 xmlXPathConcatFunction);
6472 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
6473 xmlXPathContainsFunction);
6474 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
6475 xmlXPathIdFunction);
6476 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
6477 xmlXPathFalseFunction);
6478 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
6479 xmlXPathFloorFunction);
6480 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
6481 xmlXPathLastFunction);
6482 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
6483 xmlXPathLangFunction);
6484 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
6485 xmlXPathLocalNameFunction);
6486 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
6487 xmlXPathNotFunction);
6488 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
6489 xmlXPathNameFunction);
6490 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
6491 xmlXPathNamespaceURIFunction);
6492 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
6493 xmlXPathNormalizeFunction);
6494 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
6495 xmlXPathNumberFunction);
6496 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
6497 xmlXPathPositionFunction);
6498 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
6499 xmlXPathRoundFunction);
6500 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
6501 xmlXPathStringFunction);
6502 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
6503 xmlXPathStringLengthFunction);
6504 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
6505 xmlXPathStartsWithFunction);
6506 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
6507 xmlXPathSubstringFunction);
6508 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
6509 xmlXPathSubstringBeforeFunction);
6510 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
6511 xmlXPathSubstringAfterFunction);
6512 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
6513 xmlXPathSumFunction);
6514 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
6515 xmlXPathTrueFunction);
6516 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
6517 xmlXPathTranslateFunction);
6518}
6519
6520#endif /* LIBXML_XPATH_ENABLED */