blob: 9d1ba1ba263225da01482a230a522bf6851ab577 [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) {
2574 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict, arg1, arg2);
2575 } else {
Daniel Veillardbbd51d52001-02-24 03:07:03 +00002576 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, !strict, arg2, arg1);
Owen Taylor3473f882001-02-23 17:55:21 +00002577 }
2578 }
2579 return(ret);
2580 }
2581
2582 if (arg1->type != XPATH_NUMBER) {
2583 valuePush(ctxt, arg1);
2584 xmlXPathNumberFunction(ctxt, 1);
2585 arg1 = valuePop(ctxt);
2586 }
2587 if (arg1->type != XPATH_NUMBER) {
2588 xmlXPathFreeObject(arg1);
2589 xmlXPathFreeObject(arg2);
2590 XP_ERROR0(XPATH_INVALID_OPERAND);
2591 }
2592 if (arg2->type != XPATH_NUMBER) {
2593 valuePush(ctxt, arg2);
2594 xmlXPathNumberFunction(ctxt, 1);
2595 arg2 = valuePop(ctxt);
2596 }
2597 if (arg2->type != XPATH_NUMBER) {
2598 xmlXPathFreeObject(arg1);
2599 xmlXPathFreeObject(arg2);
2600 XP_ERROR0(XPATH_INVALID_OPERAND);
2601 }
2602 /*
2603 * Add tests for infinity and nan
2604 * => feedback on 3.4 for Inf and NaN
2605 */
2606 if (inf && strict)
2607 ret = (arg1->floatval < arg2->floatval);
2608 else 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 xmlXPathFreeObject(arg1);
2615 xmlXPathFreeObject(arg2);
2616 return(ret);
2617}
2618
2619/**
2620 * xmlXPathValueFlipSign:
2621 * @ctxt: the XPath Parser context
2622 *
2623 * Implement the unary - operation on an XPath object
2624 * The numeric operators convert their operands to numbers as if
2625 * by calling the number function.
2626 */
2627void
2628xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
2629 xmlXPathObjectPtr arg;
2630
2631 POP_FLOAT
2632 arg->floatval = -arg->floatval;
2633 valuePush(ctxt, arg);
2634}
2635
2636/**
2637 * xmlXPathAddValues:
2638 * @ctxt: the XPath Parser context
2639 *
2640 * Implement the add operation on XPath objects:
2641 * The numeric operators convert their operands to numbers as if
2642 * by calling the number function.
2643 */
2644void
2645xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
2646 xmlXPathObjectPtr arg;
2647 double val;
2648
2649 POP_FLOAT
2650 val = arg->floatval;
2651 xmlXPathFreeObject(arg);
2652
2653 POP_FLOAT
2654 arg->floatval += val;
2655 valuePush(ctxt, arg);
2656}
2657
2658/**
2659 * xmlXPathSubValues:
2660 * @ctxt: the XPath Parser context
2661 *
2662 * Implement the substraction operation on XPath objects:
2663 * The numeric operators convert their operands to numbers as if
2664 * by calling the number function.
2665 */
2666void
2667xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
2668 xmlXPathObjectPtr arg;
2669 double val;
2670
2671 POP_FLOAT
2672 val = arg->floatval;
2673 xmlXPathFreeObject(arg);
2674
2675 POP_FLOAT
2676 arg->floatval -= val;
2677 valuePush(ctxt, arg);
2678}
2679
2680/**
2681 * xmlXPathMultValues:
2682 * @ctxt: the XPath Parser context
2683 *
2684 * Implement the multiply operation on XPath objects:
2685 * The numeric operators convert their operands to numbers as if
2686 * by calling the number function.
2687 */
2688void
2689xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
2690 xmlXPathObjectPtr arg;
2691 double val;
2692
2693 POP_FLOAT
2694 val = arg->floatval;
2695 xmlXPathFreeObject(arg);
2696
2697 POP_FLOAT
2698 arg->floatval *= val;
2699 valuePush(ctxt, arg);
2700}
2701
2702/**
2703 * xmlXPathDivValues:
2704 * @ctxt: the XPath Parser context
2705 *
2706 * Implement the div operation on XPath objects @arg1 / @arg2:
2707 * The numeric operators convert their operands to numbers as if
2708 * by calling the number function.
2709 */
2710void
2711xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
2712 xmlXPathObjectPtr arg;
2713 double val;
2714
2715 POP_FLOAT
2716 val = arg->floatval;
2717 xmlXPathFreeObject(arg);
2718
2719 POP_FLOAT
2720 arg->floatval /= val;
2721 valuePush(ctxt, arg);
2722}
2723
2724/**
2725 * xmlXPathModValues:
2726 * @ctxt: the XPath Parser context
2727 *
2728 * Implement the mod operation on XPath objects: @arg1 / @arg2
2729 * The numeric operators convert their operands to numbers as if
2730 * by calling the number function.
2731 */
2732void
2733xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
2734 xmlXPathObjectPtr arg;
2735 int arg1, arg2;
2736
2737 POP_FLOAT
2738 arg2 = (int) arg->floatval;
2739 xmlXPathFreeObject(arg);
2740
2741 POP_FLOAT
2742 arg1 = (int) arg->floatval;
2743 arg->floatval = arg1 % arg2;
2744 valuePush(ctxt, arg);
2745}
2746
2747/************************************************************************
2748 * *
2749 * The traversal functions *
2750 * *
2751 ************************************************************************/
2752
2753typedef enum {
2754 AXIS_ANCESTOR = 1,
2755 AXIS_ANCESTOR_OR_SELF,
2756 AXIS_ATTRIBUTE,
2757 AXIS_CHILD,
2758 AXIS_DESCENDANT,
2759 AXIS_DESCENDANT_OR_SELF,
2760 AXIS_FOLLOWING,
2761 AXIS_FOLLOWING_SIBLING,
2762 AXIS_NAMESPACE,
2763 AXIS_PARENT,
2764 AXIS_PRECEDING,
2765 AXIS_PRECEDING_SIBLING,
2766 AXIS_SELF
2767} xmlXPathAxisVal;
2768
2769/*
2770 * A traversal function enumerates nodes along an axis.
2771 * Initially it must be called with NULL, and it indicates
2772 * termination on the axis by returning NULL.
2773 */
2774typedef xmlNodePtr (*xmlXPathTraversalFunction)
2775 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
2776
2777/**
2778 * xmlXPathNextSelf:
2779 * @ctxt: the XPath Parser context
2780 * @cur: the current node in the traversal
2781 *
2782 * Traversal function for the "self" direction
2783 * The self axis contains just the context node itself
2784 *
2785 * Returns the next element following that axis
2786 */
2787xmlNodePtr
2788xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2789 if (cur == NULL)
2790 return(ctxt->context->node);
2791 return(NULL);
2792}
2793
2794/**
2795 * xmlXPathNextChild:
2796 * @ctxt: the XPath Parser context
2797 * @cur: the current node in the traversal
2798 *
2799 * Traversal function for the "child" direction
2800 * The child axis contains the children of the context node in document order.
2801 *
2802 * Returns the next element following that axis
2803 */
2804xmlNodePtr
2805xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2806 if (cur == NULL) {
2807 if (ctxt->context->node == NULL) return(NULL);
2808 switch (ctxt->context->node->type) {
2809 case XML_ELEMENT_NODE:
2810 case XML_TEXT_NODE:
2811 case XML_CDATA_SECTION_NODE:
2812 case XML_ENTITY_REF_NODE:
2813 case XML_ENTITY_NODE:
2814 case XML_PI_NODE:
2815 case XML_COMMENT_NODE:
2816 case XML_NOTATION_NODE:
2817 case XML_DTD_NODE:
2818 return(ctxt->context->node->children);
2819 case XML_DOCUMENT_NODE:
2820 case XML_DOCUMENT_TYPE_NODE:
2821 case XML_DOCUMENT_FRAG_NODE:
2822 case XML_HTML_DOCUMENT_NODE:
2823#ifdef LIBXML_SGML_ENABLED
2824 case XML_SGML_DOCUMENT_NODE:
2825#endif
2826 return(((xmlDocPtr) ctxt->context->node)->children);
2827 case XML_ELEMENT_DECL:
2828 case XML_ATTRIBUTE_DECL:
2829 case XML_ENTITY_DECL:
2830 case XML_ATTRIBUTE_NODE:
2831 case XML_NAMESPACE_DECL:
2832 case XML_XINCLUDE_START:
2833 case XML_XINCLUDE_END:
2834 return(NULL);
2835 }
2836 return(NULL);
2837 }
2838 if ((cur->type == XML_DOCUMENT_NODE) ||
2839 (cur->type == XML_HTML_DOCUMENT_NODE))
2840 return(NULL);
2841 return(cur->next);
2842}
2843
2844/**
2845 * xmlXPathNextDescendant:
2846 * @ctxt: the XPath Parser context
2847 * @cur: the current node in the traversal
2848 *
2849 * Traversal function for the "descendant" direction
2850 * the descendant axis contains the descendants of the context node in document
2851 * order; a descendant is a child or a child of a child and so on.
2852 *
2853 * Returns the next element following that axis
2854 */
2855xmlNodePtr
2856xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2857 if (cur == NULL) {
2858 if (ctxt->context->node == NULL)
2859 return(NULL);
2860 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
2861 (ctxt->context->node->type == XML_NAMESPACE_DECL))
2862 return(NULL);
2863
2864 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
2865 return(ctxt->context->doc->children);
2866 return(ctxt->context->node->children);
2867 }
2868
2869 if (cur->children != NULL)
2870 {
2871 if (cur->children->type != XML_ENTITY_DECL)
2872 return(cur->children);
2873 }
2874 if (cur->next != NULL) return(cur->next);
2875
2876 do {
2877 cur = cur->parent;
2878 if (cur == NULL) return(NULL);
2879 if (cur == ctxt->context->node) return(NULL);
2880 if (cur->next != NULL) {
2881 cur = cur->next;
2882 return(cur);
2883 }
2884 } while (cur != NULL);
2885 return(cur);
2886}
2887
2888/**
2889 * xmlXPathNextDescendantOrSelf:
2890 * @ctxt: the XPath Parser context
2891 * @cur: the current node in the traversal
2892 *
2893 * Traversal function for the "descendant-or-self" direction
2894 * the descendant-or-self axis contains the context node and the descendants
2895 * of the context node in document order; thus the context node is the first
2896 * node on the axis, and the first child of the context node is the second node
2897 * on the axis
2898 *
2899 * Returns the next element following that axis
2900 */
2901xmlNodePtr
2902xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2903 if (cur == NULL) {
2904 if (ctxt->context->node == NULL)
2905 return(NULL);
2906 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
2907 (ctxt->context->node->type == XML_NAMESPACE_DECL))
2908 return(NULL);
2909 return(ctxt->context->node);
2910 }
2911
2912 return(xmlXPathNextDescendant(ctxt, cur));
2913}
2914
2915/**
2916 * xmlXPathNextParent:
2917 * @ctxt: the XPath Parser context
2918 * @cur: the current node in the traversal
2919 *
2920 * Traversal function for the "parent" direction
2921 * The parent axis contains the parent of the context node, if there is one.
2922 *
2923 * Returns the next element following that axis
2924 */
2925xmlNodePtr
2926xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2927 /*
2928 * the parent of an attribute or namespace node is the element
2929 * to which the attribute or namespace node is attached
2930 * Namespace handling !!!
2931 */
2932 if (cur == NULL) {
2933 if (ctxt->context->node == NULL) return(NULL);
2934 switch (ctxt->context->node->type) {
2935 case XML_ELEMENT_NODE:
2936 case XML_TEXT_NODE:
2937 case XML_CDATA_SECTION_NODE:
2938 case XML_ENTITY_REF_NODE:
2939 case XML_ENTITY_NODE:
2940 case XML_PI_NODE:
2941 case XML_COMMENT_NODE:
2942 case XML_NOTATION_NODE:
2943 case XML_DTD_NODE:
2944 case XML_ELEMENT_DECL:
2945 case XML_ATTRIBUTE_DECL:
2946 case XML_XINCLUDE_START:
2947 case XML_XINCLUDE_END:
2948 case XML_ENTITY_DECL:
2949 if (ctxt->context->node->parent == NULL)
2950 return((xmlNodePtr) ctxt->context->doc);
2951 return(ctxt->context->node->parent);
2952 case XML_ATTRIBUTE_NODE: {
2953 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
2954
2955 return(att->parent);
2956 }
2957 case XML_DOCUMENT_NODE:
2958 case XML_DOCUMENT_TYPE_NODE:
2959 case XML_DOCUMENT_FRAG_NODE:
2960 case XML_HTML_DOCUMENT_NODE:
2961#ifdef LIBXML_SGML_ENABLED
2962 case XML_SGML_DOCUMENT_NODE:
2963#endif
2964 return(NULL);
2965 case XML_NAMESPACE_DECL:
2966 /*
2967 * TODO !!! may require extending struct _xmlNs with
2968 * parent field
2969 * C.f. Infoset case...
2970 */
2971 return(NULL);
2972 }
2973 }
2974 return(NULL);
2975}
2976
2977/**
2978 * xmlXPathNextAncestor:
2979 * @ctxt: the XPath Parser context
2980 * @cur: the current node in the traversal
2981 *
2982 * Traversal function for the "ancestor" direction
2983 * the ancestor axis contains the ancestors of the context node; the ancestors
2984 * of the context node consist of the parent of context node and the parent's
2985 * parent and so on; the nodes are ordered in reverse document order; thus the
2986 * parent is the first node on the axis, and the parent's parent is the second
2987 * node on the axis
2988 *
2989 * Returns the next element following that axis
2990 */
2991xmlNodePtr
2992xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
2993 /*
2994 * the parent of an attribute or namespace node is the element
2995 * to which the attribute or namespace node is attached
2996 * !!!!!!!!!!!!!
2997 */
2998 if (cur == NULL) {
2999 if (ctxt->context->node == NULL) return(NULL);
3000 switch (ctxt->context->node->type) {
3001 case XML_ELEMENT_NODE:
3002 case XML_TEXT_NODE:
3003 case XML_CDATA_SECTION_NODE:
3004 case XML_ENTITY_REF_NODE:
3005 case XML_ENTITY_NODE:
3006 case XML_PI_NODE:
3007 case XML_COMMENT_NODE:
3008 case XML_DTD_NODE:
3009 case XML_ELEMENT_DECL:
3010 case XML_ATTRIBUTE_DECL:
3011 case XML_ENTITY_DECL:
3012 case XML_NOTATION_NODE:
3013 case XML_XINCLUDE_START:
3014 case XML_XINCLUDE_END:
3015 if (ctxt->context->node->parent == NULL)
3016 return((xmlNodePtr) ctxt->context->doc);
3017 return(ctxt->context->node->parent);
3018 case XML_ATTRIBUTE_NODE: {
3019 xmlAttrPtr cur = (xmlAttrPtr) ctxt->context->node;
3020
3021 return(cur->parent);
3022 }
3023 case XML_DOCUMENT_NODE:
3024 case XML_DOCUMENT_TYPE_NODE:
3025 case XML_DOCUMENT_FRAG_NODE:
3026 case XML_HTML_DOCUMENT_NODE:
3027#ifdef LIBXML_SGML_ENABLED
3028 case XML_SGML_DOCUMENT_NODE:
3029#endif
3030 return(NULL);
3031 case XML_NAMESPACE_DECL:
3032 /*
3033 * TODO !!! may require extending struct _xmlNs with
3034 * parent field
3035 * C.f. Infoset case...
3036 */
3037 return(NULL);
3038 }
3039 return(NULL);
3040 }
3041 if (cur == ctxt->context->doc->children)
3042 return((xmlNodePtr) ctxt->context->doc);
3043 if (cur == (xmlNodePtr) ctxt->context->doc)
3044 return(NULL);
3045 switch (cur->type) {
3046 case XML_ELEMENT_NODE:
3047 case XML_TEXT_NODE:
3048 case XML_CDATA_SECTION_NODE:
3049 case XML_ENTITY_REF_NODE:
3050 case XML_ENTITY_NODE:
3051 case XML_PI_NODE:
3052 case XML_COMMENT_NODE:
3053 case XML_NOTATION_NODE:
3054 case XML_DTD_NODE:
3055 case XML_ELEMENT_DECL:
3056 case XML_ATTRIBUTE_DECL:
3057 case XML_ENTITY_DECL:
3058 case XML_XINCLUDE_START:
3059 case XML_XINCLUDE_END:
3060 return(cur->parent);
3061 case XML_ATTRIBUTE_NODE: {
3062 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
3063
3064 return(att->parent);
3065 }
3066 case XML_DOCUMENT_NODE:
3067 case XML_DOCUMENT_TYPE_NODE:
3068 case XML_DOCUMENT_FRAG_NODE:
3069 case XML_HTML_DOCUMENT_NODE:
3070#ifdef LIBXML_SGML_ENABLED
3071 case XML_SGML_DOCUMENT_NODE:
3072#endif
3073 return(NULL);
3074 case XML_NAMESPACE_DECL:
3075 /*
3076 * TODO !!! may require extending struct _xmlNs with
3077 * parent field
3078 * C.f. Infoset case...
3079 */
3080 return(NULL);
3081 }
3082 return(NULL);
3083}
3084
3085/**
3086 * xmlXPathNextAncestorOrSelf:
3087 * @ctxt: the XPath Parser context
3088 * @cur: the current node in the traversal
3089 *
3090 * Traversal function for the "ancestor-or-self" direction
3091 * he ancestor-or-self axis contains the context node and ancestors of
3092 * the context node in reverse document order; thus the context node is
3093 * the first node on the axis, and the context node's parent the second;
3094 * parent here is defined the same as with the parent axis.
3095 *
3096 * Returns the next element following that axis
3097 */
3098xmlNodePtr
3099xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3100 if (cur == NULL)
3101 return(ctxt->context->node);
3102 return(xmlXPathNextAncestor(ctxt, cur));
3103}
3104
3105/**
3106 * xmlXPathNextFollowingSibling:
3107 * @ctxt: the XPath Parser context
3108 * @cur: the current node in the traversal
3109 *
3110 * Traversal function for the "following-sibling" direction
3111 * The following-sibling axis contains the following siblings of the context
3112 * node in document order.
3113 *
3114 * Returns the next element following that axis
3115 */
3116xmlNodePtr
3117xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3118 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3119 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3120 return(NULL);
3121 if (cur == (xmlNodePtr) ctxt->context->doc)
3122 return(NULL);
3123 if (cur == NULL)
3124 return(ctxt->context->node->next);
3125 return(cur->next);
3126}
3127
3128/**
3129 * xmlXPathNextPrecedingSibling:
3130 * @ctxt: the XPath Parser context
3131 * @cur: the current node in the traversal
3132 *
3133 * Traversal function for the "preceding-sibling" direction
3134 * The preceding-sibling axis contains the preceding siblings of the context
3135 * node in reverse document order; the first preceding sibling is first on the
3136 * axis; the sibling preceding that node is the second on the axis and so on.
3137 *
3138 * Returns the next element following that axis
3139 */
3140xmlNodePtr
3141xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3142 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
3143 (ctxt->context->node->type == XML_NAMESPACE_DECL))
3144 return(NULL);
3145 if (cur == (xmlNodePtr) ctxt->context->doc)
3146 return(NULL);
3147 if (cur == NULL)
3148 return(ctxt->context->node->prev);
3149 return(cur->prev);
3150}
3151
3152/**
3153 * xmlXPathNextFollowing:
3154 * @ctxt: the XPath Parser context
3155 * @cur: the current node in the traversal
3156 *
3157 * Traversal function for the "following" direction
3158 * The following axis contains all nodes in the same document as the context
3159 * node that are after the context node in document order, excluding any
3160 * descendants and excluding attribute nodes and namespace nodes; the nodes
3161 * are ordered in document order
3162 *
3163 * Returns the next element following that axis
3164 */
3165xmlNodePtr
3166xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3167 if (cur != NULL && cur->children != NULL)
3168 return cur->children ;
3169 if (cur == NULL) cur = ctxt->context->node;
3170 if (cur == NULL) return(NULL) ; /* ERROR */
3171 if (cur->next != NULL) return(cur->next) ;
3172 do {
3173 cur = cur->parent;
3174 if (cur == NULL) return(NULL);
3175 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
3176 if (cur->next != NULL) return(cur->next);
3177 } while (cur != NULL);
3178 return(cur);
3179}
3180
3181/*
3182 * xmlXPathIsAncestor:
3183 * @ancestor: the ancestor node
3184 * @node: the current node
3185 *
3186 * Check that @ancestor is a @node's ancestor
3187 *
3188 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
3189 */
3190static int
3191xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
3192 if ((ancestor == NULL) || (node == NULL)) return(0);
3193 /* nodes need to be in the same document */
3194 if (ancestor->doc != node->doc) return(0);
3195 /* avoid searching if ancestor or node is the root node */
3196 if (ancestor == (xmlNodePtr) node->doc) return(1);
3197 if (node == (xmlNodePtr) ancestor->doc) return(0);
3198 while (node->parent != NULL) {
3199 if (node->parent == ancestor)
3200 return(1);
3201 node = node->parent;
3202 }
3203 return(0);
3204}
3205
3206/**
3207 * xmlXPathNextPreceding:
3208 * @ctxt: the XPath Parser context
3209 * @cur: the current node in the traversal
3210 *
3211 * Traversal function for the "preceding" direction
3212 * the preceding axis contains all nodes in the same document as the context
3213 * node that are before the context node in document order, excluding any
3214 * ancestors and excluding attribute nodes and namespace nodes; the nodes are
3215 * ordered in reverse document order
3216 *
3217 * Returns the next element following that axis
3218 */
3219xmlNodePtr
3220xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3221 if (cur == NULL)
3222 cur = ctxt->context->node ;
3223 do {
3224 if (cur->prev != NULL) {
3225 for (cur = cur->prev ; cur->last != NULL ; cur = cur->last)
3226 ;
3227 return(cur) ;
3228 }
3229
3230 cur = cur->parent;
3231 if (cur == NULL) return(NULL);
3232 if (cur == ctxt->context->doc->children) return(NULL);
3233 } while (xmlXPathIsAncestor(cur, ctxt->context->node));
3234 return(cur);
3235}
3236
3237/**
3238 * xmlXPathNextNamespace:
3239 * @ctxt: the XPath Parser context
3240 * @cur: the current attribute in the traversal
3241 *
3242 * Traversal function for the "namespace" direction
3243 * the namespace axis contains the namespace nodes of the context node;
3244 * the order of nodes on this axis is implementation-defined; the axis will
3245 * be empty unless the context node is an element
3246 *
3247 * Returns the next element following that axis
3248 */
3249xmlNodePtr
3250xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3251 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3252 if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
3253 if (ctxt->context->namespaces != NULL)
3254 xmlFree(ctxt->context->namespaces);
3255 ctxt->context->namespaces =
3256 xmlGetNsList(ctxt->context->doc, ctxt->context->node);
3257 if (ctxt->context->namespaces == NULL) return(NULL);
3258 ctxt->context->nsNr = 0;
3259 }
3260 return((xmlNodePtr)ctxt->context->namespaces[ctxt->context->nsNr++]);
3261}
3262
3263/**
3264 * xmlXPathNextAttribute:
3265 * @ctxt: the XPath Parser context
3266 * @cur: the current attribute in the traversal
3267 *
3268 * Traversal function for the "attribute" direction
3269 * TODO: support DTD inherited default attributes
3270 *
3271 * Returns the next element following that axis
3272 */
3273xmlNodePtr
3274xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
3275 if (ctxt->context->node->type != XML_ELEMENT_NODE) return(NULL);
3276 if (cur == NULL) {
3277 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
3278 return(NULL);
3279 return((xmlNodePtr)ctxt->context->node->properties);
3280 }
3281 return((xmlNodePtr)cur->next);
3282}
3283
3284/************************************************************************
3285 * *
3286 * NodeTest Functions *
3287 * *
3288 ************************************************************************/
3289
3290typedef enum {
3291 NODE_TEST_NONE = 0,
3292 NODE_TEST_TYPE = 1,
3293 NODE_TEST_PI = 2,
3294 NODE_TEST_ALL = 3,
3295 NODE_TEST_NS = 4,
3296 NODE_TEST_NAME = 5
3297} xmlXPathTestVal;
3298
3299typedef enum {
3300 NODE_TYPE_NODE = 0,
3301 NODE_TYPE_COMMENT = XML_COMMENT_NODE,
3302 NODE_TYPE_TEXT = XML_TEXT_NODE,
3303 NODE_TYPE_PI = XML_PI_NODE
3304} xmlXPathTypeVal;
3305
3306#define IS_FUNCTION 200
3307
3308/**
3309 * xmlXPathNodeCollectAndTest:
3310 * @ctxt: the XPath Parser context
3311 * @axis: the XPath axis
3312 * @test: the XPath test
3313 * @type: the XPath type
3314 * @prefix: the namesapce prefix if any
3315 * @name: the name used in the search if any
3316 *
3317 * This is the function implementing a step: based on the current list
3318 * of nodes, it builds up a new list, looking at all nodes under that
3319 * axis and selecting them.
3320 *
3321 * Returns the new NodeSet resulting from the search.
3322 */
3323void
3324xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis,
3325 xmlXPathTestVal test, xmlXPathTypeVal type,
3326 const xmlChar *prefix, const xmlChar *name) {
3327#ifdef DEBUG_STEP
3328 int n = 0, t = 0;
3329#endif
3330 int i;
3331 xmlNodeSetPtr ret;
3332 xmlXPathTraversalFunction next = NULL;
3333 void (*addNode)(xmlNodeSetPtr, xmlNodePtr);
3334 xmlNodePtr cur = NULL;
3335 xmlXPathObjectPtr obj;
3336 xmlNodeSetPtr nodelist;
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003337 xmlNodePtr tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003338
3339 CHECK_TYPE(XPATH_NODESET);
3340 obj = valuePop(ctxt);
3341 addNode = xmlXPathNodeSetAdd;
3342
3343#ifdef DEBUG_STEP
3344 xmlGenericError(xmlGenericErrorContext,
3345 "new step : ");
3346#endif
3347 switch (axis) {
3348 case AXIS_ANCESTOR:
3349#ifdef DEBUG_STEP
3350 xmlGenericError(xmlGenericErrorContext,
3351 "axis 'ancestors' ");
3352#endif
3353 next = xmlXPathNextAncestor; break;
3354 case AXIS_ANCESTOR_OR_SELF:
3355#ifdef DEBUG_STEP
3356 xmlGenericError(xmlGenericErrorContext,
3357 "axis 'ancestors-or-self' ");
3358#endif
3359 next = xmlXPathNextAncestorOrSelf; break;
3360 case AXIS_ATTRIBUTE:
3361#ifdef DEBUG_STEP
3362 xmlGenericError(xmlGenericErrorContext,
3363 "axis 'attributes' ");
3364#endif
3365 next = xmlXPathNextAttribute; break;
3366 break;
3367 case AXIS_CHILD:
3368#ifdef DEBUG_STEP
3369 xmlGenericError(xmlGenericErrorContext,
3370 "axis 'child' ");
3371#endif
3372 next = xmlXPathNextChild; break;
3373 case AXIS_DESCENDANT:
3374#ifdef DEBUG_STEP
3375 xmlGenericError(xmlGenericErrorContext,
3376 "axis 'descendant' ");
3377#endif
3378 next = xmlXPathNextDescendant; break;
3379 case AXIS_DESCENDANT_OR_SELF:
3380#ifdef DEBUG_STEP
3381 xmlGenericError(xmlGenericErrorContext,
3382 "axis 'descendant-or-self' ");
3383#endif
3384 next = xmlXPathNextDescendantOrSelf; break;
3385 case AXIS_FOLLOWING:
3386#ifdef DEBUG_STEP
3387 xmlGenericError(xmlGenericErrorContext,
3388 "axis 'following' ");
3389#endif
3390 next = xmlXPathNextFollowing; break;
3391 case AXIS_FOLLOWING_SIBLING:
3392#ifdef DEBUG_STEP
3393 xmlGenericError(xmlGenericErrorContext,
3394 "axis 'following-siblings' ");
3395#endif
3396 next = xmlXPathNextFollowingSibling; break;
3397 case AXIS_NAMESPACE:
3398#ifdef DEBUG_STEP
3399 xmlGenericError(xmlGenericErrorContext,
3400 "axis 'namespace' ");
3401#endif
3402 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
3403 break;
3404 case AXIS_PARENT:
3405#ifdef DEBUG_STEP
3406 xmlGenericError(xmlGenericErrorContext,
3407 "axis 'parent' ");
3408#endif
3409 next = xmlXPathNextParent; break;
3410 case AXIS_PRECEDING:
3411#ifdef DEBUG_STEP
3412 xmlGenericError(xmlGenericErrorContext,
3413 "axis 'preceding' ");
3414#endif
3415 next = xmlXPathNextPreceding; break;
3416 case AXIS_PRECEDING_SIBLING:
3417#ifdef DEBUG_STEP
3418 xmlGenericError(xmlGenericErrorContext,
3419 "axis 'preceding-sibling' ");
3420#endif
3421 next = xmlXPathNextPrecedingSibling; break;
3422 case AXIS_SELF:
3423#ifdef DEBUG_STEP
3424 xmlGenericError(xmlGenericErrorContext,
3425 "axis 'self' ");
3426#endif
3427 next = xmlXPathNextSelf; break;
3428 }
3429 if (next == NULL)
3430 return;
3431
3432 nodelist = obj->nodesetval;
3433 if ((nodelist != NULL) &&
3434 (nodelist->nodeNr <= 1))
3435 addNode = xmlXPathNodeSetAddUnique;
3436 else
3437 addNode = xmlXPathNodeSetAdd;
3438 ret = xmlXPathNodeSetCreate(NULL);
3439#ifdef DEBUG_STEP
3440 xmlGenericError(xmlGenericErrorContext,
3441 " context contains %d nodes\n",
3442 nodelist->nodeNr);
3443 switch (test) {
3444 case NODE_TEST_NODE:
3445 xmlGenericError(xmlGenericErrorContext,
3446 " searching all nodes\n");
3447 break;
3448 case NODE_TEST_NONE:
3449 xmlGenericError(xmlGenericErrorContext,
3450 " searching for none !!!\n");
3451 break;
3452 case NODE_TEST_TYPE:
3453 xmlGenericError(xmlGenericErrorContext,
3454 " searching for type %d\n", type);
3455 break;
3456 case NODE_TEST_PI:
3457 xmlGenericError(xmlGenericErrorContext,
3458 " searching for PI !!!\n");
3459 break;
3460 case NODE_TEST_ALL:
3461 xmlGenericError(xmlGenericErrorContext,
3462 " searching for *\n");
3463 break;
3464 case NODE_TEST_NS:
3465 xmlGenericError(xmlGenericErrorContext,
3466 " searching for namespace %s\n",
3467 prefix);
3468 break;
3469 case NODE_TEST_NAME:
3470 xmlGenericError(xmlGenericErrorContext,
3471 " searching for name %s\n", name);
3472 if (prefix != NULL)
3473 xmlGenericError(xmlGenericErrorContext,
3474 " with namespace %s\n",
3475 prefix);
3476 break;
3477 }
3478 xmlGenericError(xmlGenericErrorContext, "Testing : ");
3479#endif
3480 /*
3481 * 2.3 Node Tests
3482 * - For the attribute axis, the principal node type is attribute.
3483 * - For the namespace axis, the principal node type is namespace.
3484 * - For other axes, the principal node type is element.
3485 *
3486 * A node test * is true for any node of the
3487 * principal node type. For example, child::* willi
3488 * select all element children of the context node
3489 */
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003490 tmp = ctxt->context->node;
Owen Taylor3473f882001-02-23 17:55:21 +00003491 for (i = 0;i < nodelist->nodeNr; i++) {
3492 ctxt->context->node = nodelist->nodeTab[i];
3493
3494 cur = NULL;
3495 do {
3496 cur = next(ctxt, cur);
3497 if (cur == NULL) break;
3498#ifdef DEBUG_STEP
3499 t++;
3500 xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
3501#endif
3502 switch (test) {
3503 case NODE_TEST_NONE:
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003504 ctxt->context->node = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003505 STRANGE
3506 return;
3507 case NODE_TEST_TYPE:
3508 if ((cur->type == type) ||
3509 ((type == NODE_TYPE_NODE) &&
3510 ((cur->type == XML_DOCUMENT_NODE) ||
3511 (cur->type == XML_HTML_DOCUMENT_NODE) ||
3512 (cur->type == XML_ELEMENT_NODE) ||
3513 (cur->type == XML_PI_NODE) ||
3514 (cur->type == XML_COMMENT_NODE) ||
3515 (cur->type == XML_CDATA_SECTION_NODE) ||
3516 (cur->type == XML_TEXT_NODE)))) {
3517#ifdef DEBUG_STEP
3518 n++;
3519#endif
3520 addNode(ret, cur);
3521 }
3522 break;
3523 case NODE_TEST_PI:
3524 if (cur->type == XML_PI_NODE) {
3525 if ((name != NULL) &&
3526 (!xmlStrEqual(name, cur->name)))
3527 break;
3528#ifdef DEBUG_STEP
3529 n++;
3530#endif
3531 addNode(ret, cur);
3532 }
3533 break;
3534 case NODE_TEST_ALL:
3535 if (axis == AXIS_ATTRIBUTE) {
3536 if (cur->type == XML_ATTRIBUTE_NODE) {
3537#ifdef DEBUG_STEP
3538 n++;
3539#endif
3540 addNode(ret, cur);
3541 }
3542 } else if (axis == AXIS_NAMESPACE) {
3543 if (cur->type == XML_NAMESPACE_DECL) {
3544#ifdef DEBUG_STEP
3545 n++;
3546#endif
3547 addNode(ret, cur);
3548 }
3549 } else {
3550 if ((cur->type == XML_ELEMENT_NODE) ||
3551 (cur->type == XML_DOCUMENT_NODE) ||
3552 (cur->type == XML_HTML_DOCUMENT_NODE)) {
3553 if (prefix == NULL) {
3554#ifdef DEBUG_STEP
3555 n++;
3556#endif
3557 addNode(ret, cur);
3558 } else if ((cur->ns != NULL) &&
3559 (xmlStrEqual(prefix,
3560 cur->ns->href))) {
3561#ifdef DEBUG_STEP
3562 n++;
3563#endif
3564 addNode(ret, cur);
3565 }
3566 }
3567 }
3568 break;
3569 case NODE_TEST_NS: {
3570 TODO;
3571 break;
3572 }
3573 case NODE_TEST_NAME:
3574 switch (cur->type) {
3575 case XML_ELEMENT_NODE:
3576 if (xmlStrEqual(name, cur->name)) {
3577 if (prefix == NULL) {
3578 if ((cur->ns == NULL) ||
3579 (cur->ns->prefix == NULL)) {
3580#ifdef DEBUG_STEP
3581 n++;
3582#endif
3583 addNode(ret, cur);
3584 }
3585 } else {
3586 if ((cur->ns != NULL) &&
3587 (xmlStrEqual(prefix,
3588 cur->ns->href))) {
3589#ifdef DEBUG_STEP
3590 n++;
3591#endif
3592 addNode(ret, cur);
3593 }
3594 }
3595 }
3596 break;
3597 case XML_ATTRIBUTE_NODE: {
3598 xmlAttrPtr attr = (xmlAttrPtr) cur;
3599 if (xmlStrEqual(name, attr->name)) {
3600 if (prefix == NULL) {
3601 if ((attr->ns == NULL) ||
3602 (attr->ns->prefix == NULL)) {
3603#ifdef DEBUG_STEP
3604 n++;
3605#endif
3606 addNode(ret, (xmlNodePtr) attr);
3607 }
3608 } else {
3609 if ((attr->ns != NULL) &&
3610 (xmlStrEqual(prefix,
3611 attr->ns->href))) {
3612#ifdef DEBUG_STEP
3613 n++;
3614#endif
3615 addNode(ret, (xmlNodePtr) attr);
3616 }
3617 }
3618 }
3619 break;
3620 }
3621 case XML_NAMESPACE_DECL: {
3622 TODO;
3623 break;
3624 }
3625 default:
3626 break;
3627 }
3628 break;
3629 }
3630 } while (cur != NULL);
3631 }
Bjorn Reesee1dc0112001-03-03 12:09:03 +00003632 ctxt->context->node = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00003633#ifdef DEBUG_STEP
3634 xmlGenericError(xmlGenericErrorContext,
3635 "\nExamined %d nodes, found %d nodes at that step\n", t, n);
3636#endif
3637 xmlXPathFreeObject(obj);
3638 valuePush(ctxt, xmlXPathWrapNodeSet(ret));
3639}
3640
3641
3642/************************************************************************
3643 * *
3644 * Implicit tree core function library *
3645 * *
3646 ************************************************************************/
3647
3648/**
3649 * xmlXPathRoot:
3650 * @ctxt: the XPath Parser context
3651 *
3652 * Initialize the context to the root of the document
3653 */
3654void
3655xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
3656 ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
3657 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3658}
3659
3660/************************************************************************
3661 * *
3662 * The explicit core function library *
3663 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
3664 * *
3665 ************************************************************************/
3666
3667
3668/**
3669 * xmlXPathLastFunction:
3670 * @ctxt: the XPath Parser context
3671 * @nargs: the number of arguments
3672 *
3673 * Implement the last() XPath function
3674 * number last()
3675 * The last function returns the number of nodes in the context node list.
3676 */
3677void
3678xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3679 CHECK_ARITY(0);
3680 if (ctxt->context->contextSize >= 0) {
3681 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
3682#ifdef DEBUG_EXPR
3683 xmlGenericError(xmlGenericErrorContext,
3684 "last() : %d\n", ctxt->context->contextSize);
3685#endif
3686 } else {
3687 XP_ERROR(XPATH_INVALID_CTXT_SIZE);
3688 }
3689}
3690
3691/**
3692 * xmlXPathPositionFunction:
3693 * @ctxt: the XPath Parser context
3694 * @nargs: the number of arguments
3695 *
3696 * Implement the position() XPath function
3697 * number position()
3698 * The position function returns the position of the context node in the
3699 * context node list. The first position is 1, and so the last positionr
3700 * will be equal to last().
3701 */
3702void
3703xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3704 CHECK_ARITY(0);
3705 if (ctxt->context->proximityPosition >= 0) {
3706 valuePush(ctxt,
3707 xmlXPathNewFloat((double) ctxt->context->proximityPosition));
3708#ifdef DEBUG_EXPR
3709 xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
3710 ctxt->context->proximityPosition);
3711#endif
3712 } else {
3713 XP_ERROR(XPATH_INVALID_CTXT_POSITION);
3714 }
3715}
3716
3717/**
3718 * xmlXPathCountFunction:
3719 * @ctxt: the XPath Parser context
3720 * @nargs: the number of arguments
3721 *
3722 * Implement the count() XPath function
3723 * number count(node-set)
3724 */
3725void
3726xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3727 xmlXPathObjectPtr cur;
3728
3729 CHECK_ARITY(1);
3730 if ((ctxt->value == NULL) ||
3731 ((ctxt->value->type != XPATH_NODESET) &&
3732 (ctxt->value->type != XPATH_XSLT_TREE)))
3733 XP_ERROR(XPATH_INVALID_TYPE);
3734 cur = valuePop(ctxt);
3735
3736 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
3737 xmlXPathFreeObject(cur);
3738}
3739
3740/**
3741 * xmlXPathIdFunction:
3742 * @ctxt: the XPath Parser context
3743 * @nargs: the number of arguments
3744 *
3745 * Implement the id() XPath function
3746 * node-set id(object)
3747 * The id function selects elements by their unique ID
3748 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
3749 * then the result is the union of the result of applying id to the
3750 * string value of each of the nodes in the argument node-set. When the
3751 * argument to id is of any other type, the argument is converted to a
3752 * string as if by a call to the string function; the string is split
3753 * into a whitespace-separated list of tokens (whitespace is any sequence
3754 * of characters matching the production S); the result is a node-set
3755 * containing the elements in the same document as the context node that
3756 * have a unique ID equal to any of the tokens in the list.
3757 */
3758void
3759xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3760 const xmlChar *tokens;
3761 const xmlChar *cur;
3762 xmlChar *ID;
3763 xmlAttrPtr attr;
3764 xmlNodePtr elem = NULL;
3765 xmlXPathObjectPtr ret, obj;
3766
3767 CHECK_ARITY(1);
3768 obj = valuePop(ctxt);
3769 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
3770 if (obj->type == XPATH_NODESET) {
3771 xmlXPathObjectPtr newobj;
3772 int i;
3773
3774 ret = xmlXPathNewNodeSet(NULL);
3775
3776 for (i = 0; i < obj->nodesetval->nodeNr; i++) {
3777 valuePush(ctxt,
3778 xmlXPathNewNodeSet(obj->nodesetval->nodeTab[i]));
3779 xmlXPathStringFunction(ctxt, 1);
3780 xmlXPathIdFunction(ctxt, 1);
3781 newobj = valuePop(ctxt);
3782 ret->nodesetval = xmlXPathNodeSetMerge(ret->nodesetval,
3783 newobj->nodesetval);
3784 xmlXPathFreeObject(newobj);
3785 }
3786
3787 xmlXPathFreeObject(obj);
3788 valuePush(ctxt, ret);
3789 return;
3790 }
3791 if (obj->type != XPATH_STRING) {
3792 valuePush(ctxt, obj);
3793 xmlXPathStringFunction(ctxt, 1);
3794 obj = valuePop(ctxt);
3795 if (obj->type != XPATH_STRING) {
3796 xmlXPathFreeObject(obj);
3797 return;
3798 }
3799 }
3800 tokens = obj->stringval;
3801
3802 ret = xmlXPathNewNodeSet(NULL);
3803 valuePush(ctxt, ret);
3804 if (tokens == NULL) {
3805 xmlXPathFreeObject(obj);
3806 return;
3807 }
3808
3809 cur = tokens;
3810
3811 while (IS_BLANK(*cur)) cur++;
3812 while (*cur != 0) {
3813 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
3814 (*cur == '.') || (*cur == '-') ||
3815 (*cur == '_') || (*cur == ':') ||
3816 (IS_COMBINING(*cur)) ||
3817 (IS_EXTENDER(*cur)))
3818 cur++;
3819
3820 if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
3821
3822 ID = xmlStrndup(tokens, cur - tokens);
3823 attr = xmlGetID(ctxt->context->doc, ID);
3824 if (attr != NULL) {
3825 elem = attr->parent;
3826 xmlXPathNodeSetAdd(ret->nodesetval, elem);
3827 }
3828 if (ID != NULL)
3829 xmlFree(ID);
3830
3831 while (IS_BLANK(*cur)) cur++;
3832 tokens = cur;
3833 }
3834 xmlXPathFreeObject(obj);
3835 return;
3836}
3837
3838/**
3839 * xmlXPathLocalNameFunction:
3840 * @ctxt: the XPath Parser context
3841 * @nargs: the number of arguments
3842 *
3843 * Implement the local-name() XPath function
3844 * string local-name(node-set?)
3845 * The local-name function returns a string containing the local part
3846 * of the name of the node in the argument node-set that is first in
3847 * document order. If the node-set is empty or the first node has no
3848 * name, an empty string is returned. If the argument is omitted it
3849 * defaults to the context node.
3850 */
3851void
3852xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3853 xmlXPathObjectPtr cur;
3854
3855 if (nargs == 0) {
3856 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3857 nargs = 1;
3858 }
3859
3860 CHECK_ARITY(1);
3861 if ((ctxt->value == NULL) ||
3862 ((ctxt->value->type != XPATH_NODESET) &&
3863 (ctxt->value->type != XPATH_XSLT_TREE)))
3864 XP_ERROR(XPATH_INVALID_TYPE);
3865 cur = valuePop(ctxt);
3866
3867 if (cur->nodesetval->nodeNr == 0) {
3868 valuePush(ctxt, xmlXPathNewCString(""));
3869 } else {
3870 int i = 0; /* Should be first in document order !!!!! */
3871 switch (cur->nodesetval->nodeTab[i]->type) {
3872 case XML_ELEMENT_NODE:
3873 case XML_ATTRIBUTE_NODE:
3874 case XML_PI_NODE:
3875 valuePush(ctxt,
3876 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
3877 break;
3878 case XML_NAMESPACE_DECL:
3879 valuePush(ctxt, xmlXPathNewString(
3880 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
3881 break;
3882 default:
3883 valuePush(ctxt, xmlXPathNewCString(""));
3884 }
3885 }
3886 xmlXPathFreeObject(cur);
3887}
3888
3889/**
3890 * xmlXPathNamespaceURIFunction:
3891 * @ctxt: the XPath Parser context
3892 * @nargs: the number of arguments
3893 *
3894 * Implement the namespace-uri() XPath function
3895 * string namespace-uri(node-set?)
3896 * The namespace-uri function returns a string containing the
3897 * namespace URI of the expanded name of the node in the argument
3898 * node-set that is first in document order. If the node-set is empty,
3899 * the first node has no name, or the expanded name has no namespace
3900 * URI, an empty string is returned. If the argument is omitted it
3901 * defaults to the context node.
3902 */
3903void
3904xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3905 xmlXPathObjectPtr cur;
3906
3907 if (nargs == 0) {
3908 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3909 nargs = 1;
3910 }
3911 CHECK_ARITY(1);
3912 if ((ctxt->value == NULL) ||
3913 ((ctxt->value->type != XPATH_NODESET) &&
3914 (ctxt->value->type != XPATH_XSLT_TREE)))
3915 XP_ERROR(XPATH_INVALID_TYPE);
3916 cur = valuePop(ctxt);
3917
3918 if (cur->nodesetval->nodeNr == 0) {
3919 valuePush(ctxt, xmlXPathNewCString(""));
3920 } else {
3921 int i = 0; /* Should be first in document order !!!!! */
3922 switch (cur->nodesetval->nodeTab[i]->type) {
3923 case XML_ELEMENT_NODE:
3924 case XML_ATTRIBUTE_NODE:
3925 if (cur->nodesetval->nodeTab[i]->ns == NULL)
3926 valuePush(ctxt, xmlXPathNewCString(""));
3927 else
3928 valuePush(ctxt, xmlXPathNewString(
3929 cur->nodesetval->nodeTab[i]->ns->href));
3930 break;
3931 default:
3932 valuePush(ctxt, xmlXPathNewCString(""));
3933 }
3934 }
3935 xmlXPathFreeObject(cur);
3936}
3937
3938/**
3939 * xmlXPathNameFunction:
3940 * @ctxt: the XPath Parser context
3941 * @nargs: the number of arguments
3942 *
3943 * Implement the name() XPath function
3944 * string name(node-set?)
3945 * The name function returns a string containing a QName representing
3946 * the name of the node in the argument node-set that is first in documenti
3947 * order. The QName must represent the name with respect to the namespace
3948 * declarations in effect on the node whose name is being represented.
3949 * Typically, this will be the form in which the name occurred in the XML
3950 * source. This need not be the case if there are namespace declarations
3951 * in effect on the node that associate multiple prefixes with the same
3952 * namespace. However, an implementation may include information about
3953 * the original prefix in its representation of nodes; in this case, an
3954 * implementation can ensure that the returned string is always the same
3955 * as the QName used in the XML source. If the argument it omitted it
3956 * defaults to the context node.
3957 * Libxml keep the original prefix so the "real qualified name" used is
3958 * returned.
3959 */
3960void
3961xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
3962 xmlXPathObjectPtr cur;
3963
3964 if (nargs == 0) {
3965 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
3966 nargs = 1;
3967 }
3968
3969 CHECK_ARITY(1);
3970 if ((ctxt->value == NULL) ||
3971 ((ctxt->value->type != XPATH_NODESET) &&
3972 (ctxt->value->type != XPATH_XSLT_TREE)))
3973 XP_ERROR(XPATH_INVALID_TYPE);
3974 cur = valuePop(ctxt);
3975
3976 if (cur->nodesetval->nodeNr == 0) {
3977 valuePush(ctxt, xmlXPathNewCString(""));
3978 } else {
3979 int i = 0; /* Should be first in document order !!!!! */
3980
3981 switch (cur->nodesetval->nodeTab[i]->type) {
3982 case XML_ELEMENT_NODE:
3983 case XML_ATTRIBUTE_NODE:
3984 if (cur->nodesetval->nodeTab[i]->ns == NULL)
3985 valuePush(ctxt, xmlXPathNewString(
3986 cur->nodesetval->nodeTab[i]->name));
3987
3988 else {
3989 char name[2000];
3990#ifdef HAVE_SNPRINTF
3991 snprintf(name, sizeof(name), "%s:%s",
3992 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
3993 (char *) cur->nodesetval->nodeTab[i]->name);
3994#else
3995 sprintf(name, "%s:%s",
3996 (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
3997 (char *) cur->nodesetval->nodeTab[i]->name);
3998#endif
3999 name[sizeof(name) - 1] = 0;
4000 valuePush(ctxt, xmlXPathNewCString(name));
4001 }
4002 break;
4003 default:
4004 valuePush(ctxt,
4005 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4006 xmlXPathLocalNameFunction(ctxt, 1);
4007 }
4008 }
4009 xmlXPathFreeObject(cur);
4010}
4011
4012/**
4013 * xmlXPathStringFunction:
4014 * @ctxt: the XPath Parser context
4015 * @nargs: the number of arguments
4016 *
4017 * Implement the string() XPath function
4018 * string string(object?)
4019 * he string function converts an object to a string as follows:
4020 * - A node-set is converted to a string by returning the value of
4021 * the node in the node-set that is first in document order.
4022 * If the node-set is empty, an empty string is returned.
4023 * - A number is converted to a string as follows
4024 * + NaN is converted to the string NaN
4025 * + positive zero is converted to the string 0
4026 * + negative zero is converted to the string 0
4027 * + positive infinity is converted to the string Infinity
4028 * + negative infinity is converted to the string -Infinity
4029 * + if the number is an integer, the number is represented in
4030 * decimal form as a Number with no decimal point and no leading
4031 * zeros, preceded by a minus sign (-) if the number is negative
4032 * + otherwise, the number is represented in decimal form as a
4033 * Number including a decimal point with at least one digit
4034 * before the decimal point and at least one digit after the
4035 * decimal point, preceded by a minus sign (-) if the number
4036 * is negative; there must be no leading zeros before the decimal
4037 * point apart possibly from the one required digit immediatelyi
4038 * before the decimal point; beyond the one required digit
4039 * after the decimal point there must be as many, but only as
4040 * many, more digits as are needed to uniquely distinguish the
4041 * number from all other IEEE 754 numeric values.
4042 * - The boolean false value is converted to the string false.
4043 * The boolean true value is converted to the string true.
4044 *
4045 * If the argument is omitted, it defaults to a node-set with the
4046 * context node as its only member.
4047 */
4048void
4049xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4050 xmlXPathObjectPtr cur;
4051
4052 if (nargs == 0) {
4053 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4054 nargs = 1;
4055 }
4056
4057 CHECK_ARITY(1);
4058 cur = valuePop(ctxt);
4059 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
4060 switch (cur->type) {
4061 case XPATH_UNDEFINED:
4062#ifdef DEBUG_EXPR
4063 xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
4064#endif
4065 valuePush(ctxt, xmlXPathNewCString(""));
4066 break;
4067 case XPATH_XSLT_TREE:
4068 case XPATH_NODESET:
4069 if (cur->nodesetval == NULL)
4070 valuePush(ctxt, xmlXPathNewCString(""));
4071 else if (cur->nodesetval->nodeNr == 0) {
4072 valuePush(ctxt, xmlXPathNewCString(""));
4073 } else {
4074 xmlChar *res;
4075 int i = 0; /* Should be first in document order !!!!! */
4076 res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
4077 valuePush(ctxt, xmlXPathNewString(res));
4078 if (res != NULL)
4079 xmlFree(res);
4080 }
4081 xmlXPathFreeObject(cur);
4082 return;
4083 case XPATH_STRING:
4084 valuePush(ctxt, cur);
4085 return;
4086 case XPATH_BOOLEAN:
4087 if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true"));
4088 else valuePush(ctxt, xmlXPathNewCString("false"));
4089 xmlXPathFreeObject(cur);
4090 return;
4091 case XPATH_NUMBER: {
4092 char buf[100];
4093
Bjorn Reesee1dc0112001-03-03 12:09:03 +00004094 xmlXPathFormatNumber(cur->floatval, buf, sizeof(buf));
Owen Taylor3473f882001-02-23 17:55:21 +00004095 valuePush(ctxt, xmlXPathNewCString(buf));
4096 xmlXPathFreeObject(cur);
4097 return;
4098 }
4099 case XPATH_USERS:
4100 case XPATH_POINT:
4101 case XPATH_RANGE:
4102 case XPATH_LOCATIONSET:
4103 TODO
4104 valuePush(ctxt, xmlXPathNewCString(""));
4105 break;
4106 }
4107 STRANGE
4108}
4109
4110/**
4111 * xmlXPathStringLengthFunction:
4112 * @ctxt: the XPath Parser context
4113 * @nargs: the number of arguments
4114 *
4115 * Implement the string-length() XPath function
4116 * number string-length(string?)
4117 * The string-length returns the number of characters in the string
4118 * (see [3.6 Strings]). If the argument is omitted, it defaults to
4119 * the context node converted to a string, in other words the value
4120 * of the context node.
4121 */
4122void
4123xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4124 xmlXPathObjectPtr cur;
4125
4126 if (nargs == 0) {
4127 if (ctxt->context->node == NULL) {
4128 valuePush(ctxt, xmlXPathNewFloat(0));
4129 } else {
4130 xmlChar *content;
4131
4132 content = xmlNodeGetContent(ctxt->context->node);
4133 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
4134 xmlFree(content);
4135 }
4136 return;
4137 }
4138 CHECK_ARITY(1);
4139 CAST_TO_STRING;
4140 CHECK_TYPE(XPATH_STRING);
4141 cur = valuePop(ctxt);
4142 valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
4143 xmlXPathFreeObject(cur);
4144}
4145
4146/**
4147 * xmlXPathConcatFunction:
4148 * @ctxt: the XPath Parser context
4149 * @nargs: the number of arguments
4150 *
4151 * Implement the concat() XPath function
4152 * string concat(string, string, string*)
4153 * The concat function returns the concatenation of its arguments.
4154 */
4155void
4156xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4157 xmlXPathObjectPtr cur, newobj;
4158 xmlChar *tmp;
4159
4160 if (nargs < 2) {
4161 CHECK_ARITY(2);
4162 }
4163
4164 CAST_TO_STRING;
4165 cur = valuePop(ctxt);
4166 if ((cur == NULL) || (cur->type != XPATH_STRING)) {
4167 xmlXPathFreeObject(cur);
4168 return;
4169 }
4170 nargs--;
4171
4172 while (nargs > 0) {
4173 CAST_TO_STRING;
4174 newobj = valuePop(ctxt);
4175 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
4176 xmlXPathFreeObject(newobj);
4177 xmlXPathFreeObject(cur);
4178 XP_ERROR(XPATH_INVALID_TYPE);
4179 }
4180 tmp = xmlStrcat(newobj->stringval, cur->stringval);
4181 newobj->stringval = cur->stringval;
4182 cur->stringval = tmp;
4183
4184 xmlXPathFreeObject(newobj);
4185 nargs--;
4186 }
4187 valuePush(ctxt, cur);
4188}
4189
4190/**
4191 * xmlXPathContainsFunction:
4192 * @ctxt: the XPath Parser context
4193 * @nargs: the number of arguments
4194 *
4195 * Implement the contains() XPath function
4196 * boolean contains(string, string)
4197 * The contains function returns true if the first argument string
4198 * contains the second argument string, and otherwise returns false.
4199 */
4200void
4201xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4202 xmlXPathObjectPtr hay, needle;
4203
4204 CHECK_ARITY(2);
4205 CAST_TO_STRING;
4206 CHECK_TYPE(XPATH_STRING);
4207 needle = valuePop(ctxt);
4208 CAST_TO_STRING;
4209 hay = valuePop(ctxt);
4210 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4211 xmlXPathFreeObject(hay);
4212 xmlXPathFreeObject(needle);
4213 XP_ERROR(XPATH_INVALID_TYPE);
4214 }
4215 if (xmlStrstr(hay->stringval, needle->stringval))
4216 valuePush(ctxt, xmlXPathNewBoolean(1));
4217 else
4218 valuePush(ctxt, xmlXPathNewBoolean(0));
4219 xmlXPathFreeObject(hay);
4220 xmlXPathFreeObject(needle);
4221}
4222
4223/**
4224 * xmlXPathStartsWithFunction:
4225 * @ctxt: the XPath Parser context
4226 * @nargs: the number of arguments
4227 *
4228 * Implement the starts-with() XPath function
4229 * boolean starts-with(string, string)
4230 * The starts-with function returns true if the first argument string
4231 * starts with the second argument string, and otherwise returns false.
4232 */
4233void
4234xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4235 xmlXPathObjectPtr hay, needle;
4236 int n;
4237
4238 CHECK_ARITY(2);
4239 CAST_TO_STRING;
4240 CHECK_TYPE(XPATH_STRING);
4241 needle = valuePop(ctxt);
4242 CAST_TO_STRING;
4243 hay = valuePop(ctxt);
4244 if ((hay == NULL) || (hay->type != XPATH_STRING)) {
4245 xmlXPathFreeObject(hay);
4246 xmlXPathFreeObject(needle);
4247 XP_ERROR(XPATH_INVALID_TYPE);
4248 }
4249 n = xmlStrlen(needle->stringval);
4250 if (xmlStrncmp(hay->stringval, needle->stringval, n))
4251 valuePush(ctxt, xmlXPathNewBoolean(0));
4252 else
4253 valuePush(ctxt, xmlXPathNewBoolean(1));
4254 xmlXPathFreeObject(hay);
4255 xmlXPathFreeObject(needle);
4256}
4257
4258/**
4259 * xmlXPathSubstringFunction:
4260 * @ctxt: the XPath Parser context
4261 * @nargs: the number of arguments
4262 *
4263 * Implement the substring() XPath function
4264 * string substring(string, number, number?)
4265 * The substring function returns the substring of the first argument
4266 * starting at the position specified in the second argument with
4267 * length specified in the third argument. For example,
4268 * substring("12345",2,3) returns "234". If the third argument is not
4269 * specified, it returns the substring starting at the position specified
4270 * in the second argument and continuing to the end of the string. For
4271 * example, substring("12345",2) returns "2345". More precisely, each
4272 * character in the string (see [3.6 Strings]) is considered to have a
4273 * numeric position: the position of the first character is 1, the position
4274 * of the second character is 2 and so on. The returned substring contains
4275 * those characters for which the position of the character is greater than
4276 * or equal to the second argument and, if the third argument is specified,
4277 * less than the sum of the second and third arguments; the comparisons
4278 * and addition used for the above follow the standard IEEE 754 rules. Thus:
4279 * - substring("12345", 1.5, 2.6) returns "234"
4280 * - substring("12345", 0, 3) returns "12"
4281 * - substring("12345", 0 div 0, 3) returns ""
4282 * - substring("12345", 1, 0 div 0) returns ""
4283 * - substring("12345", -42, 1 div 0) returns "12345"
4284 * - substring("12345", -1 div 0, 1 div 0) returns ""
4285 */
4286void
4287xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4288 xmlXPathObjectPtr str, start, len;
4289 double le, in;
4290 int i, l;
4291 xmlChar *ret;
4292
4293 /*
4294 * Conformance needs to be checked !!!!!
4295 */
4296 if (nargs < 2) {
4297 CHECK_ARITY(2);
4298 }
4299 if (nargs > 3) {
4300 CHECK_ARITY(3);
4301 }
4302 if (nargs == 3) {
4303 CAST_TO_NUMBER;
4304 CHECK_TYPE(XPATH_NUMBER);
4305 len = valuePop(ctxt);
4306 le = len->floatval;
4307 xmlXPathFreeObject(len);
4308 } else {
4309 le = 2000000000;
4310 }
4311 CAST_TO_NUMBER;
4312 CHECK_TYPE(XPATH_NUMBER);
4313 start = valuePop(ctxt);
4314 in = start->floatval;
4315 xmlXPathFreeObject(start);
4316 CAST_TO_STRING;
4317 CHECK_TYPE(XPATH_STRING);
4318 str = valuePop(ctxt);
4319 le += in;
4320
4321 /* integer index of the first char */
4322 i = (int) in;
4323 if (((double)i) != in) i++;
4324
4325 /* integer index of the last char */
4326 l = (int) le;
4327 if (((double)l) != le) l++;
4328
4329 /* back to a zero based len */
4330 i--;
4331 l--;
4332
4333 /* check against the string len */
4334 if (l > 1024) {
4335 l = xmlStrlen(str->stringval);
4336 }
4337 if (i < 0) {
4338 i = 0;
4339 }
4340
4341 /* number of chars to copy */
4342 l -= i;
4343
4344 ret = xmlStrsub(str->stringval, i, l);
4345 if (ret == NULL)
4346 valuePush(ctxt, xmlXPathNewCString(""));
4347 else {
4348 valuePush(ctxt, xmlXPathNewString(ret));
4349 xmlFree(ret);
4350 }
4351 xmlXPathFreeObject(str);
4352}
4353
4354/**
4355 * xmlXPathSubstringBeforeFunction:
4356 * @ctxt: the XPath Parser context
4357 * @nargs: the number of arguments
4358 *
4359 * Implement the substring-before() XPath function
4360 * string substring-before(string, string)
4361 * The substring-before function returns the substring of the first
4362 * argument string that precedes the first occurrence of the second
4363 * argument string in the first argument string, or the empty string
4364 * if the first argument string does not contain the second argument
4365 * string. For example, substring-before("1999/04/01","/") returns 1999.
4366 */
4367void
4368xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4369 xmlXPathObjectPtr str;
4370 xmlXPathObjectPtr find;
4371 xmlBufferPtr target;
4372 const xmlChar *point;
4373 int offset;
4374
4375 CHECK_ARITY(2);
4376 CAST_TO_STRING;
4377 find = valuePop(ctxt);
4378 CAST_TO_STRING;
4379 str = valuePop(ctxt);
4380
4381 target = xmlBufferCreate();
4382 if (target) {
4383 point = xmlStrstr(str->stringval, find->stringval);
4384 if (point) {
4385 offset = (int)(point - str->stringval);
4386 xmlBufferAdd(target, str->stringval, offset);
4387 }
4388 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4389 xmlBufferFree(target);
4390 }
4391
4392 xmlXPathFreeObject(str);
4393 xmlXPathFreeObject(find);
4394}
4395
4396/**
4397 * xmlXPathSubstringAfterFunction:
4398 * @ctxt: the XPath Parser context
4399 * @nargs: the number of arguments
4400 *
4401 * Implement the substring-after() XPath function
4402 * string substring-after(string, string)
4403 * The substring-after function returns the substring of the first
4404 * argument string that follows the first occurrence of the second
4405 * argument string in the first argument string, or the empty stringi
4406 * if the first argument string does not contain the second argument
4407 * string. For example, substring-after("1999/04/01","/") returns 04/01,
4408 * and substring-after("1999/04/01","19") returns 99/04/01.
4409 */
4410void
4411xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4412 xmlXPathObjectPtr str;
4413 xmlXPathObjectPtr find;
4414 xmlBufferPtr target;
4415 const xmlChar *point;
4416 int offset;
4417
4418 CHECK_ARITY(2);
4419 CAST_TO_STRING;
4420 find = valuePop(ctxt);
4421 CAST_TO_STRING;
4422 str = valuePop(ctxt);
4423
4424 target = xmlBufferCreate();
4425 if (target) {
4426 point = xmlStrstr(str->stringval, find->stringval);
4427 if (point) {
4428 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
4429 xmlBufferAdd(target, &str->stringval[offset],
4430 xmlStrlen(str->stringval) - offset);
4431 }
4432 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4433 xmlBufferFree(target);
4434 }
4435
4436 xmlXPathFreeObject(str);
4437 xmlXPathFreeObject(find);
4438}
4439
4440/**
4441 * xmlXPathNormalizeFunction:
4442 * @ctxt: the XPath Parser context
4443 * @nargs: the number of arguments
4444 *
4445 * Implement the normalize-space() XPath function
4446 * string normalize-space(string?)
4447 * The normalize-space function returns the argument string with white
4448 * space normalized by stripping leading and trailing whitespace
4449 * and replacing sequences of whitespace characters by a single
4450 * space. Whitespace characters are the same allowed by the S production
4451 * in XML. If the argument is omitted, it defaults to the context
4452 * node converted to a string, in other words the value of the context node.
4453 */
4454void
4455xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4456 xmlXPathObjectPtr obj = NULL;
4457 xmlChar *source = NULL;
4458 xmlBufferPtr target;
4459 xmlChar blank;
4460
4461 if (nargs == 0) {
4462 /* Use current context node */
4463 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
4464 xmlXPathStringFunction(ctxt, 1);
4465 nargs = 1;
4466 }
4467
4468 CHECK_ARITY(1);
4469 CAST_TO_STRING;
4470 CHECK_TYPE(XPATH_STRING);
4471 obj = valuePop(ctxt);
4472 source = obj->stringval;
4473
4474 target = xmlBufferCreate();
4475 if (target && source) {
4476
4477 /* Skip leading whitespaces */
4478 while (IS_BLANK(*source))
4479 source++;
4480
4481 /* Collapse intermediate whitespaces, and skip trailing whitespaces */
4482 blank = 0;
4483 while (*source) {
4484 if (IS_BLANK(*source)) {
4485 blank = *source;
4486 } else {
4487 if (blank) {
4488 xmlBufferAdd(target, &blank, 1);
4489 blank = 0;
4490 }
4491 xmlBufferAdd(target, source, 1);
4492 }
4493 source++;
4494 }
4495
4496 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4497 xmlBufferFree(target);
4498 }
4499 xmlXPathFreeObject(obj);
4500}
4501
4502/**
4503 * xmlXPathTranslateFunction:
4504 * @ctxt: the XPath Parser context
4505 * @nargs: the number of arguments
4506 *
4507 * Implement the translate() XPath function
4508 * string translate(string, string, string)
4509 * The translate function returns the first argument string with
4510 * occurrences of characters in the second argument string replaced
4511 * by the character at the corresponding position in the third argument
4512 * string. For example, translate("bar","abc","ABC") returns the string
4513 * BAr. If there is a character in the second argument string with no
4514 * character at a corresponding position in the third argument string
4515 * (because the second argument string is longer than the third argument
4516 * string), then occurrences of that character in the first argument
4517 * string are removed. For example, translate("--aaa--","abc-","ABC")
4518 * returns "AAA". If a character occurs more than once in second
4519 * argument string, then the first occurrence determines the replacement
4520 * character. If the third argument string is longer than the second
4521 * argument string, then excess characters are ignored.
4522 */
4523void
4524xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4525 xmlXPathObjectPtr str;
4526 xmlXPathObjectPtr from;
4527 xmlXPathObjectPtr to;
4528 xmlBufferPtr target;
4529 int i, offset, max;
4530 xmlChar ch;
4531 const xmlChar *point;
4532
4533 CHECK_ARITY(3);
4534
4535 CAST_TO_STRING;
4536 to = valuePop(ctxt);
4537 CAST_TO_STRING;
4538 from = valuePop(ctxt);
4539 CAST_TO_STRING;
4540 str = valuePop(ctxt);
4541
4542 target = xmlBufferCreate();
4543 if (target) {
4544 max = xmlStrlen(to->stringval);
4545 for (i = 0; (ch = str->stringval[i]); i++) {
4546 point = xmlStrchr(from->stringval, ch);
4547 if (point) {
4548 /* Warning: This may not work with UTF-8 */
4549 offset = (int)(point - from->stringval);
4550 if (offset < max)
4551 xmlBufferAdd(target, &to->stringval[offset], 1);
4552 } else
4553 xmlBufferAdd(target, &ch, 1);
4554 }
4555 }
4556 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
4557 xmlBufferFree(target);
4558 xmlXPathFreeObject(str);
4559 xmlXPathFreeObject(from);
4560 xmlXPathFreeObject(to);
4561}
4562
4563/**
4564 * xmlXPathBooleanFunction:
4565 * @ctxt: the XPath Parser context
4566 * @nargs: the number of arguments
4567 *
4568 * Implement the boolean() XPath function
4569 * boolean boolean(object)
4570 * he boolean function converts its argument to a boolean as follows:
4571 * - a number is true if and only if it is neither positive or
4572 * negative zero nor NaN
4573 * - a node-set is true if and only if it is non-empty
4574 * - a string is true if and only if its length is non-zero
4575 */
4576void
4577xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4578 xmlXPathObjectPtr cur;
4579 int res = 0;
4580
4581 CHECK_ARITY(1);
4582 cur = valuePop(ctxt);
4583 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
4584 switch (cur->type) {
4585 case XPATH_NODESET:
4586 case XPATH_XSLT_TREE:
4587 if ((cur->nodesetval == NULL) ||
4588 (cur->nodesetval->nodeNr == 0)) res = 0;
4589 else
4590 res = 1;
4591 break;
4592 case XPATH_STRING:
4593 if ((cur->stringval == NULL) ||
4594 (cur->stringval[0] == 0)) res = 0;
4595 else
4596 res = 1;
4597 break;
4598 case XPATH_BOOLEAN:
4599 valuePush(ctxt, cur);
4600 return;
4601 case XPATH_NUMBER:
4602 if (cur->floatval) res = 1;
4603 break;
4604 default:
4605 STRANGE
4606 }
4607 xmlXPathFreeObject(cur);
4608 valuePush(ctxt, xmlXPathNewBoolean(res));
4609}
4610
4611/**
4612 * xmlXPathNotFunction:
4613 * @ctxt: the XPath Parser context
4614 * @nargs: the number of arguments
4615 *
4616 * Implement the not() XPath function
4617 * boolean not(boolean)
4618 * The not function returns true if its argument is false,
4619 * and false otherwise.
4620 */
4621void
4622xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4623 CHECK_ARITY(1);
4624 CAST_TO_BOOLEAN;
4625 CHECK_TYPE(XPATH_BOOLEAN);
4626 ctxt->value->boolval = ! ctxt->value->boolval;
4627}
4628
4629/**
4630 * xmlXPathTrueFunction:
4631 * @ctxt: the XPath Parser context
4632 * @nargs: the number of arguments
4633 *
4634 * Implement the true() XPath function
4635 * boolean true()
4636 */
4637void
4638xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4639 CHECK_ARITY(0);
4640 valuePush(ctxt, xmlXPathNewBoolean(1));
4641}
4642
4643/**
4644 * xmlXPathFalseFunction:
4645 * @ctxt: the XPath Parser context
4646 * @nargs: the number of arguments
4647 *
4648 * Implement the false() XPath function
4649 * boolean false()
4650 */
4651void
4652xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4653 CHECK_ARITY(0);
4654 valuePush(ctxt, xmlXPathNewBoolean(0));
4655}
4656
4657/**
4658 * xmlXPathLangFunction:
4659 * @ctxt: the XPath Parser context
4660 * @nargs: the number of arguments
4661 *
4662 * Implement the lang() XPath function
4663 * boolean lang(string)
4664 * The lang function returns true or false depending on whether the
4665 * language of the context node as specified by xml:lang attributes
4666 * is the same as or is a sublanguage of the language specified by
4667 * the argument string. The language of the context node is determined
4668 * by the value of the xml:lang attribute on the context node, or, if
4669 * the context node has no xml:lang attribute, by the value of the
4670 * xml:lang attribute on the nearest ancestor of the context node that
4671 * has an xml:lang attribute. If there is no such attribute, then lang
4672 * returns false. If there is such an attribute, then lang returns
4673 * true if the attribute value is equal to the argument ignoring case,
4674 * or if there is some suffix starting with - such that the attribute
4675 * value is equal to the argument ignoring that suffix of the attribute
4676 * value and ignoring case.
4677 */
4678void
4679xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4680 xmlXPathObjectPtr val;
4681 const xmlChar *theLang;
4682 const xmlChar *lang;
4683 int ret = 0;
4684 int i;
4685
4686 CHECK_ARITY(1);
4687 CAST_TO_STRING;
4688 CHECK_TYPE(XPATH_STRING);
4689 val = valuePop(ctxt);
4690 lang = val->stringval;
4691 theLang = xmlNodeGetLang(ctxt->context->node);
4692 if ((theLang != NULL) && (lang != NULL)) {
4693 for (i = 0;lang[i] != 0;i++)
4694 if (toupper(lang[i]) != toupper(theLang[i]))
4695 goto not_equal;
4696 ret = 1;
4697 }
4698not_equal:
4699 xmlXPathFreeObject(val);
4700 valuePush(ctxt, xmlXPathNewBoolean(ret));
4701}
4702
4703/**
4704 * xmlXPathNumberFunction:
4705 * @ctxt: the XPath Parser context
4706 * @nargs: the number of arguments
4707 *
4708 * Implement the number() XPath function
4709 * number number(object?)
4710 */
4711void
4712xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4713 xmlXPathObjectPtr cur;
4714 double res;
4715
4716 if (nargs == 0) {
4717 if (ctxt->context->node == NULL) {
4718 valuePush(ctxt, xmlXPathNewFloat(0.0));
4719 } else {
4720 xmlChar* content = xmlNodeGetContent(ctxt->context->node);
4721
4722 res = xmlXPathStringEvalNumber(content);
4723 valuePush(ctxt, xmlXPathNewFloat(res));
4724 xmlFree(content);
4725 }
4726 return;
4727 }
4728
4729 CHECK_ARITY(1);
4730 cur = valuePop(ctxt);
4731 switch (cur->type) {
4732 case XPATH_UNDEFINED:
4733#ifdef DEBUG_EXPR
4734 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
4735#endif
4736 valuePush(ctxt, xmlXPathNewFloat(0.0));
4737 break;
4738 case XPATH_XSLT_TREE:
4739 case XPATH_NODESET:
4740 valuePush(ctxt, cur);
4741 xmlXPathStringFunction(ctxt, 1);
4742 cur = valuePop(ctxt);
4743 case XPATH_STRING:
4744 res = xmlXPathStringEvalNumber(cur->stringval);
4745 valuePush(ctxt, xmlXPathNewFloat(res));
4746 xmlXPathFreeObject(cur);
4747 return;
4748 case XPATH_BOOLEAN:
4749 if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
4750 else valuePush(ctxt, xmlXPathNewFloat(0.0));
4751 xmlXPathFreeObject(cur);
4752 return;
4753 case XPATH_NUMBER:
4754 valuePush(ctxt, cur);
4755 return;
4756 case XPATH_USERS:
4757 case XPATH_POINT:
4758 case XPATH_RANGE:
4759 case XPATH_LOCATIONSET:
4760 TODO
4761 valuePush(ctxt, xmlXPathNewFloat(0.0));
4762 break;
4763 }
4764 STRANGE
4765}
4766
4767/**
4768 * xmlXPathSumFunction:
4769 * @ctxt: the XPath Parser context
4770 * @nargs: the number of arguments
4771 *
4772 * Implement the sum() XPath function
4773 * number sum(node-set)
4774 * The sum function returns the sum of the values of the nodes in
4775 * the argument node-set.
4776 */
4777void
4778xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4779 xmlXPathObjectPtr cur;
4780 int i;
4781
4782 CHECK_ARITY(1);
4783 if ((ctxt->value == NULL) ||
4784 ((ctxt->value->type != XPATH_NODESET) &&
4785 (ctxt->value->type != XPATH_XSLT_TREE)))
4786 XP_ERROR(XPATH_INVALID_TYPE);
4787 cur = valuePop(ctxt);
4788
4789 if (cur->nodesetval->nodeNr == 0) {
4790 valuePush(ctxt, xmlXPathNewFloat(0.0));
4791 } else {
4792 valuePush(ctxt,
4793 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[0]));
4794 xmlXPathNumberFunction(ctxt, 1);
4795 for (i = 1; i < cur->nodesetval->nodeNr; i++) {
4796 valuePush(ctxt,
4797 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
4798 xmlXPathAddValues(ctxt);
4799 }
4800 }
4801 xmlXPathFreeObject(cur);
4802}
4803
4804/**
4805 * xmlXPathFloorFunction:
4806 * @ctxt: the XPath Parser context
4807 * @nargs: the number of arguments
4808 *
4809 * Implement the floor() XPath function
4810 * number floor(number)
4811 * The floor function returns the largest (closest to positive infinity)
4812 * number that is not greater than the argument and that is an integer.
4813 */
4814void
4815xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4816 CHECK_ARITY(1);
4817 CAST_TO_NUMBER;
4818 CHECK_TYPE(XPATH_NUMBER);
4819#if 0
4820 ctxt->value->floatval = floor(ctxt->value->floatval);
4821#else
4822 /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
4823 ctxt->value->floatval = (double)((int) ctxt->value->floatval);
4824#endif
4825}
4826
4827/**
4828 * xmlXPathCeilingFunction:
4829 * @ctxt: the XPath Parser context
4830 * @nargs: the number of arguments
4831 *
4832 * Implement the ceiling() XPath function
4833 * number ceiling(number)
4834 * The ceiling function returns the smallest (closest to negative infinity)
4835 * number that is not less than the argument and that is an integer.
4836 */
4837void
4838xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4839 double f;
4840
4841 CHECK_ARITY(1);
4842 CAST_TO_NUMBER;
4843 CHECK_TYPE(XPATH_NUMBER);
4844
4845#if 0
4846 ctxt->value->floatval = ceil(ctxt->value->floatval);
4847#else
4848 f = (double)((int) ctxt->value->floatval);
4849 if (f != ctxt->value->floatval)
4850 ctxt->value->floatval = f + 1;
4851#endif
4852}
4853
4854/**
4855 * xmlXPathRoundFunction:
4856 * @ctxt: the XPath Parser context
4857 * @nargs: the number of arguments
4858 *
4859 * Implement the round() XPath function
4860 * number round(number)
4861 * The round function returns the number that is closest to the
4862 * argument and that is an integer. If there are two such numbers,
4863 * then the one that is even is returned.
4864 */
4865void
4866xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
4867 double f;
4868
4869 CHECK_ARITY(1);
4870 CAST_TO_NUMBER;
4871 CHECK_TYPE(XPATH_NUMBER);
4872
4873 if ((ctxt->value->floatval == xmlXPathNAN) ||
4874 (ctxt->value->floatval == xmlXPathPINF) ||
4875 (ctxt->value->floatval == xmlXPathNINF) ||
4876 (ctxt->value->floatval == 0.0))
4877 return;
4878
4879#if 0
4880 f = floor(ctxt->value->floatval);
4881#else
4882 f = (double)((int) ctxt->value->floatval);
4883#endif
4884 if (ctxt->value->floatval < f + 0.5)
4885 ctxt->value->floatval = f;
4886 else
4887 ctxt->value->floatval = f + 1;
4888}
4889
4890/************************************************************************
4891 * *
4892 * The Parser *
4893 * *
4894 ************************************************************************/
4895
4896/*
4897 * a couple of forward declarations since we use a recursive call based
4898 * implementation.
4899 */
4900void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
4901void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
4902void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
4903#ifdef VMS
4904void xmlXPathEvalRelLocationPath(xmlXPathParserContextPtr ctxt);
4905#define xmlXPathEvalRelativeLocationPath xmlXPathEvalRelLocationPath
4906#else
4907void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
4908#endif
4909
4910/**
4911 * xmlXPathParseNCName:
4912 * @ctxt: the XPath Parser context
4913 *
4914 * parse an XML namespace non qualified name.
4915 *
4916 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
4917 *
4918 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
4919 * CombiningChar | Extender
4920 *
4921 * Returns the namespace name or NULL
4922 */
4923
4924xmlChar *
4925xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
4926 const xmlChar *q;
4927 xmlChar *ret = NULL;
4928
4929 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
4930 q = NEXT;
4931
4932 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
4933 (CUR == '.') || (CUR == '-') ||
4934 (CUR == '_') ||
4935 (IS_COMBINING(CUR)) ||
4936 (IS_EXTENDER(CUR)))
4937 NEXT;
4938
4939 ret = xmlStrndup(q, CUR_PTR - q);
4940
4941 return(ret);
4942}
4943
4944/**
4945 * xmlXPathParseQName:
4946 * @ctxt: the XPath Parser context
4947 * @prefix: a xmlChar **
4948 *
4949 * parse an XML qualified name
4950 *
4951 * [NS 5] QName ::= (Prefix ':')? LocalPart
4952 *
4953 * [NS 6] Prefix ::= NCName
4954 *
4955 * [NS 7] LocalPart ::= NCName
4956 *
4957 * Returns the function returns the local part, and prefix is updated
4958 * to get the Prefix if any.
4959 */
4960
4961xmlChar *
4962xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
4963 xmlChar *ret = NULL;
4964
4965 *prefix = NULL;
4966 ret = xmlXPathParseNCName(ctxt);
4967 if (CUR == ':') {
4968 *prefix = ret;
4969 NEXT;
4970 ret = xmlXPathParseNCName(ctxt);
4971 }
4972 return(ret);
4973}
4974
4975/**
4976 * xmlXPathParseName:
4977 * @ctxt: the XPath Parser context
4978 *
4979 * parse an XML name
4980 *
4981 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
4982 * CombiningChar | Extender
4983 *
4984 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
4985 *
4986 * Returns the namespace name or NULL
4987 */
4988
4989xmlChar *
4990xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
4991 const xmlChar *q;
4992 xmlChar *ret = NULL;
4993
4994 if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
4995 q = NEXT;
4996
4997 /* TODO Make this UTF8 compliant !!! */
4998 while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
4999 (CUR == '.') || (CUR == '-') ||
5000 (CUR == '_') || (CUR == ':') ||
5001 (IS_COMBINING(CUR)) ||
5002 (IS_EXTENDER(CUR)))
5003 NEXT;
5004
5005 ret = xmlStrndup(q, CUR_PTR - q);
5006
5007 return(ret);
5008}
5009
5010/**
5011 * xmlXPathStringEvalNumber:
5012 * @str: A string to scan
5013 *
5014 * [30] Number ::= Digits ('.' Digits?)?
5015 * | '.' Digits
5016 * [31] Digits ::= [0-9]+
5017 *
5018 * Parse and evaluate a Number in the string
5019 * In complement of the Number expression, this function also handles
5020 * negative values : '-' Number.
5021 *
5022 * Returns the double value.
5023 */
5024double
5025xmlXPathStringEvalNumber(const xmlChar *str) {
5026 const xmlChar *cur = str;
5027 double ret = 0.0;
5028 double mult = 1;
5029 int ok = 0;
5030 int isneg = 0;
5031
5032 while (IS_BLANK(*cur)) cur++;
5033 if ((*cur != '.') && ((*cur < '0') || (*cur > '9')) && (*cur != '-')) {
5034 return(xmlXPathNAN);
5035 }
5036 if (*cur == '-') {
5037 isneg = 1;
5038 cur++;
5039 }
5040 while ((*cur >= '0') && (*cur <= '9')) {
5041 ret = ret * 10 + (*cur - '0');
5042 ok = 1;
5043 cur++;
5044 }
5045 if (*cur == '.') {
5046 cur++;
5047 if (((*cur < '0') || (*cur > '9')) && (!ok)) {
5048 return(xmlXPathNAN);
5049 }
5050 while ((*cur >= '0') && (*cur <= '9')) {
5051 mult /= 10;
5052 ret = ret + (*cur - '0') * mult;
5053 cur++;
5054 }
5055 }
5056 while (IS_BLANK(*cur)) cur++;
5057 if (*cur != 0) return(xmlXPathNAN);
5058 if (isneg) ret = -ret;
5059 return(ret);
5060}
5061
5062/**
5063 * xmlXPathEvalNumber:
5064 * @ctxt: the XPath Parser context
5065 *
5066 * [30] Number ::= Digits ('.' Digits?)?
5067 * | '.' Digits
5068 * [31] Digits ::= [0-9]+
5069 *
5070 * Parse and evaluate a Number, then push it on the stack
5071 *
5072 */
5073void
5074xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
5075 double ret = 0.0;
5076 double mult = 1;
5077 int ok = 0;
5078
5079 CHECK_ERROR;
5080 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
5081 XP_ERROR(XPATH_NUMBER_ERROR);
5082 }
5083 while ((CUR >= '0') && (CUR <= '9')) {
5084 ret = ret * 10 + (CUR - '0');
5085 ok = 1;
5086 NEXT;
5087 }
5088 if (CUR == '.') {
5089 NEXT;
5090 if (((CUR < '0') || (CUR > '9')) && (!ok)) {
5091 XP_ERROR(XPATH_NUMBER_ERROR);
5092 }
5093 while ((CUR >= '0') && (CUR <= '9')) {
5094 mult /= 10;
5095 ret = ret + (CUR - '0') * mult;
5096 NEXT;
5097 }
5098 }
5099 valuePush(ctxt, xmlXPathNewFloat(ret));
5100}
5101
5102/**
5103 * xmlXPathEvalLiteral:
5104 * @ctxt: the XPath Parser context
5105 *
5106 * Parse a Literal and push it on the stack.
5107 *
5108 * [29] Literal ::= '"' [^"]* '"'
5109 * | "'" [^']* "'"
5110 *
5111 * TODO: xmlXPathEvalLiteral memory allocation could be improved.
5112 */
5113void
5114xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
5115 const xmlChar *q;
5116 xmlChar *ret = NULL;
5117
5118 if (CUR == '"') {
5119 NEXT;
5120 q = CUR_PTR;
5121 while ((IS_CHAR(CUR)) && (CUR != '"'))
5122 NEXT;
5123 if (!IS_CHAR(CUR)) {
5124 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5125 } else {
5126 ret = xmlStrndup(q, CUR_PTR - q);
5127 NEXT;
5128 }
5129 } else if (CUR == '\'') {
5130 NEXT;
5131 q = CUR_PTR;
5132 while ((IS_CHAR(CUR)) && (CUR != '\''))
5133 NEXT;
5134 if (!IS_CHAR(CUR)) {
5135 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
5136 } else {
5137 ret = xmlStrndup(q, CUR_PTR - q);
5138 NEXT;
5139 }
5140 } else {
5141 XP_ERROR(XPATH_START_LITERAL_ERROR);
5142 }
5143 if (ret == NULL) return;
5144 valuePush(ctxt, xmlXPathNewString(ret));
5145 xmlFree(ret);
5146}
5147
5148/**
5149 * xmlXPathEvalVariableReference:
5150 * @ctxt: the XPath Parser context
5151 *
5152 * Parse a VariableReference, evaluate it and push it on the stack.
5153 *
5154 * The variable bindings consist of a mapping from variable names
5155 * to variable values. The value of a variable is an object, which
5156 * of any of the types that are possible for the value of an expression,
5157 * and may also be of additional types not specified here.
5158 *
5159 * Early evaluation is possible since:
5160 * The variable bindings [...] used to evaluate a subexpression are
5161 * always the same as those used to evaluate the containing expression.
5162 *
5163 * [36] VariableReference ::= '$' QName
5164 */
5165void
5166xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
5167 xmlChar *name;
5168 xmlChar *prefix;
5169 xmlXPathObjectPtr value;
5170
5171 SKIP_BLANKS;
5172 if (CUR != '$') {
5173 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5174 }
5175 NEXT;
5176 name = xmlXPathParseQName(ctxt, &prefix);
5177 if (name == NULL) {
5178 XP_ERROR(XPATH_VARIABLE_REF_ERROR);
5179 }
5180 if (prefix == NULL) {
5181 value = xmlXPathVariableLookup(ctxt->context, name);
5182 } else {
5183 TODO;
5184 value = NULL;
5185 }
5186 xmlFree(name);
5187 if (prefix != NULL) xmlFree(prefix);
5188 if (value == NULL) {
5189 XP_ERROR(XPATH_UNDEF_VARIABLE_ERROR);
5190 }
5191 valuePush(ctxt, value);
5192 SKIP_BLANKS;
5193}
5194
5195/**
5196 * xmlXPathIsNodeType:
5197 * @ctxt: the XPath Parser context
5198 * @name: a name string
5199 *
5200 * Is the name given a NodeType one.
5201 *
5202 * [38] NodeType ::= 'comment'
5203 * | 'text'
5204 * | 'processing-instruction'
5205 * | 'node'
5206 *
5207 * Returns 1 if true 0 otherwise
5208 */
5209int
5210xmlXPathIsNodeType(const xmlChar *name) {
5211 if (name == NULL)
5212 return(0);
5213
5214 if (xmlStrEqual(name, BAD_CAST "comment"))
5215 return(1);
5216 if (xmlStrEqual(name, BAD_CAST "text"))
5217 return(1);
5218 if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
5219 return(1);
5220 if (xmlStrEqual(name, BAD_CAST "node"))
5221 return(1);
5222 return(0);
5223}
5224
5225/**
5226 * xmlXPathEvalFunctionCall:
5227 * @ctxt: the XPath Parser context
5228 *
5229 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
5230 * [17] Argument ::= Expr
5231 *
5232 * Parse and evaluate a function call, the evaluation of all arguments are
5233 * pushed on the stack
5234 */
5235void
5236xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
5237 xmlChar *name;
5238 xmlChar *prefix;
5239 xmlXPathFunction func;
5240 int nbargs = 0;
5241
5242 name = xmlXPathParseQName(ctxt, &prefix);
5243 if (name == NULL) {
5244 XP_ERROR(XPATH_EXPR_ERROR);
5245 }
5246 SKIP_BLANKS;
5247 if (prefix == NULL) {
5248 func = xmlXPathFunctionLookup(ctxt->context, name);
5249 } else {
5250 TODO;
5251 func = NULL;
5252 }
5253 if (func == NULL) {
5254 xmlFree(name);
5255 if (prefix != NULL) xmlFree(prefix);
5256 XP_ERROR(XPATH_UNKNOWN_FUNC_ERROR);
5257 }
5258#ifdef DEBUG_EXPR
5259 if (prefix == NULL)
5260 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
5261 name);
5262 else
5263 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
5264 prefix, name);
5265#endif
5266
5267 xmlFree(name);
5268 if (prefix != NULL) xmlFree(prefix);
5269
5270 if (CUR != '(') {
5271 XP_ERROR(XPATH_EXPR_ERROR);
5272 }
5273 NEXT;
5274 SKIP_BLANKS;
5275
5276 while (CUR != ')') {
5277 xmlXPathEvalExpr(ctxt);
5278 nbargs++;
5279 if (CUR == ')') break;
5280 if (CUR != ',') {
5281 XP_ERROR(XPATH_EXPR_ERROR);
5282 }
5283 NEXT;
5284 SKIP_BLANKS;
5285 }
5286 NEXT;
5287 SKIP_BLANKS;
5288 func(ctxt, nbargs);
5289}
5290
5291/**
5292 * xmlXPathEvalPrimaryExpr:
5293 * @ctxt: the XPath Parser context
5294 *
5295 * [15] PrimaryExpr ::= VariableReference
5296 * | '(' Expr ')'
5297 * | Literal
5298 * | Number
5299 * | FunctionCall
5300 *
5301 * Parse and evaluate a primary expression, then push the result on the stack
5302 */
5303void
5304xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
5305 SKIP_BLANKS;
5306 if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
5307 else if (CUR == '(') {
5308 NEXT;
5309 SKIP_BLANKS;
5310 xmlXPathEvalExpr(ctxt);
5311 if (CUR != ')') {
5312 XP_ERROR(XPATH_EXPR_ERROR);
5313 }
5314 NEXT;
5315 SKIP_BLANKS;
5316 } else if (IS_DIGIT(CUR)) {
5317 xmlXPathEvalNumber(ctxt);
5318 } else if ((CUR == '\'') || (CUR == '"')) {
5319 xmlXPathEvalLiteral(ctxt);
5320 } else {
5321 xmlXPathEvalFunctionCall(ctxt);
5322 }
5323 SKIP_BLANKS;
5324}
5325
5326/**
5327 * xmlXPathEvalFilterExpr:
5328 * @ctxt: the XPath Parser context
5329 *
5330 * [20] FilterExpr ::= PrimaryExpr
5331 * | FilterExpr Predicate
5332 *
5333 * Parse and evaluate a filter expression, then push the result on the stack
5334 * Square brackets are used to filter expressions in the same way that
5335 * they are used in location paths. It is an error if the expression to
5336 * be filtered does not evaluate to a node-set. The context node list
5337 * used for evaluating the expression in square brackets is the node-set
5338 * to be filtered listed in document order.
5339 */
5340
5341void
5342xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
5343 xmlXPathEvalPrimaryExpr(ctxt);
5344 CHECK_ERROR;
5345 SKIP_BLANKS;
5346
5347 while (CUR == '[') {
5348 if ((ctxt->value == NULL) ||
5349 ((ctxt->value->type != XPATH_NODESET) &&
5350 (ctxt->value->type != XPATH_LOCATIONSET)))
5351 XP_ERROR(XPATH_INVALID_TYPE)
5352
5353 if (ctxt->value->type == XPATH_NODESET)
5354 xmlXPathEvalPredicate(ctxt);
5355 else
5356 xmlXPtrEvalRangePredicate(ctxt);
5357 SKIP_BLANKS;
5358 }
5359
5360
5361}
5362
5363/**
5364 * xmlXPathScanName:
5365 * @ctxt: the XPath Parser context
5366 *
5367 * Trickery: parse an XML name but without consuming the input flow
5368 * Needed to avoid insanity in the parser state.
5369 *
5370 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
5371 * CombiningChar | Extender
5372 *
5373 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
5374 *
5375 * [6] Names ::= Name (S Name)*
5376 *
5377 * Returns the Name parsed or NULL
5378 */
5379
5380xmlChar *
5381xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
5382 xmlChar buf[XML_MAX_NAMELEN];
5383 int len = 0;
5384
5385 SKIP_BLANKS;
5386 if (!IS_LETTER(CUR) && (CUR != '_') &&
5387 (CUR != ':')) {
5388 return(NULL);
5389 }
5390
5391 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5392 (NXT(len) == '.') || (NXT(len) == '-') ||
5393 (NXT(len) == '_') || (NXT(len) == ':') ||
5394 (IS_COMBINING(NXT(len))) ||
5395 (IS_EXTENDER(NXT(len)))) {
5396 buf[len] = NXT(len);
5397 len++;
5398 if (len >= XML_MAX_NAMELEN) {
5399 xmlGenericError(xmlGenericErrorContext,
5400 "xmlScanName: reached XML_MAX_NAMELEN limit\n");
5401 while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
5402 (NXT(len) == '.') || (NXT(len) == '-') ||
5403 (NXT(len) == '_') || (NXT(len) == ':') ||
5404 (IS_COMBINING(NXT(len))) ||
5405 (IS_EXTENDER(NXT(len))))
5406 len++;
5407 break;
5408 }
5409 }
5410 return(xmlStrndup(buf, len));
5411}
5412
5413/**
5414 * xmlXPathEvalPathExpr:
5415 * @ctxt: the XPath Parser context
5416 *
5417 * [19] PathExpr ::= LocationPath
5418 * | FilterExpr
5419 * | FilterExpr '/' RelativeLocationPath
5420 * | FilterExpr '//' RelativeLocationPath
5421 *
5422 * Parse and evaluate a path expression, then push the result on the stack
5423 * The / operator and // operators combine an arbitrary expression
5424 * and a relative location path. It is an error if the expression
5425 * does not evaluate to a node-set.
5426 * The / operator does composition in the same way as when / is
5427 * used in a location path. As in location paths, // is short for
5428 * /descendant-or-self::node()/.
5429 */
5430
5431void
5432xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
5433 int lc = 1; /* Should we branch to LocationPath ? */
5434 xmlChar *name = NULL; /* we may have to preparse a name to find out */
5435
5436 SKIP_BLANKS;
5437 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
5438 (CUR == '\'') || (CUR == '"')) {
5439 lc = 0;
5440 } else if (CUR == '*') {
5441 /* relative or absolute location path */
5442 lc = 1;
5443 } else if (CUR == '/') {
5444 /* relative or absolute location path */
5445 lc = 1;
5446 } else if (CUR == '@') {
5447 /* relative abbreviated attribute location path */
5448 lc = 1;
5449 } else if (CUR == '.') {
5450 /* relative abbreviated attribute location path */
5451 lc = 1;
5452 } else {
5453 /*
5454 * Problem is finding if we have a name here whether it's:
5455 * - a nodetype
5456 * - a function call in which case it's followed by '('
5457 * - an axis in which case it's followed by ':'
5458 * - a element name
5459 * We do an a priori analysis here rather than having to
5460 * maintain parsed token content through the recursive function
5461 * calls. This looks uglier but makes the code quite easier to
5462 * read/write/debug.
5463 */
5464 SKIP_BLANKS;
5465 name = xmlXPathScanName(ctxt);
5466 if ((name != NULL) && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
5467#ifdef DEBUG_STEP
5468 xmlGenericError(xmlGenericErrorContext,
5469 "PathExpr: Axis\n");
5470#endif
5471 lc = 1;
5472 xmlFree(name);
5473 } else if (name != NULL) {
5474 int len =xmlStrlen(name);
5475 int blank = 0;
5476
5477
5478 while (NXT(len) != 0) {
5479 if (NXT(len) == '/') {
5480 /* element name */
5481#ifdef DEBUG_STEP
5482 xmlGenericError(xmlGenericErrorContext,
5483 "PathExpr: AbbrRelLocation\n");
5484#endif
5485 lc = 1;
5486 break;
5487 } else if (IS_BLANK(NXT(len))) {
5488 /* skip to next */
5489 blank = 1;
5490 } else if (NXT(len) == ':') {
5491#ifdef DEBUG_STEP
5492 xmlGenericError(xmlGenericErrorContext,
5493 "PathExpr: AbbrRelLocation\n");
5494#endif
5495 lc = 1;
5496 break;
5497 } else if ((NXT(len) == '(')) {
5498 /* Note Type or Function */
5499 if (xmlXPathIsNodeType(name)) {
5500#ifdef DEBUG_STEP
5501 xmlGenericError(xmlGenericErrorContext,
5502 "PathExpr: Type search\n");
5503#endif
5504 lc = 1;
5505 } else {
5506#ifdef DEBUG_STEP
5507 xmlGenericError(xmlGenericErrorContext,
5508 "PathExpr: function call\n");
5509#endif
5510 lc = 0;
5511 }
5512 break;
5513 } else if ((NXT(len) == '[')) {
5514 /* element name */
5515#ifdef DEBUG_STEP
5516 xmlGenericError(xmlGenericErrorContext,
5517 "PathExpr: AbbrRelLocation\n");
5518#endif
5519 lc = 1;
5520 break;
5521 } else if ((NXT(len) == '<') || (NXT(len) == '>') ||
5522 (NXT(len) == '=')) {
5523 lc = 1;
5524 break;
5525 } else {
5526 lc = 1;
5527 break;
5528 }
5529 len++;
5530 }
5531 if (NXT(len) == 0) {
5532#ifdef DEBUG_STEP
5533 xmlGenericError(xmlGenericErrorContext,
5534 "PathExpr: AbbrRelLocation\n");
5535#endif
5536 /* element name */
5537 lc = 1;
5538 }
5539 xmlFree(name);
5540 } else {
5541 /* make sure all cases are covered explicitely */
5542 XP_ERROR(XPATH_EXPR_ERROR);
5543 }
5544 }
5545
5546 if (lc) {
5547 if (CUR == '/')
5548 xmlXPathRoot(ctxt);
5549 else {
5550 /* TAG:9999 */
5551 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
5552 }
5553 xmlXPathEvalLocationPath(ctxt);
5554 } else {
5555 xmlXPathEvalFilterExpr(ctxt);
5556 CHECK_ERROR;
5557 if ((CUR == '/') && (NXT(1) == '/')) {
5558 SKIP(2);
5559 SKIP_BLANKS;
5560 xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
5561 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
5562 ctxt->context->node = NULL;
5563 xmlXPathEvalRelativeLocationPath(ctxt);
5564 } else if (CUR == '/') {
5565 xmlXPathEvalRelativeLocationPath(ctxt);
5566 }
5567 }
5568 SKIP_BLANKS;
5569}
5570
5571/**
5572 * xmlXPathEvalUnionExpr:
5573 * @ctxt: the XPath Parser context
5574 *
5575 * [18] UnionExpr ::= PathExpr
5576 * | UnionExpr '|' PathExpr
5577 *
5578 * Parse and evaluate an union expression, then push the result on the stack
5579 */
5580
5581void
5582xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
5583 int sort = 0;
5584 xmlXPathEvalPathExpr(ctxt);
5585 CHECK_ERROR;
5586 SKIP_BLANKS;
5587 while (CUR == '|') {
5588 xmlXPathObjectPtr obj1,obj2, tmp;
5589
5590 sort = 1;
5591 CHECK_TYPE(XPATH_NODESET);
5592 obj1 = valuePop(ctxt);
5593 tmp = xmlXPathNewNodeSet(ctxt->context->node);
5594 valuePush(ctxt, tmp);
5595
5596 NEXT;
5597 SKIP_BLANKS;
5598 xmlXPathEvalPathExpr(ctxt);
5599
5600 CHECK_TYPE(XPATH_NODESET);
5601 obj2 = valuePop(ctxt);
5602 obj1->nodesetval = xmlXPathNodeSetMerge(obj1->nodesetval,
5603 obj2->nodesetval);
5604 if (ctxt->value == tmp) {
5605 tmp = valuePop(ctxt);
5606 xmlXPathFreeObject(tmp);
5607 }
5608 valuePush(ctxt, obj1);
5609 xmlXPathFreeObject(obj2);
5610 SKIP_BLANKS;
5611 }
5612 if (sort) {
5613 }
5614}
5615
5616/**
5617 * xmlXPathEvalUnaryExpr:
5618 * @ctxt: the XPath Parser context
5619 *
5620 * [27] UnaryExpr ::= UnionExpr
5621 * | '-' UnaryExpr
5622 *
5623 * Parse and evaluate an unary expression, then push the result on the stack
5624 */
5625
5626void
5627xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
5628 int minus = 0;
5629
5630 SKIP_BLANKS;
5631 if (CUR == '-') {
5632 minus = 1;
5633 NEXT;
5634 SKIP_BLANKS;
5635 }
5636 xmlXPathEvalUnionExpr(ctxt);
5637 CHECK_ERROR;
5638 if (minus) {
5639 xmlXPathValueFlipSign(ctxt);
5640 }
5641}
5642
5643/**
5644 * xmlXPathEvalMultiplicativeExpr:
5645 * @ctxt: the XPath Parser context
5646 *
5647 * [26] MultiplicativeExpr ::= UnaryExpr
5648 * | MultiplicativeExpr MultiplyOperator UnaryExpr
5649 * | MultiplicativeExpr 'div' UnaryExpr
5650 * | MultiplicativeExpr 'mod' UnaryExpr
5651 * [34] MultiplyOperator ::= '*'
5652 *
5653 * Parse and evaluate an Additive expression, then push the result on the stack
5654 */
5655
5656void
5657xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
5658 xmlXPathEvalUnaryExpr(ctxt);
5659 CHECK_ERROR;
5660 SKIP_BLANKS;
5661 while ((CUR == '*') ||
5662 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
5663 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
5664 int op = -1;
5665
5666 if (CUR == '*') {
5667 op = 0;
5668 NEXT;
5669 } else if (CUR == 'd') {
5670 op = 1;
5671 SKIP(3);
5672 } else if (CUR == 'm') {
5673 op = 2;
5674 SKIP(3);
5675 }
5676 SKIP_BLANKS;
5677 xmlXPathEvalUnaryExpr(ctxt);
5678 CHECK_ERROR;
5679 switch (op) {
5680 case 0:
5681 xmlXPathMultValues(ctxt);
5682 break;
5683 case 1:
5684 xmlXPathDivValues(ctxt);
5685 break;
5686 case 2:
5687 xmlXPathModValues(ctxt);
5688 break;
5689 }
5690 SKIP_BLANKS;
5691 }
5692}
5693
5694/**
5695 * xmlXPathEvalAdditiveExpr:
5696 * @ctxt: the XPath Parser context
5697 *
5698 * [25] AdditiveExpr ::= MultiplicativeExpr
5699 * | AdditiveExpr '+' MultiplicativeExpr
5700 * | AdditiveExpr '-' MultiplicativeExpr
5701 *
5702 * Parse and evaluate an Additive expression, then push the result on the stack
5703 */
5704
5705void
5706xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
5707 xmlXPathEvalMultiplicativeExpr(ctxt);
5708 CHECK_ERROR;
5709 SKIP_BLANKS;
5710 while ((CUR == '+') || (CUR == '-')) {
5711 int plus;
5712
5713 if (CUR == '+') plus = 1;
5714 else plus = 0;
5715 NEXT;
5716 SKIP_BLANKS;
5717 xmlXPathEvalMultiplicativeExpr(ctxt);
5718 CHECK_ERROR;
5719 if (plus) xmlXPathAddValues(ctxt);
5720 else xmlXPathSubValues(ctxt);
5721 SKIP_BLANKS;
5722 }
5723}
5724
5725/**
5726 * xmlXPathEvalRelationalExpr:
5727 * @ctxt: the XPath Parser context
5728 *
5729 * [24] RelationalExpr ::= AdditiveExpr
5730 * | RelationalExpr '<' AdditiveExpr
5731 * | RelationalExpr '>' AdditiveExpr
5732 * | RelationalExpr '<=' AdditiveExpr
5733 * | RelationalExpr '>=' AdditiveExpr
5734 *
5735 * A <= B > C is allowed ? Answer from James, yes with
5736 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
5737 * which is basically what got implemented.
5738 *
5739 * Parse and evaluate a Relational expression, then push the result
5740 * on the stack
5741 */
5742
5743void
5744xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
5745 xmlXPathEvalAdditiveExpr(ctxt);
5746 CHECK_ERROR;
5747 SKIP_BLANKS;
5748 while ((CUR == '<') ||
5749 (CUR == '>') ||
5750 ((CUR == '<') && (NXT(1) == '=')) ||
5751 ((CUR == '>') && (NXT(1) == '='))) {
5752 int inf, strict, ret;
5753
5754 if (CUR == '<') inf = 1;
5755 else inf = 0;
5756 if (NXT(1) == '=') strict = 0;
5757 else strict = 1;
5758 NEXT;
5759 if (!strict) NEXT;
5760 SKIP_BLANKS;
5761 xmlXPathEvalAdditiveExpr(ctxt);
5762 CHECK_ERROR;
5763 ret = xmlXPathCompareValues(ctxt, inf, strict);
5764 valuePush(ctxt, xmlXPathNewBoolean(ret));
5765 SKIP_BLANKS;
5766 }
5767}
5768
5769/**
5770 * xmlXPathEvalEqualityExpr:
5771 * @ctxt: the XPath Parser context
5772 *
5773 * [23] EqualityExpr ::= RelationalExpr
5774 * | EqualityExpr '=' RelationalExpr
5775 * | EqualityExpr '!=' RelationalExpr
5776 *
5777 * A != B != C is allowed ? Answer from James, yes with
5778 * (RelationalExpr = RelationalExpr) = RelationalExpr
5779 * (RelationalExpr != RelationalExpr) != RelationalExpr
5780 * which is basically what got implemented.
5781 *
5782 * Parse and evaluate an Equality expression, then push the result on the stack
5783 *
5784 */
5785void
5786xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
5787 xmlXPathEvalRelationalExpr(ctxt);
5788 CHECK_ERROR;
5789 SKIP_BLANKS;
5790 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
5791 xmlXPathObjectPtr res;
5792 int eq, equal;
5793
5794 if (CUR == '=') eq = 1;
5795 else eq = 0;
5796 NEXT;
5797 if (!eq) NEXT;
5798 SKIP_BLANKS;
5799 xmlXPathEvalRelationalExpr(ctxt);
5800 CHECK_ERROR;
5801 equal = xmlXPathEqualValues(ctxt);
5802 if (eq) res = xmlXPathNewBoolean(equal);
5803 else res = xmlXPathNewBoolean(!equal);
5804 valuePush(ctxt, res);
5805 SKIP_BLANKS;
5806 }
5807}
5808
5809/**
5810 * xmlXPathEvalAndExpr:
5811 * @ctxt: the XPath Parser context
5812 *
5813 * [22] AndExpr ::= EqualityExpr
5814 * | AndExpr 'and' EqualityExpr
5815 *
5816 * Parse and evaluate an AND expression, then push the result on the stack
5817 *
5818 */
5819void
5820xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
5821 xmlXPathEvalEqualityExpr(ctxt);
5822 CHECK_ERROR;
5823 SKIP_BLANKS;
5824 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
5825 xmlXPathObjectPtr arg1, arg2;
5826
5827 SKIP(3);
5828 SKIP_BLANKS;
5829 xmlXPathEvalEqualityExpr(ctxt);
5830 CHECK_ERROR;
5831 xmlXPathBooleanFunction(ctxt, 1);
5832 arg2 = valuePop(ctxt);
5833 xmlXPathBooleanFunction(ctxt, 1);
5834 arg1 = valuePop(ctxt);
5835 arg1->boolval &= arg2->boolval;
5836 valuePush(ctxt, arg1);
5837 xmlXPathFreeObject(arg2);
5838 SKIP_BLANKS;
5839 }
5840}
5841
5842/**
5843 * xmlXPathEvalExpr:
5844 * @ctxt: the XPath Parser context
5845 *
5846 * [14] Expr ::= OrExpr
5847 * [21] OrExpr ::= AndExpr
5848 * | OrExpr 'or' AndExpr
5849 *
5850 * Parse and evaluate an expression, then push the result on the stack
5851 *
5852 */
5853void
5854xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
5855 xmlXPathEvalAndExpr(ctxt);
5856 CHECK_ERROR;
5857 SKIP_BLANKS;
5858 while ((CUR == 'o') && (NXT(1) == 'r')) {
5859 xmlXPathObjectPtr arg1, arg2;
5860
5861 SKIP(2);
5862 SKIP_BLANKS;
5863 xmlXPathEvalAndExpr(ctxt);
5864 CHECK_ERROR;
5865 xmlXPathBooleanFunction(ctxt, 1);
5866 arg2 = valuePop(ctxt);
5867 xmlXPathBooleanFunction(ctxt, 1);
5868 arg1 = valuePop(ctxt);
5869 arg1->boolval |= arg2->boolval;
5870 valuePush(ctxt, arg1);
5871 xmlXPathFreeObject(arg2);
5872 SKIP_BLANKS;
5873 }
5874 if ((ctxt->value != NULL) && (ctxt->value->type == XPATH_NODESET) &&
5875 (ctxt->value->nodesetval != NULL))
5876 xmlXPathNodeSetSort(ctxt->value->nodesetval);
5877}
5878
5879/**
5880 * xmlXPathEvaluatePredicateResult:
5881 * @ctxt: the XPath Parser context
5882 * @res: the Predicate Expression evaluation result
5883 *
5884 * Evaluate a predicate result for the current node.
5885 * A PredicateExpr is evaluated by evaluating the Expr and converting
5886 * the result to a boolean. If the result is a number, the result will
5887 * be converted to true if the number is equal to the position of the
5888 * context node in the context node list (as returned by the position
5889 * function) and will be converted to false otherwise; if the result
5890 * is not a number, then the result will be converted as if by a call
5891 * to the boolean function.
5892 *
5893 * Return 1 if predicate is true, 0 otherwise
5894 */
5895int
5896xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
5897 xmlXPathObjectPtr res) {
5898 if (res == NULL) return(0);
5899 switch (res->type) {
5900 case XPATH_BOOLEAN:
5901 return(res->boolval);
5902 case XPATH_NUMBER:
5903 return(res->floatval == ctxt->context->proximityPosition);
5904 case XPATH_NODESET:
5905 case XPATH_XSLT_TREE:
5906 return(res->nodesetval->nodeNr != 0);
5907 case XPATH_STRING:
5908 return((res->stringval != NULL) &&
5909 (xmlStrlen(res->stringval) != 0));
5910 default:
5911 STRANGE
5912 }
5913 return(0);
5914}
5915
5916/**
5917 * xmlXPathEvalPredicate:
5918 * @ctxt: the XPath Parser context
5919 *
5920 * [8] Predicate ::= '[' PredicateExpr ']'
5921 * [9] PredicateExpr ::= Expr
5922 *
5923 * ---------------------
5924 * For each node in the node-set to be filtered, the PredicateExpr is
5925 * evaluated with that node as the context node, with the number of nodes
5926 * in the node-set as the context size, and with the proximity position
5927 * of the node in the node-set with respect to the axis as the context
5928 * position; if PredicateExpr evaluates to true for that node, the node
5929 * is included in the new node-set; otherwise, it is not included.
5930 * ---------------------
5931 *
5932 * Parse and evaluate a predicate for all the elements of the
5933 * current node list. Then refine the list by removing all
5934 * nodes where the predicate is false.
5935 */
5936void
5937xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
5938 const xmlChar *cur;
5939 xmlXPathObjectPtr res;
5940 xmlXPathObjectPtr obj, tmp;
5941 xmlNodeSetPtr newset = NULL;
5942 xmlNodeSetPtr oldset;
5943 xmlNodePtr oldnode;
5944 int i;
5945
5946 SKIP_BLANKS;
5947 if (CUR != '[') {
5948 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
5949 }
5950 NEXT;
5951 SKIP_BLANKS;
5952
5953 /*
5954 * Extract the old set, and then evaluate the result of the
5955 * expression for all the element in the set. use it to grow
5956 * up a new set.
5957 */
5958 CHECK_TYPE(XPATH_NODESET);
5959 obj = valuePop(ctxt);
5960 oldset = obj->nodesetval;
5961 oldnode = ctxt->context->node;
5962 ctxt->context->node = NULL;
5963
5964 if ((oldset == NULL) || (oldset->nodeNr == 0)) {
5965 ctxt->context->contextSize = 0;
5966 ctxt->context->proximityPosition = 0;
5967 xmlXPathEvalExpr(ctxt);
5968 res = valuePop(ctxt);
5969 if (res != NULL)
5970 xmlXPathFreeObject(res);
5971 valuePush(ctxt, obj);
5972 CHECK_ERROR;
5973 } else {
5974 /*
5975 * Save the expression pointer since we will have to evaluate
5976 * it multiple times. Initialize the new set.
5977 */
5978 cur = ctxt->cur;
5979 newset = xmlXPathNodeSetCreate(NULL);
5980
5981 for (i = 0; i < oldset->nodeNr; i++) {
5982 ctxt->cur = cur;
5983
5984 /*
5985 * Run the evaluation with a node list made of a single item
5986 * in the nodeset.
5987 */
5988 ctxt->context->node = oldset->nodeTab[i];
5989 tmp = xmlXPathNewNodeSet(ctxt->context->node);
5990 valuePush(ctxt, tmp);
5991 ctxt->context->contextSize = oldset->nodeNr;
5992 ctxt->context->proximityPosition = i + 1;
5993
5994 xmlXPathEvalExpr(ctxt);
5995 CHECK_ERROR;
5996
5997 /*
5998 * The result of the evaluation need to be tested to
5999 * decided whether the filter succeeded or not
6000 */
6001 res = valuePop(ctxt);
6002 if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
6003 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
6004 }
6005
6006 /*
6007 * Cleanup
6008 */
6009 if (res != NULL)
6010 xmlXPathFreeObject(res);
6011 if (ctxt->value == tmp) {
6012 res = valuePop(ctxt);
6013 xmlXPathFreeObject(res);
6014 }
6015
6016 ctxt->context->node = NULL;
6017 }
6018
6019 /*
6020 * The result is used as the new evaluation set.
6021 */
6022 xmlXPathFreeObject(obj);
6023 ctxt->context->node = NULL;
6024 ctxt->context->contextSize = -1;
6025 ctxt->context->proximityPosition = -1;
6026 valuePush(ctxt, xmlXPathWrapNodeSet(newset));
6027 }
6028 if (CUR != ']') {
6029 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
6030 }
6031
6032 NEXT;
6033 SKIP_BLANKS;
6034#ifdef DEBUG_STEP
6035 xmlGenericError(xmlGenericErrorContext, "After predicate : ");
6036 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6037 ctxt->value->nodesetval);
6038#endif
6039 ctxt->context->node = oldnode;
6040}
6041
6042/**
6043 * xmlXPathEvalNodeTest:
6044 * @ctxt: the XPath Parser context
6045 * @test: pointer to a xmlXPathTestVal
6046 * @type: pointer to a xmlXPathTypeVal
6047 * @prefix: placeholder for a possible name prefix
6048 *
6049 * [7] NodeTest ::= NameTest
6050 * | NodeType '(' ')'
6051 * | 'processing-instruction' '(' Literal ')'
6052 *
6053 * [37] NameTest ::= '*'
6054 * | NCName ':' '*'
6055 * | QName
6056 * [38] NodeType ::= 'comment'
6057 * | 'text'
6058 * | 'processing-instruction'
6059 * | 'node'
6060 *
6061 * Returns the name found and update @test, @type and @prefix appropriately
6062 */
6063xmlChar *
6064xmlXPathEvalNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
6065 xmlXPathTypeVal *type, const xmlChar **prefix, xmlChar *name) {
6066 int blanks;
6067
6068 if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
6069 STRANGE;
6070 return(NULL);
6071 }
6072 *type = 0;
6073 *test = 0;
6074 *prefix = NULL;
6075 SKIP_BLANKS;
6076
6077 if ((name == NULL) && (CUR == '*')) {
6078 /*
6079 * All elements
6080 */
6081 NEXT;
6082 *test = NODE_TEST_ALL;
6083 return(NULL);
6084 }
6085
6086 if (name == NULL)
6087 name = xmlXPathParseNCName(ctxt);
6088 if (name == NULL) {
6089 XP_ERROR0(XPATH_EXPR_ERROR);
6090 }
6091
6092 blanks = IS_BLANK(CUR);
6093 SKIP_BLANKS;
6094 if (CUR == '(') {
6095 NEXT;
6096 /*
6097 * NodeType or PI search
6098 */
6099 if (xmlStrEqual(name, BAD_CAST "comment"))
6100 *type = NODE_TYPE_COMMENT;
6101 else if (xmlStrEqual(name, BAD_CAST "node"))
6102 *type = NODE_TYPE_NODE;
6103 else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
6104 *type = NODE_TYPE_PI;
6105 else if (xmlStrEqual(name, BAD_CAST "text"))
6106 *type = NODE_TYPE_TEXT;
6107 else {
6108 if (name != NULL)
6109 xmlFree(name);
6110 XP_ERROR0(XPATH_EXPR_ERROR);
6111 }
6112
6113 *test = NODE_TEST_TYPE;
6114
6115 SKIP_BLANKS;
6116 if (*type == NODE_TYPE_PI) {
6117 /*
6118 * Specific case: search a PI by name.
6119 */
6120 xmlXPathObjectPtr cur;
6121
6122 if (name != NULL)
6123 xmlFree(name);
6124
6125 xmlXPathEvalLiteral(ctxt);
6126 CHECK_ERROR 0;
6127 xmlXPathStringFunction(ctxt, 1);
6128 CHECK_ERROR0;
6129 cur = valuePop(ctxt);
6130 name = xmlStrdup(cur->stringval);
6131 xmlXPathFreeObject(cur);
6132 SKIP_BLANKS;
6133 }
6134 if (CUR != ')') {
6135 if (name != NULL)
6136 xmlFree(name);
6137 XP_ERROR0(XPATH_UNCLOSED_ERROR);
6138 }
6139 NEXT;
6140 return(name);
6141 }
6142 *test = NODE_TEST_NAME;
6143 if ((!blanks) && (CUR == ':')) {
6144 NEXT;
6145
6146 /*
6147 * get the namespace name for this prefix
6148 */
6149 *prefix = xmlXPathNsLookup(ctxt->context, name);
6150 if (name != NULL)
6151 xmlFree(name);
6152 if (*prefix == NULL) {
6153 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
6154 }
6155
6156 if (CUR == '*') {
6157 /*
6158 * All elements
6159 */
6160 NEXT;
6161 *test = NODE_TEST_ALL;
6162 return(NULL);
6163 }
6164
6165 name = xmlXPathParseNCName(ctxt);
6166 if (name == NULL) {
6167 XP_ERROR0(XPATH_EXPR_ERROR);
6168 }
6169 }
6170 return(name);
6171}
6172
6173/**
6174 * xmlXPathIsAxisName:
6175 * @name: a preparsed name token
6176 *
6177 * [6] AxisName ::= 'ancestor'
6178 * | 'ancestor-or-self'
6179 * | 'attribute'
6180 * | 'child'
6181 * | 'descendant'
6182 * | 'descendant-or-self'
6183 * | 'following'
6184 * | 'following-sibling'
6185 * | 'namespace'
6186 * | 'parent'
6187 * | 'preceding'
6188 * | 'preceding-sibling'
6189 * | 'self'
6190 *
6191 * Returns the axis or 0
6192 */
6193xmlXPathAxisVal
6194xmlXPathIsAxisName(const xmlChar *name) {
6195 xmlXPathAxisVal ret = 0;
6196 switch (name[0]) {
6197 case 'a':
6198 if (xmlStrEqual(name, BAD_CAST "ancestor"))
6199 ret = AXIS_ANCESTOR;
6200 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
6201 ret = AXIS_ANCESTOR_OR_SELF;
6202 if (xmlStrEqual(name, BAD_CAST "attribute"))
6203 ret = AXIS_ATTRIBUTE;
6204 break;
6205 case 'c':
6206 if (xmlStrEqual(name, BAD_CAST "child"))
6207 ret = AXIS_CHILD;
6208 break;
6209 case 'd':
6210 if (xmlStrEqual(name, BAD_CAST "descendant"))
6211 ret = AXIS_DESCENDANT;
6212 if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
6213 ret = AXIS_DESCENDANT_OR_SELF;
6214 break;
6215 case 'f':
6216 if (xmlStrEqual(name, BAD_CAST "following"))
6217 ret = AXIS_FOLLOWING;
6218 if (xmlStrEqual(name, BAD_CAST "following-sibling"))
6219 ret = AXIS_FOLLOWING_SIBLING;
6220 break;
6221 case 'n':
6222 if (xmlStrEqual(name, BAD_CAST "namespace"))
6223 ret = AXIS_NAMESPACE;
6224 break;
6225 case 'p':
6226 if (xmlStrEqual(name, BAD_CAST "parent"))
6227 ret = AXIS_PARENT;
6228 if (xmlStrEqual(name, BAD_CAST "preceding"))
6229 ret = AXIS_PRECEDING;
6230 if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
6231 ret = AXIS_PRECEDING_SIBLING;
6232 break;
6233 case 's':
6234 if (xmlStrEqual(name, BAD_CAST "self"))
6235 ret = AXIS_SELF;
6236 break;
6237 }
6238 return(ret);
6239}
6240
6241/**
6242 * xmlXPathEvalAxisSpecifier:
6243 * @ctxt: the XPath Parser context
6244 *
6245 *
6246 * Returns the axis found
6247 */
6248xmlXPathAxisVal
6249xmlXPathEvalAxisSpecifier(xmlXPathParserContextPtr ctxt) {
6250 xmlXPathAxisVal ret = AXIS_CHILD;
6251 int blank = 0;
6252 xmlChar *name;
6253
6254 if (CUR == '@') {
6255 NEXT;
6256 return(AXIS_ATTRIBUTE);
6257 } else {
6258 name = xmlXPathParseNCName(ctxt);
6259 if (name == NULL) {
6260 XP_ERROR0(XPATH_EXPR_ERROR);
6261 }
6262 if (IS_BLANK(CUR))
6263 blank = 1;
6264 SKIP_BLANKS;
6265 if ((CUR == ':') && (NXT(1) == ':')) {
6266 ret = xmlXPathIsAxisName(name);
6267 } else if ((blank) && (CUR == ':'))
6268 XP_ERROR0(XPATH_EXPR_ERROR);
6269
6270 xmlFree(name);
6271 }
6272 return(ret);
6273}
6274
6275/**
6276 * xmlXPathEvalStep:
6277 * @ctxt: the XPath Parser context
6278 *
6279 * [4] Step ::= AxisSpecifier NodeTest Predicate*
6280 * | AbbreviatedStep
6281 *
6282 * [12] AbbreviatedStep ::= '.' | '..'
6283 *
6284 * [5] AxisSpecifier ::= AxisName '::'
6285 * | AbbreviatedAxisSpecifier
6286 *
6287 * [13] AbbreviatedAxisSpecifier ::= '@'?
6288 *
6289 * Modified for XPtr range support as:
6290 *
6291 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
6292 * | AbbreviatedStep
6293 * | 'range-to' '(' Expr ')' Predicate*
6294 *
6295 * Evaluate one step in a Location Path
6296 * A location step of . is short for self::node(). This is
6297 * particularly useful in conjunction with //. For example, the
6298 * location path .//para is short for
6299 * self::node()/descendant-or-self::node()/child::para
6300 * and so will select all para descendant elements of the context
6301 * node.
6302 * Similarly, a location step of .. is short for parent::node().
6303 * For example, ../title is short for parent::node()/child::title
6304 * and so will select the title children of the parent of the context
6305 * node.
6306 */
6307void
6308xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
6309 SKIP_BLANKS;
6310 if ((CUR == '.') && (NXT(1) == '.')) {
6311 SKIP(2);
6312 SKIP_BLANKS;
6313 xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
6314 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6315 } else if (CUR == '.') {
6316 NEXT;
6317 SKIP_BLANKS;
6318 } else {
6319 xmlChar *name = NULL;
6320 const xmlChar *prefix = NULL;
6321 xmlXPathTestVal test;
6322 xmlXPathAxisVal axis;
6323 xmlXPathTypeVal type;
6324
6325 /*
6326 * The modification needed for XPointer change to the production
6327 */
6328#ifdef LIBXML_XPTR_ENABLED
6329 if (ctxt->context->xptr) {
6330 name = xmlXPathParseNCName(ctxt);
6331 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
6332 xmlFree(name);
6333 SKIP_BLANKS;
6334 if (CUR != '(') {
6335 XP_ERROR(XPATH_EXPR_ERROR);
6336 }
6337 NEXT;
6338 SKIP_BLANKS;
6339
6340 xmlXPtrRangeToFunction(ctxt, 1);
6341 CHECK_ERROR;
6342
6343 SKIP_BLANKS;
6344 if (CUR != ')') {
6345 XP_ERROR(XPATH_EXPR_ERROR);
6346 }
6347 NEXT;
6348 goto eval_predicates;
6349 }
6350 }
6351#endif
6352 if (name == NULL)
6353 name = xmlXPathParseNCName(ctxt);
6354 if (name != NULL) {
6355 axis = xmlXPathIsAxisName(name);
6356 if (axis != 0) {
6357 SKIP_BLANKS;
6358 if ((CUR == ':') && (NXT(1) == ':')) {
6359 SKIP(2);
6360 xmlFree(name);
6361 name = NULL;
6362 } else {
6363 /* an element name can conflict with an axis one :-\ */
6364 axis = AXIS_CHILD;
6365 }
6366 } else {
6367 axis = AXIS_CHILD;
6368 }
6369 } else if (CUR == '@') {
6370 NEXT;
6371 axis = AXIS_ATTRIBUTE;
6372 } else {
6373 axis = AXIS_CHILD;
6374 }
6375
6376 CHECK_ERROR;
6377
6378 name = xmlXPathEvalNodeTest(ctxt, &test, &type, &prefix, name);
6379 if (test == 0)
6380 return;
6381
6382#ifdef DEBUG_STEP
6383 xmlGenericError(xmlGenericErrorContext,
6384 "Basis : computing new set\n");
6385#endif
6386 xmlXPathNodeCollectAndTest(ctxt, axis, test, type, prefix, name);
6387#ifdef DEBUG_STEP
6388 xmlGenericError(xmlGenericErrorContext, "Basis : ");
6389 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
6390#endif
6391 if (name != NULL)
6392 xmlFree(name);
6393
6394eval_predicates:
6395 SKIP_BLANKS;
6396 while (CUR == '[') {
6397 xmlXPathEvalPredicate(ctxt);
6398 }
6399 }
6400#ifdef DEBUG_STEP
6401 xmlGenericError(xmlGenericErrorContext, "Step : ");
6402 xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
6403 ctxt->value->nodesetval);
6404#endif
6405}
6406
6407/**
6408 * xmlXPathEvalRelativeLocationPath:
6409 * @ctxt: the XPath Parser context
6410 *
6411 * [3] RelativeLocationPath ::= Step
6412 * | RelativeLocationPath '/' Step
6413 * | AbbreviatedRelativeLocationPath
6414 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
6415 *
6416 */
6417void
6418#ifdef VMS
6419xmlXPathEvalRelLocationPath
6420#else
6421xmlXPathEvalRelativeLocationPath
6422#endif
6423(xmlXPathParserContextPtr ctxt) {
6424 SKIP_BLANKS;
6425 if ((CUR == '/') && (NXT(1) == '/')) {
6426 SKIP(2);
6427 SKIP_BLANKS;
6428 xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
6429 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6430 } else if (CUR == '/') {
6431 NEXT;
6432 SKIP_BLANKS;
6433 }
6434 xmlXPathEvalStep(ctxt);
6435 SKIP_BLANKS;
6436 while (CUR == '/') {
6437 if ((CUR == '/') && (NXT(1) == '/')) {
6438 SKIP(2);
6439 SKIP_BLANKS;
6440 xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
6441 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
6442 xmlXPathEvalStep(ctxt);
6443 } else if (CUR == '/') {
6444 NEXT;
6445 SKIP_BLANKS;
6446 xmlXPathEvalStep(ctxt);
6447 }
6448 SKIP_BLANKS;
6449 }
6450}
6451
6452/**
6453 * xmlXPathEvalLocationPath:
6454 * @ctxt: the XPath Parser context
6455 *
6456 * [1] LocationPath ::= RelativeLocationPath
6457 * | AbsoluteLocationPath
6458 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
6459 * | AbbreviatedAbsoluteLocationPath
6460 * [10] AbbreviatedAbsoluteLocationPath ::=
6461 * '//' RelativeLocationPath
6462 *
6463 * // is short for /descendant-or-self::node()/. For example,
6464 * //para is short for /descendant-or-self::node()/child::para and
6465 * so will select any para element in the document (even a para element
6466 * that is a document element will be selected by //para since the
6467 * document element node is a child of the root node); div//para is
6468 * short for div/descendant-or-self::node()/child::para and so will
6469 * select all para descendants of div children.
6470 */
6471void
6472xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
6473 SKIP_BLANKS;
6474 if (CUR != '/') {
6475 xmlXPathEvalRelativeLocationPath(ctxt);
6476 } else {
6477 while (CUR == '/') {
6478 if ((CUR == '/') && (NXT(1) == '/')) {
6479 SKIP(2);
6480 SKIP_BLANKS;
6481 xmlXPathNodeCollectAndTest(ctxt,
6482 AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
6483 NODE_TYPE_NODE, NULL, NULL);
6484 xmlXPathEvalRelativeLocationPath(ctxt);
6485 } else if (CUR == '/') {
6486 NEXT;
6487 SKIP_BLANKS;
6488 if (CUR != 0)
6489 xmlXPathEvalRelativeLocationPath(ctxt);
6490 }
6491 }
6492 }
6493}
6494
6495/**
6496 * xmlXPathEval:
6497 * @str: the XPath expression
6498 * @ctx: the XPath context
6499 *
6500 * Evaluate the XPath Location Path in the given context.
6501 *
6502 * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
6503 * the caller has to free the object.
6504 */
6505xmlXPathObjectPtr
6506xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
6507 xmlXPathParserContextPtr ctxt;
6508 xmlXPathObjectPtr res, tmp, init = NULL;
6509 int stack = 0;
6510
6511 xmlXPathInit();
6512
6513 CHECK_CONTEXT(ctx)
6514
6515 ctxt = xmlXPathNewParserContext(str, ctx);
6516 /**** TAG:9999
6517 if (ctx->node != NULL) {
6518 init = xmlXPathNewNodeSet(ctx->node);
6519 valuePush(ctxt, init);
6520 }
6521 ****/
6522 xmlXPathEvalExpr(ctxt);
6523
6524 if (ctxt->value == NULL) {
6525 xmlGenericError(xmlGenericErrorContext,
6526 "xmlXPathEval: evaluation failed\n");
6527 res = NULL;
6528 } else if (*ctxt->cur != 0) {
6529 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
6530 res = NULL;
6531 } else {
6532 res = valuePop(ctxt);
6533 }
6534
6535 do {
6536 tmp = valuePop(ctxt);
6537 if (tmp != NULL) {
6538 if (tmp != init)
6539 stack++;
6540 xmlXPathFreeObject(tmp);
6541 }
6542 } while (tmp != NULL);
6543 if ((stack != 0) && (res != NULL)) {
6544 xmlGenericError(xmlGenericErrorContext,
6545 "xmlXPathEval: %d object left on the stack\n",
6546 stack);
6547 }
6548 if (ctxt->error != XPATH_EXPRESSION_OK) {
6549 xmlXPathFreeObject(res);
6550 res = NULL;
6551 }
6552
6553 xmlXPathFreeParserContext(ctxt);
6554 return(res);
6555}
6556
6557/**
6558 * xmlXPathEvalExpression:
6559 * @str: the XPath expression
6560 * @ctxt: the XPath context
6561 *
6562 * Evaluate the XPath expression in the given context.
6563 *
6564 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
6565 * the caller has to free the object.
6566 */
6567xmlXPathObjectPtr
6568xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
6569 xmlXPathParserContextPtr pctxt;
6570 xmlXPathObjectPtr res, tmp;
6571 int stack = 0;
6572
6573 xmlXPathInit();
6574
6575 CHECK_CONTEXT(ctxt)
6576
6577 pctxt = xmlXPathNewParserContext(str, ctxt);
6578 xmlXPathEvalExpr(pctxt);
6579
6580 if (*pctxt->cur != 0) {
6581 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
6582 res = NULL;
6583 } else {
6584 res = valuePop(pctxt);
6585 }
6586 do {
6587 tmp = valuePop(pctxt);
6588 if (tmp != NULL) {
6589 xmlXPathFreeObject(tmp);
6590 stack++;
6591 }
6592 } while (tmp != NULL);
6593 if ((stack != 0) && (res != NULL)) {
6594 xmlGenericError(xmlGenericErrorContext,
6595 "xmlXPathEvalExpression: %d object left on the stack\n",
6596 stack);
6597 }
6598 xmlXPathFreeParserContext(pctxt);
6599 return(res);
6600}
6601
6602/**
6603 * xmlXPathRegisterAllFunctions:
6604 * @ctxt: the XPath context
6605 *
6606 * Registers all default XPath functions in this context
6607 */
6608void
6609xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
6610{
6611 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
6612 xmlXPathBooleanFunction);
6613 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
6614 xmlXPathCeilingFunction);
6615 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
6616 xmlXPathCountFunction);
6617 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
6618 xmlXPathConcatFunction);
6619 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
6620 xmlXPathContainsFunction);
6621 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
6622 xmlXPathIdFunction);
6623 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
6624 xmlXPathFalseFunction);
6625 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
6626 xmlXPathFloorFunction);
6627 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
6628 xmlXPathLastFunction);
6629 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
6630 xmlXPathLangFunction);
6631 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
6632 xmlXPathLocalNameFunction);
6633 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
6634 xmlXPathNotFunction);
6635 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
6636 xmlXPathNameFunction);
6637 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
6638 xmlXPathNamespaceURIFunction);
6639 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
6640 xmlXPathNormalizeFunction);
6641 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
6642 xmlXPathNumberFunction);
6643 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
6644 xmlXPathPositionFunction);
6645 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
6646 xmlXPathRoundFunction);
6647 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
6648 xmlXPathStringFunction);
6649 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
6650 xmlXPathStringLengthFunction);
6651 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
6652 xmlXPathStartsWithFunction);
6653 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
6654 xmlXPathSubstringFunction);
6655 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
6656 xmlXPathSubstringBeforeFunction);
6657 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
6658 xmlXPathSubstringAfterFunction);
6659 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
6660 xmlXPathSumFunction);
6661 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
6662 xmlXPathTrueFunction);
6663 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
6664 xmlXPathTranslateFunction);
6665}
6666
6667#endif /* LIBXML_XPATH_ENABLED */