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