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