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