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