|
1 /* |
|
2 * libxml2_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.com |
|
14 * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
15 * |
|
16 */ |
|
17 |
|
18 #define IN_LIBXML |
|
19 |
|
20 #include "xmlenglibxml.h" |
|
21 |
|
22 #include <string.h> |
|
23 |
|
24 |
|
25 #ifdef HAVE_SYS_TYPES_H |
|
26 #include <sys/types.h> |
|
27 #endif |
|
28 #ifdef HAVE_MATH_H |
|
29 #include <math.h> |
|
30 #endif |
|
31 #ifdef HAVE_FLOAT_H |
|
32 #include <float.h> |
|
33 #endif |
|
34 #if defined(HAVE_CTYPE_H) |
|
35 #include <ctype.h> |
|
36 #endif |
|
37 #ifdef HAVE_SIGNAL_H |
|
38 #include <signal.h> |
|
39 #endif |
|
40 |
|
41 #include <stdapis/libxml2/libxml2_globals.h> |
|
42 #include <stdapis/libxml2/libxml2_xpathinternals.h> |
|
43 #include <stdapis/libxml2/libxml2_parserinternals.h> |
|
44 #include "libxml2_xmlerror2.h" |
|
45 |
|
46 #ifdef LIBXML_XPTR_ENABLED |
|
47 #include <stdapis/libxml2/libxml2_xpointer.h> |
|
48 #endif |
|
49 |
|
50 #ifdef LIBXML_DEBUG_ENABLED |
|
51 #include "libxml2_debugxml.h" |
|
52 #endif |
|
53 |
|
54 #include <ctype.h> |
|
55 |
|
56 |
|
57 #define TODO \ |
|
58 xmlGenericError(xmlGenericErrorContext, \ |
|
59 EMBED_ERRTXT("Unimplemented block at %s:%d\n"), \ |
|
60 __FILE__, __LINE__); |
|
61 |
|
62 #if defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XPATH_ENABLED) |
|
63 /************************************************************************ |
|
64 * * |
|
65 * Floating point stuff * |
|
66 * * |
|
67 ************************************************************************/ |
|
68 |
|
69 #ifndef TRIO_REPLACE_STDIO |
|
70 //#define TRIO_PUBLIC static |
|
71 #endif |
|
72 #include "libxml2_trionan.inc" |
|
73 |
|
74 /** |
|
75 * xmlXPathInit: |
|
76 * |
|
77 * Initialize the XPath environment |
|
78 * |
|
79 * OOM: never (so far -- soon hash tables will be created here) |
|
80 */ |
|
81 XMLPUBFUNEXPORT void |
|
82 xmlXPathInit(void) { |
|
83 LOAD_GS_DIRECT |
|
84 if (xmlXPathInitialized) |
|
85 return; |
|
86 |
|
87 xmlXPathPINF = trio_pinf(); |
|
88 xmlXPathNINF = trio_ninf(); |
|
89 xmlXPathNAN = trio_nan(); |
|
90 xmlXPathNZERO = trio_nzero(); |
|
91 |
|
92 xmlXPathInitialized = 1; |
|
93 |
|
94 } |
|
95 |
|
96 /** |
|
97 * xeXPathCleanup: |
|
98 * |
|
99 * Free any reusable by XPath module resources |
|
100 */ |
|
101 void xeXPathCleanup() |
|
102 { |
|
103 LOAD_GS_DIRECT |
|
104 if (xmlXPathDefaultFunctionsHash){ |
|
105 xmlHashFree(xmlXPathDefaultFunctionsHash, NULL); |
|
106 xmlXPathDefaultFunctionsHash = NULL; |
|
107 } |
|
108 |
|
109 if (xmlXPathIntermediaryExtensionFunctionsHash){ |
|
110 xmlHashFree(xmlXPathIntermediaryExtensionFunctionsHash, NULL); |
|
111 xmlXPathIntermediaryExtensionFunctionsHash = NULL; |
|
112 } |
|
113 } |
|
114 |
|
115 // DONE: OPTIMIZE: Define as macro or inliner |
|
116 /** |
|
117 * xmlXPathIsNaN: |
|
118 * @param val a double value |
|
119 * |
|
120 * Provides a portable isnan() function to detect whether a double |
|
121 * is a NotaNumber. Based on trio code |
|
122 * http://sourceforge.net/projects/ctrio/ |
|
123 * |
|
124 * Returns 1 if the value is a NaN, 0 otherwise |
|
125 */ |
|
126 /* |
|
127 inline int xmlXPathIsNaN(double val) { |
|
128 return(trio_isnan(val)); |
|
129 }*/ |
|
130 //#define xmlXPathIsNaN(val) trio_isnan(val) |
|
131 |
|
132 //#define xmlXPathIsNaN(val) trio_isnan(val) |
|
133 |
|
134 // DONE: OPTIMIZE: Define as macro or inliner |
|
135 /** |
|
136 * xmlXPathIsInf: |
|
137 * @param val a double value |
|
138 * |
|
139 * Provides a portable isinf() function to detect whether a double |
|
140 * is a +Infinite or -Infinite. Based on trio code |
|
141 * http://sourceforge.net/projects/ctrio/ |
|
142 * |
|
143 * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise |
|
144 */ |
|
145 /* |
|
146 inline int xmlXPathIsInf(double val) { |
|
147 return(trio_isinf(val)); |
|
148 } |
|
149 */ |
|
150 #define xmlXPathIsInf(val) trio_isinf(val) |
|
151 |
|
152 #endif /* SCHEMAS or XPATH */ |
|
153 |
|
154 |
|
155 #ifdef LIBXML_XPATH_ENABLED |
|
156 |
|
157 |
|
158 |
|
159 /** |
|
160 * xmlXPathGetSign: |
|
161 * @param val a double value |
|
162 * |
|
163 * Provides a portable function to detect the sign of a double |
|
164 * Modified from trio code |
|
165 * http://sourceforge.net/projects/ctrio/ |
|
166 * |
|
167 * Returns 1 if the value is Negative, 0 if positive |
|
168 */ |
|
169 /* |
|
170 inline int xmlXPathGetSign(double val) { |
|
171 return(trio_signbit(val)); |
|
172 } |
|
173 */ |
|
174 #define xmlXPathGetSign(val) trio_signbit(val) |
|
175 |
|
176 |
|
177 /* |
|
178 * |
|
179 * |
|
180 */ |
|
181 /* #define DEBUG */ |
|
182 /* #define DEBUG_STEP */ |
|
183 /* #define DEBUG_STEP_NTH */ |
|
184 /* #define DEBUG_EXPR */ |
|
185 /* #define DEBUG_EVAL_COUNTS */ |
|
186 |
|
187 |
|
188 |
|
189 static const xmlNs xmlXPathXMLNamespaceStruct = { |
|
190 NULL, |
|
191 XML_NAMESPACE_DECL, |
|
192 XML_XML_NAMESPACE, |
|
193 BAD_CAST "xml", |
|
194 NULL |
|
195 }; |
|
196 static const xmlNsPtr xmlXPathXMLNamespace = (xmlNsPtr)&xmlXPathXMLNamespaceStruct; |
|
197 |
|
198 /************************************************************************ |
|
199 * * |
|
200 * Error handling routines * |
|
201 * * |
|
202 ************************************************************************/ |
|
203 |
|
204 /* |
|
205 * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError |
|
206 */ |
|
207 const char *const xmlXPathErrorMessages[] = { |
|
208 EMBED_ERRTXT("Ok\n"), |
|
209 EMBED_ERRTXT("Number encoding\n"), |
|
210 EMBED_ERRTXT("Unfinished literal\n"), |
|
211 EMBED_ERRTXT("Start of literal\n"), |
|
212 EMBED_ERRTXT("Expected $ for variable reference\n"), |
|
213 EMBED_ERRTXT("Undefined variable\n"), |
|
214 EMBED_ERRTXT("Invalid predicate\n"), |
|
215 EMBED_ERRTXT("Invalid expression\n"), |
|
216 EMBED_ERRTXT("Missing closing curly brace\n"), |
|
217 EMBED_ERRTXT("Unregistered function\n"), |
|
218 EMBED_ERRTXT("Invalid operand\n"), |
|
219 EMBED_ERRTXT("Invalid type\n"), |
|
220 EMBED_ERRTXT("Invalid number of arguments\n"), |
|
221 EMBED_ERRTXT("Invalid context size\n"), |
|
222 EMBED_ERRTXT("Invalid context position\n"), |
|
223 EMBED_ERRTXT("Memory allocation error\n"), |
|
224 EMBED_ERRTXT("Syntax error\n"), |
|
225 EMBED_ERRTXT("Resource error\n"), |
|
226 EMBED_ERRTXT("Sub resource error\n"), |
|
227 EMBED_ERRTXT("Undefined namespace prefix\n"), |
|
228 EMBED_ERRTXT("Encoding error\n"), |
|
229 EMBED_ERRTXT("Char out of XML range\n") |
|
230 }; |
|
231 |
|
232 |
|
233 |
|
234 /** |
|
235 * xmlXPathErrMemory: |
|
236 * @param ctxt an XPath context |
|
237 * @param extra extra informations |
|
238 * |
|
239 * Handle a redefinition of attribute error |
|
240 */ |
|
241 static void |
|
242 xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) |
|
243 { |
|
244 if (ctxt != NULL) { |
|
245 /* Disabled -- do not allocate memory during OOM handling!!! |
|
246 |
|
247 if (extra) { |
|
248 xmlChar buf[200]; |
|
249 |
|
250 xmlStrPrintf(buf, 200, |
|
251 BAD_CAST EMBED_ERRTXT("Memory allocation failed : %s\n"), extra); |
|
252 ctxt->lastError.message = (char *) xmlStrdup(buf); |
|
253 } else { |
|
254 ctxt->lastError.message = (char *) |
|
255 xmlStrdup(BAD_CAST EMBED_ERRTXT("Memory allocation failed\n")); |
|
256 } |
|
257 |
|
258 */ |
|
259 ctxt->lastError.domain = XML_FROM_XPATH; |
|
260 ctxt->lastError.code = XML_ERR_NO_MEMORY; |
|
261 if (ctxt->error != NULL) |
|
262 ctxt->error(ctxt->userData, &ctxt->lastError); |
|
263 } else { |
|
264 if (extra) |
|
265 __xmlRaiseError(NULL, NULL, NULL, |
|
266 NULL, NULL, XML_FROM_XPATH, |
|
267 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
|
268 extra, NULL, NULL, 0, 0, |
|
269 EMBED_ERRTXT("Memory allocation failed : %s\n"), extra); |
|
270 else |
|
271 __xmlRaiseError(NULL, NULL, NULL, |
|
272 NULL, NULL, XML_FROM_XPATH, |
|
273 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
|
274 NULL, NULL, NULL, 0, 0, |
|
275 EMBED_ERRTXT("Memory allocation failed\n")); |
|
276 } |
|
277 } |
|
278 |
|
279 /** |
|
280 * xmlXPathErrMemory: |
|
281 * @param ctxt an XPath parser context |
|
282 * @param extra extra informations |
|
283 * |
|
284 * Handle a redefinition of attribute error |
|
285 */ |
|
286 |
|
287 static void |
|
288 xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra) |
|
289 { |
|
290 ctxt->error = XPATH_MEMORY_ERROR; |
|
291 if (ctxt == NULL) |
|
292 xmlXPathErrMemory(NULL, extra); |
|
293 else |
|
294 xmlXPathErrMemory(ctxt->context, extra); |
|
295 } |
|
296 |
|
297 /** |
|
298 * xmlXPathErr: |
|
299 * @param ctxt a XPath parser context |
|
300 * @param error the error code |
|
301 * |
|
302 * Handle a Relax NG Parsing error |
|
303 */ |
|
304 XMLPUBFUNEXPORT void |
|
305 xmlXPathErr(xmlXPathParserContextPtr ctxt, int error) |
|
306 { |
|
307 |
|
308 if (ctxt == NULL) { |
|
309 __xmlRaiseError(NULL, NULL, NULL, |
|
310 NULL, NULL, XML_FROM_XPATH, |
|
311 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
|
312 XML_ERR_ERROR, NULL, 0, |
|
313 NULL, NULL, NULL, 0, 0, |
|
314 xmlXPathErrorMessages[error]); |
|
315 return; |
|
316 } |
|
317 ctxt->error = error; |
|
318 if (ctxt->context == NULL) { |
|
319 __xmlRaiseError(NULL, NULL, NULL, |
|
320 NULL, NULL, XML_FROM_XPATH, |
|
321 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
|
322 XML_ERR_ERROR, NULL, 0, |
|
323 (const char *) ctxt->base, NULL, NULL, |
|
324 ctxt->cur - ctxt->base, 0, |
|
325 xmlXPathErrorMessages[error]); |
|
326 return; |
|
327 } |
|
328 |
|
329 ctxt->context->lastError.domain = XML_FROM_XPATH; |
|
330 ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK; |
|
331 ctxt->context->lastError.level = XML_ERR_ERROR; |
|
332 if (ctxt->context->lastError.str1){ |
|
333 xmlFree(ctxt->context->lastError.str1); |
|
334 ctxt->context->lastError.str1 = NULL; |
|
335 } |
|
336 // Avoid memory allocation if we are in OOM ! |
|
337 if(error != XPATH_MEMORY_ERROR){ |
|
338 ctxt->context->lastError.str1 = (char*) xmlStrdup(ctxt->base); |
|
339 } |
|
340 ctxt->context->lastError.int1 = ctxt->cur - ctxt->base; |
|
341 ctxt->context->lastError.node = ctxt->context->debugNode; |
|
342 |
|
343 if (ctxt->context->error != NULL) { |
|
344 ctxt->context->error(ctxt->context->userData, &ctxt->context->lastError); |
|
345 } else { |
|
346 __xmlRaiseError(NULL, NULL, NULL, |
|
347 NULL, ctxt->context->debugNode, XML_FROM_XPATH, |
|
348 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
|
349 XML_ERR_ERROR, NULL, 0, |
|
350 (const char*) ctxt->base, NULL, NULL, |
|
351 ctxt->cur - ctxt->base, 0, |
|
352 xmlXPathErrorMessages[error]); |
|
353 } |
|
354 } |
|
355 |
|
356 // DONE: OPTIMIZE: Use macro instead of function call |
|
357 // Macro added in xpathInternals.h |
|
358 /** |
|
359 * xmlXPatherror: |
|
360 * @param ctxt the XPath Parser context |
|
361 * @param file the file name |
|
362 * @param line the line number |
|
363 * @param no the error number |
|
364 * |
|
365 * Formats an error message. |
|
366 */ |
|
367 //void |
|
368 //xmlXPatherror(xmlXPathParserContextPtr ctxt, const char* file ATTRIBUTE_UNUSED, |
|
369 // int line ATTRIBUTE_UNUSED , int no) |
|
370 //{ |
|
371 // xmlXPathErr(ctxt, no); |
|
372 //} |
|
373 |
|
374 |
|
375 /************************************************************************ |
|
376 * * |
|
377 * Parser Types * |
|
378 * * |
|
379 ************************************************************************/ |
|
380 |
|
381 /* |
|
382 * Types are private: |
|
383 */ |
|
384 typedef enum { |
|
385 AXIS_ANCESTOR = 1, |
|
386 AXIS_ANCESTOR_OR_SELF, |
|
387 AXIS_ATTRIBUTE, |
|
388 AXIS_CHILD, |
|
389 AXIS_DESCENDANT, |
|
390 AXIS_DESCENDANT_OR_SELF, |
|
391 AXIS_FOLLOWING, |
|
392 AXIS_FOLLOWING_SIBLING, |
|
393 AXIS_NAMESPACE, |
|
394 AXIS_PARENT, |
|
395 AXIS_PRECEDING, |
|
396 AXIS_PRECEDING_SIBLING, |
|
397 AXIS_SELF |
|
398 } xmlXPathAxisVal; |
|
399 |
|
400 typedef enum { |
|
401 NODE_TEST_NONE = 0, |
|
402 NODE_TEST_TYPE = 1, |
|
403 NODE_TEST_PI = 2, |
|
404 NODE_TEST_ALL = 3, |
|
405 NODE_TEST_NS = 4, |
|
406 NODE_TEST_NAME = 5 |
|
407 } xmlXPathTestVal; |
|
408 |
|
409 typedef enum { |
|
410 NODE_TYPE_NODE = 0, |
|
411 NODE_TYPE_COMMENT = XML_COMMENT_NODE, |
|
412 NODE_TYPE_TEXT = XML_TEXT_NODE, |
|
413 NODE_TYPE_PI = XML_PI_NODE |
|
414 } xmlXPathTypeVal; |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 /************************************************************************ |
|
420 * * |
|
421 * Parser Type functions * |
|
422 * * |
|
423 ************************************************************************/ |
|
424 |
|
425 /** |
|
426 * xmlXPathNewCompExpr: |
|
427 * |
|
428 * Create a new Xpath component |
|
429 * |
|
430 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error |
|
431 */ |
|
432 static xmlXPathCompExprPtr |
|
433 xmlXPathNewCompExpr(void) { |
|
434 xmlXPathCompExprPtr cur; |
|
435 |
|
436 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr)); |
|
437 if (cur == NULL) { |
|
438 xmlXPathErrMemory(NULL, EMBED_ERRTXT("allocating component\n")); |
|
439 return(NULL); |
|
440 } |
|
441 memset(cur, 0, sizeof(xmlXPathCompExpr)); |
|
442 cur->maxStep = XPATH_STEPS_GRANULARITY; |
|
443 cur->nbStep = 0; |
|
444 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * sizeof(xmlXPathStepOp)); |
|
445 if (cur->steps == NULL) { |
|
446 xmlXPathErrMemory(NULL, EMBED_ERRTXT("allocating steps\n")); |
|
447 xmlFree(cur); |
|
448 return(NULL); |
|
449 } |
|
450 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp)); |
|
451 cur->last = -1; |
|
452 #ifdef DEBUG_EVAL_COUNTS |
|
453 cur->nb = 0; |
|
454 #endif |
|
455 return(cur); |
|
456 } |
|
457 |
|
458 /** |
|
459 * xmlXPathFreeCompExpr: |
|
460 * @param comp an XPATH comp |
|
461 * |
|
462 * Free up the memory allocated by comp |
|
463 */ |
|
464 XMLPUBFUNEXPORT void |
|
465 xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) |
|
466 { |
|
467 xmlXPathStepOpPtr op; |
|
468 int i; |
|
469 |
|
470 if (comp == NULL) |
|
471 return; |
|
472 if (comp->dict == NULL) { |
|
473 for (i = 0; i < comp->nbStep; i++) { |
|
474 op = &comp->steps[i]; |
|
475 if (op->value4 != NULL) { |
|
476 if (op->op == XPATH_OP_VALUE) |
|
477 xmlXPathFreeObject((xmlXPathObjectPtr)op->value4); |
|
478 else |
|
479 xmlFree(op->value4); |
|
480 } |
|
481 if (op->value5 != NULL) |
|
482 xmlFree(op->value5); |
|
483 } |
|
484 } else { |
|
485 for (i = 0; i < comp->nbStep; i++) { |
|
486 op = &comp->steps[i]; |
|
487 if (op->value4 != NULL) { |
|
488 if (op->op == XPATH_OP_VALUE) |
|
489 xmlXPathFreeObject((xmlXPathObjectPtr)op->value4); |
|
490 } |
|
491 } |
|
492 xmlDictFree(comp->dict); |
|
493 } |
|
494 |
|
495 if (comp->steps != NULL) { |
|
496 xmlFree(comp->steps); |
|
497 } |
|
498 #ifdef DEBUG_EVAL_COUNTS |
|
499 if (comp->string != NULL) { |
|
500 xmlFree(comp->string); |
|
501 } |
|
502 #endif |
|
503 if (comp->expr != NULL) { |
|
504 xmlFree(comp->expr); |
|
505 } |
|
506 |
|
507 xmlFree(comp); |
|
508 } |
|
509 |
|
510 /** |
|
511 * xmlXPathCompExprAdd: |
|
512 * @param comp the compiled expression |
|
513 * @param ch1 first child index |
|
514 * @param ch2 second child index |
|
515 * @param op an op |
|
516 * @param value the first int value |
|
517 * @param value2 the second int value |
|
518 * @param value3 the third int value |
|
519 * @param value4 the first string value |
|
520 * @param value5 the second string value |
|
521 * |
|
522 * Add a step to an XPath Compiled Expression |
|
523 * |
|
524 * Returns -1 in case of failure, the index otherwise |
|
525 */ |
|
526 static int |
|
527 xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2, |
|
528 xmlXPathOp op, int value, |
|
529 int value2, int value3, void *value4, void *value5) |
|
530 { |
|
531 xmlXPathStepOpPtr step; |
|
532 |
|
533 if (comp->nbStep >= comp->maxStep) |
|
534 { |
|
535 xmlXPathStepOp *real; |
|
536 |
|
537 comp->maxStep *= 2; |
|
538 real = (xmlXPathStepOp *) xmlRealloc(comp->steps, comp->maxStep * sizeof(xmlXPathStepOp)); |
|
539 if (real == NULL) { |
|
540 comp->maxStep /= 2; |
|
541 xmlXPathErrMemory(NULL, EMBED_ERRTXT("adding step\n")); |
|
542 return(-1); |
|
543 } |
|
544 comp->steps = real; |
|
545 } |
|
546 // DONE: OPTIMIZE: make a proxy for comp->steps[comp->nbStep] |
|
547 step = &(comp->steps[comp->nbStep]); |
|
548 // |
|
549 comp->last = comp->nbStep; |
|
550 step->ch1 = ch1; |
|
551 step->ch2 = ch2; |
|
552 step->op = op; |
|
553 step->value = value; |
|
554 step->value2 = value2; |
|
555 step->value3 = value3; |
|
556 |
|
557 if ((comp->dict != NULL) && |
|
558 ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) || |
|
559 (op == XPATH_OP_COLLECT))) |
|
560 { |
|
561 if (value4 != NULL) { |
|
562 step->value4 = (xmlChar*) |
|
563 (void*)xmlDictLookup(comp->dict, (xmlChar*) value4, -1); |
|
564 xmlFree(value4); |
|
565 } else |
|
566 step->value4 = NULL; |
|
567 |
|
568 if (value5 != NULL) { |
|
569 step->value5 = (xmlChar*) |
|
570 (void*)xmlDictLookup(comp->dict, (xmlChar*)value5, -1); |
|
571 xmlFree(value5); |
|
572 }else |
|
573 step->value5 = NULL; |
|
574 } |
|
575 else |
|
576 { |
|
577 step->value4 = value4; |
|
578 step->value5 = value5; |
|
579 } |
|
580 step->cache = NULL; |
|
581 return(comp->nbStep++); |
|
582 } |
|
583 |
|
584 /** |
|
585 * xmlXPathCompSwap: |
|
586 * @param comp the compiled expression |
|
587 * @param op operation index |
|
588 * |
|
589 * Swaps 2 operations in the compiled expression |
|
590 */ |
|
591 static void |
|
592 xmlXPathCompSwap(xmlXPathStepOpPtr op) { |
|
593 LOAD_GS_DIRECT |
|
594 int tmp; |
|
595 |
|
596 #ifndef LIBXML_THREAD_ENABLED |
|
597 /* |
|
598 * Since this manipulates possibly shared variables, this is |
|
599 * disabled if one detects that the library is used in a multithreaded |
|
600 * application |
|
601 */ |
|
602 if (xmlXPathDisableOptimizer) |
|
603 return; |
|
604 #endif |
|
605 |
|
606 tmp = op->ch1; |
|
607 op->ch1 = op->ch2; |
|
608 op->ch2 = tmp; |
|
609 } |
|
610 |
|
611 |
|
612 #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \ |
|
613 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), (op), (val), (val2), (val3), (val4), (val5)) |
|
614 |
|
615 #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \ |
|
616 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, (op), (val), (val2), (val3), (val4), (val5)) |
|
617 |
|
618 #define PUSH_LEAVE_EXPR(op, val, val2) \ |
|
619 xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL) |
|
620 |
|
621 #define PUSH_UNARY_EXPR(op, ch, val, val2) \ |
|
622 xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL) |
|
623 |
|
624 #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \ |
|
625 xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL) |
|
626 |
|
627 /************************************************************************ |
|
628 * * |
|
629 * Debugging related functions * |
|
630 * * |
|
631 ************************************************************************/ |
|
632 |
|
633 |
|
634 #define STRANGE \ |
|
635 xmlGenericError(xmlGenericErrorContext, \ |
|
636 EMBED_ERRTXT("Internal error at %s:%d\n"), \ |
|
637 __FILE__, __LINE__); |
|
638 |
|
639 #ifdef LIBXML_DEBUG_ENABLED |
|
640 static void |
|
641 xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) { |
|
642 int i; |
|
643 char shift[100]; |
|
644 |
|
645 for (i = 0;((i < depth) && (i < 25));i++) |
|
646 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
647 shift[2 * i] = shift[2 * i + 1] = 0; |
|
648 if (cur == NULL) { |
|
649 fprintf(output, shift); |
|
650 fprintf(output, "Node is NULL !\n"); |
|
651 return; |
|
652 |
|
653 } |
|
654 |
|
655 if ((cur->type == XML_DOCUMENT_NODE) || |
|
656 (cur->type == XML_HTML_DOCUMENT_NODE)) { |
|
657 fprintf(output, shift); |
|
658 fprintf(output, " /\n"); |
|
659 } else if (cur->type == XML_ATTRIBUTE_NODE) |
|
660 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth); |
|
661 else |
|
662 xmlDebugDumpOneNode(output, cur, depth); |
|
663 } |
|
664 |
|
665 static void |
|
666 xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) { |
|
667 xmlNodePtr tmp; |
|
668 int i; |
|
669 char shift[100]; |
|
670 |
|
671 for (i = 0;((i < depth) && (i < 25));i++) |
|
672 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
673 shift[2 * i] = shift[2 * i + 1] = 0; |
|
674 if (cur == NULL) { |
|
675 fprintf(output, shift); |
|
676 fprintf(output, "Node is NULL !\n"); |
|
677 return; |
|
678 } |
|
679 |
|
680 while (cur != NULL) { |
|
681 tmp = cur; |
|
682 cur = cur->next; |
|
683 xmlDebugDumpOneNode(output, tmp, depth); |
|
684 } |
|
685 } |
|
686 |
|
687 static void |
|
688 xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) { |
|
689 int i; |
|
690 char shift[100]; |
|
691 |
|
692 for (i = 0;((i < depth) && (i < 25));i++) |
|
693 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
694 |
|
695 shift[2 * i] = shift[2 * i + 1] = 0; |
|
696 |
|
697 if (cur == NULL) { |
|
698 fprintf(output, shift); |
|
699 fprintf(output, "NodeSet is NULL !\n"); |
|
700 return; |
|
701 } |
|
702 |
|
703 if (cur != NULL) { |
|
704 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr); |
|
705 for (i = 0;i < cur->nodeNr;i++) { |
|
706 fprintf(output, shift); |
|
707 fprintf(output, "%d", i + 1); |
|
708 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1); |
|
709 } |
|
710 } |
|
711 } |
|
712 |
|
713 static void |
|
714 xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) { |
|
715 int i; |
|
716 char shift[100]; |
|
717 |
|
718 for (i = 0;((i < depth) && (i < 25));i++) |
|
719 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
720 shift[2 * i] = shift[2 * i + 1] = 0; |
|
721 |
|
722 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) { |
|
723 fprintf(output, shift); |
|
724 fprintf(output, "Value Tree is NULL !\n"); |
|
725 return; |
|
726 |
|
727 } |
|
728 |
|
729 fprintf(output, shift); |
|
730 fprintf(output, "%d", i + 1); |
|
731 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1); |
|
732 } |
|
733 #if defined(LIBXML_XPTR_ENABLED) |
|
734 static void |
|
735 xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) { |
|
736 int i; |
|
737 char shift[100]; |
|
738 |
|
739 for (i = 0;((i < depth) && (i < 25));i++) |
|
740 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
741 shift[2 * i] = shift[2 * i + 1] = 0; |
|
742 |
|
743 if (cur == NULL) { |
|
744 fprintf(output, shift); |
|
745 fprintf(output, "LocationSet is NULL !\n"); |
|
746 return; |
|
747 |
|
748 } |
|
749 |
|
750 for (i = 0;i < cur->locNr;i++) { |
|
751 fprintf(output, shift); |
|
752 fprintf(output, "%d : ", i + 1); |
|
753 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1); |
|
754 } |
|
755 } |
|
756 #endif /* LIBXML_XPTR_ENABLED */ |
|
757 |
|
758 /** |
|
759 * xmlXPathDebugDumpObject: |
|
760 * @param output the FILE * to dump the output |
|
761 * @param cur the object to inspect |
|
762 * @param depth indentation level |
|
763 * |
|
764 * Dump the content of the object for debugging purposes |
|
765 */ |
|
766 void |
|
767 xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) { |
|
768 int i; |
|
769 char shift[100]; |
|
770 |
|
771 for (i = 0;((i < depth) && (i < 25));i++) |
|
772 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
773 shift[2 * i] = shift[2 * i + 1] = 0; |
|
774 |
|
775 fprintf(output, shift); |
|
776 |
|
777 if (cur == NULL) { |
|
778 fprintf(output, "Object is empty (NULL)\n"); |
|
779 return; |
|
780 } |
|
781 switch(cur->type) { |
|
782 case XPATH_UNDEFINED: |
|
783 fprintf(output, "Object is uninitialized\n"); |
|
784 break; |
|
785 case XPATH_NODESET: |
|
786 fprintf(output, "Object is a Node Set :\n"); |
|
787 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth); |
|
788 break; |
|
789 case XPATH_XSLT_TREE: |
|
790 fprintf(output, "Object is an XSLT value tree :\n"); |
|
791 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth); |
|
792 break; |
|
793 case XPATH_BOOLEAN: |
|
794 fprintf(output, "Object is a Boolean : "); |
|
795 if (cur->boolval) fprintf(output, "true\n"); |
|
796 else fprintf(output, "false\n"); |
|
797 break; |
|
798 case XPATH_NUMBER: |
|
799 switch (xmlXPathIsInf(cur->floatval)) { |
|
800 case 1: |
|
801 fprintf(output, "Object is a number : Infinity\n"); |
|
802 break; |
|
803 case -1: |
|
804 fprintf(output, "Object is a number : -Infinity\n"); |
|
805 break; |
|
806 default: |
|
807 if (xmlXPathIsNaN(cur->floatval)) { |
|
808 fprintf(output, "Object is a number : NaN\n"); |
|
809 } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) { |
|
810 fprintf(output, "Object is a number : 0\n"); |
|
811 } else { |
|
812 fprintf(output, "Object is a number : %0g\n", cur->floatval); |
|
813 } |
|
814 } |
|
815 break; |
|
816 case XPATH_STRING: |
|
817 fprintf(output, "Object is a string : "); |
|
818 xmlDebugDumpString(output, cur->stringval); |
|
819 fprintf(output, "\n"); |
|
820 break; |
|
821 case XPATH_POINT: |
|
822 fprintf(output, "Object is a point : index %d in node", cur->index); |
|
823 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); |
|
824 fprintf(output, "\n"); |
|
825 break; |
|
826 case XPATH_RANGE: |
|
827 if ((cur->user2 == NULL) || |
|
828 ((cur->user2 == cur->user) && (cur->index == cur->index2))) { |
|
829 fprintf(output, "Object is a collapsed range :\n"); |
|
830 fprintf(output, shift); |
|
831 if (cur->index >= 0) |
|
832 fprintf(output, "index %d in ", cur->index); |
|
833 fprintf(output, "node\n"); |
|
834 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, |
|
835 depth + 1); |
|
836 } else { |
|
837 fprintf(output, "Object is a range :\n"); |
|
838 fprintf(output, shift); |
|
839 fprintf(output, "From "); |
|
840 if (cur->index >= 0) |
|
841 fprintf(output, "index %d in ", cur->index); |
|
842 fprintf(output, "node\n"); |
|
843 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, |
|
844 depth + 1); |
|
845 fprintf(output, shift); |
|
846 fprintf(output, "To "); |
|
847 if (cur->index2 >= 0) |
|
848 fprintf(output, "index %d in ", cur->index2); |
|
849 fprintf(output, "node\n"); |
|
850 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2, |
|
851 depth + 1); |
|
852 fprintf(output, "\n"); |
|
853 } |
|
854 break; |
|
855 case XPATH_LOCATIONSET: |
|
856 #if defined(LIBXML_XPTR_ENABLED) |
|
857 fprintf(output, "Object is a Location Set:\n"); |
|
858 xmlXPathDebugDumpLocationSet(output, |
|
859 (xmlLocationSetPtr) cur->user, depth); |
|
860 #endif |
|
861 break; |
|
862 case XPATH_USERS: |
|
863 fprintf(output, "Object is user defined\n"); |
|
864 break; |
|
865 } |
|
866 } |
|
867 |
|
868 static void |
|
869 xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp, |
|
870 xmlXPathStepOpPtr op, int depth) { |
|
871 int i; |
|
872 char shift[100]; |
|
873 |
|
874 for (i = 0;((i < depth) && (i < 25));i++) |
|
875 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
876 shift[2 * i] = shift[2 * i + 1] = 0; |
|
877 |
|
878 fprintf(output, shift); |
|
879 if (op == NULL) { |
|
880 fprintf(output, "Step is NULL\n"); |
|
881 return; |
|
882 } |
|
883 switch (op->op) { |
|
884 case XPATH_OP_END: |
|
885 fprintf(output, "END"); break; |
|
886 case XPATH_OP_AND: |
|
887 fprintf(output, "AND"); break; |
|
888 case XPATH_OP_OR: |
|
889 fprintf(output, "OR"); break; |
|
890 case XPATH_OP_EQUAL: |
|
891 if (op->value) |
|
892 fprintf(output, "EQUAL ="); |
|
893 else |
|
894 fprintf(output, "EQUAL !="); |
|
895 break; |
|
896 case XPATH_OP_CMP: |
|
897 if (op->value) |
|
898 fprintf(output, "CMP <"); |
|
899 else |
|
900 fprintf(output, "CMP >"); |
|
901 if (!op->value2) |
|
902 fprintf(output, "="); |
|
903 break; |
|
904 case XPATH_OP_PLUS: |
|
905 if (op->value == 0) |
|
906 fprintf(output, "PLUS -"); |
|
907 else if (op->value == 1) |
|
908 fprintf(output, "PLUS +"); |
|
909 else if (op->value == 2) |
|
910 fprintf(output, "PLUS unary -"); |
|
911 else if (op->value == 3) |
|
912 fprintf(output, "PLUS unary - -"); |
|
913 break; |
|
914 case XPATH_OP_MULT: |
|
915 if (op->value == 0) |
|
916 fprintf(output, "MULT *"); |
|
917 else if (op->value == 1) |
|
918 fprintf(output, "MULT div"); |
|
919 else |
|
920 fprintf(output, "MULT mod"); |
|
921 break; |
|
922 case XPATH_OP_UNION: |
|
923 fprintf(output, "UNION"); break; |
|
924 case XPATH_OP_ROOT: |
|
925 fprintf(output, "ROOT"); break; |
|
926 case XPATH_OP_NODE: |
|
927 fprintf(output, "NODE"); break; |
|
928 case XPATH_OP_RESET: |
|
929 fprintf(output, "RESET"); break; |
|
930 case XPATH_OP_SORT: |
|
931 fprintf(output, "SORT"); break; |
|
932 case XPATH_OP_COLLECT: { |
|
933 xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value; |
|
934 xmlXPathTestVal test = (xmlXPathTestVal)op->value2; |
|
935 xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3; |
|
936 const xmlChar *prefix = op->value4; |
|
937 const xmlChar *name = op->value5; |
|
938 |
|
939 fprintf(output, "COLLECT "); |
|
940 switch (axis) { |
|
941 case AXIS_ANCESTOR: |
|
942 fprintf(output, " 'ancestors' "); break; |
|
943 case AXIS_ANCESTOR_OR_SELF: |
|
944 fprintf(output, " 'ancestors-or-self' "); break; |
|
945 case AXIS_ATTRIBUTE: |
|
946 fprintf(output, " 'attributes' "); break; |
|
947 case AXIS_CHILD: |
|
948 fprintf(output, " 'child' "); break; |
|
949 case AXIS_DESCENDANT: |
|
950 fprintf(output, " 'descendant' "); break; |
|
951 case AXIS_DESCENDANT_OR_SELF: |
|
952 fprintf(output, " 'descendant-or-self' "); break; |
|
953 case AXIS_FOLLOWING: |
|
954 fprintf(output, " 'following' "); break; |
|
955 case AXIS_FOLLOWING_SIBLING: |
|
956 fprintf(output, " 'following-siblings' "); break; |
|
957 case AXIS_NAMESPACE: |
|
958 fprintf(output, " 'namespace' "); break; |
|
959 case AXIS_PARENT: |
|
960 fprintf(output, " 'parent' "); break; |
|
961 case AXIS_PRECEDING: |
|
962 fprintf(output, " 'preceding' "); break; |
|
963 case AXIS_PRECEDING_SIBLING: |
|
964 fprintf(output, " 'preceding-sibling' "); break; |
|
965 case AXIS_SELF: |
|
966 fprintf(output, " 'self' "); break; |
|
967 } |
|
968 switch (test) { |
|
969 case NODE_TEST_NONE: |
|
970 fprintf(output, "'none' "); break; |
|
971 case NODE_TEST_TYPE: |
|
972 fprintf(output, "'type' "); break; |
|
973 case NODE_TEST_PI: |
|
974 fprintf(output, "'PI' "); break; |
|
975 case NODE_TEST_ALL: |
|
976 fprintf(output, "'all' "); break; |
|
977 case NODE_TEST_NS: |
|
978 fprintf(output, "'namespace' "); break; |
|
979 case NODE_TEST_NAME: |
|
980 fprintf(output, "'name' "); break; |
|
981 } |
|
982 switch (type) { |
|
983 case NODE_TYPE_NODE: |
|
984 fprintf(output, "'node' "); break; |
|
985 case NODE_TYPE_COMMENT: |
|
986 fprintf(output, "'comment' "); break; |
|
987 case NODE_TYPE_TEXT: |
|
988 fprintf(output, "'text' "); break; |
|
989 case NODE_TYPE_PI: |
|
990 fprintf(output, "'PI' "); break; |
|
991 } |
|
992 if (prefix != NULL) |
|
993 fprintf(output, "%s:", prefix); |
|
994 if (name != NULL) |
|
995 fprintf(output, "%s", (const char *) name); |
|
996 break; |
|
997 |
|
998 } |
|
999 case XPATH_OP_VALUE: { |
|
1000 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4; |
|
1001 |
|
1002 fprintf(output, "ELEM "); |
|
1003 xmlXPathDebugDumpObject(output, object, 0); |
|
1004 goto finish; |
|
1005 } |
|
1006 case XPATH_OP_VARIABLE: { |
|
1007 const xmlChar *prefix = op->value5; |
|
1008 const xmlChar *name = op->value4; |
|
1009 |
|
1010 if (prefix != NULL) |
|
1011 fprintf(output, "VARIABLE %s:%s", prefix, name); |
|
1012 else |
|
1013 fprintf(output, "VARIABLE %s", name); |
|
1014 break; |
|
1015 } |
|
1016 case XPATH_OP_FUNCTION: { |
|
1017 int nbargs = op->value; |
|
1018 const xmlChar *prefix = op->value5; |
|
1019 const xmlChar *name = op->value4; |
|
1020 |
|
1021 if (prefix != NULL) |
|
1022 fprintf(output, "FUNCTION %s:%s(%d args)", |
|
1023 prefix, name, nbargs); |
|
1024 else |
|
1025 fprintf(output, "FUNCTION %s(%d args)", name, nbargs); |
|
1026 break; |
|
1027 } |
|
1028 case XPATH_OP_ARG: fprintf(output, "ARG"); break; |
|
1029 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break; |
|
1030 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break; |
|
1031 #ifdef LIBXML_XPTR_ENABLED |
|
1032 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break; |
|
1033 #endif |
|
1034 default: |
|
1035 fprintf(output, "UNKNOWN %d\n", op->op); return; |
|
1036 } |
|
1037 fprintf(output, "\n"); |
|
1038 finish: |
|
1039 if (op->ch1 >= 0) |
|
1040 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1); |
|
1041 if (op->ch2 >= 0) |
|
1042 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1); |
|
1043 } |
|
1044 |
|
1045 /** |
|
1046 * xmlXPathDebugDumpCompExpr: |
|
1047 * @param output the FILE * for the output |
|
1048 * @param comp the precompiled XPath expression |
|
1049 * @param depth the indentation level. |
|
1050 * |
|
1051 * Dumps the tree of the compiled XPath expression. |
|
1052 */ |
|
1053 void |
|
1054 xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp, |
|
1055 int depth) { |
|
1056 int i; |
|
1057 char shift[100]; |
|
1058 |
|
1059 for (i = 0;((i < depth) && (i < 25));i++) |
|
1060 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
1061 shift[2 * i] = shift[2 * i + 1] = 0; |
|
1062 |
|
1063 fprintf(output, shift); |
|
1064 |
|
1065 if (comp == NULL) { |
|
1066 fprintf(output, "Compiled Expression is NULL\n"); |
|
1067 return; |
|
1068 } |
|
1069 fprintf(output, "Compiled Expression : %d elements\n", |
|
1070 comp->nbStep); |
|
1071 i = comp->last; |
|
1072 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1); |
|
1073 } |
|
1074 #endif /* LIBXML_DEBUG_ENABLED */ |
|
1075 |
|
1076 /************************************************************************ |
|
1077 * * |
|
1078 * Parser stacks related functions and macros * |
|
1079 * * |
|
1080 ************************************************************************/ |
|
1081 |
|
1082 /** |
|
1083 * valuePop: |
|
1084 * @param ctxt an XPath evaluation context |
|
1085 * |
|
1086 * Pops the top XPath object from the value stack |
|
1087 * |
|
1088 * Returns the XPath object just removed |
|
1089 */ |
|
1090 XMLPUBFUNEXPORT extern xmlXPathObjectPtr |
|
1091 valuePop(xmlXPathParserContextPtr ctxt) |
|
1092 { |
|
1093 xmlXPathObjectPtr ret; |
|
1094 |
|
1095 if (ctxt->valueNr <= 0) |
|
1096 return (0); |
|
1097 ctxt->valueNr--; |
|
1098 if (ctxt->valueNr > 0) |
|
1099 ctxt->value = ctxt->valueTab[ctxt->valueNr - 1]; |
|
1100 else |
|
1101 ctxt->value = NULL; |
|
1102 ret = ctxt->valueTab[ctxt->valueNr]; |
|
1103 ctxt->valueTab[ctxt->valueNr] = 0; |
|
1104 return (ret); |
|
1105 } |
|
1106 |
|
1107 /** |
|
1108 * valuePush: |
|
1109 * @param ctxt an XPath evaluation context |
|
1110 * @param value the XPath object |
|
1111 * |
|
1112 * Pushes a new XPath object on top of the value stack |
|
1113 * |
|
1114 * returns the number of items on the value stack |
|
1115 * |
|
1116 * OOM: possible --> returns 0 |
|
1117 */ |
|
1118 XMLPUBFUNEXPORT int |
|
1119 valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value) |
|
1120 { |
|
1121 if (ctxt->valueNr >= ctxt->valueMax) |
|
1122 { |
|
1123 xmlXPathObjectPtr* tmp; |
|
1124 // DONE: Fix xmlRealloc |
|
1125 tmp = (xmlXPathObjectPtr*)xmlRealloc(ctxt->valueTab, |
|
1126 ctxt->valueMax * |
|
1127 2 * sizeof(ctxt->valueTab[0])); |
|
1128 if (!tmp) { |
|
1129 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("realloc failed !\n")); |
|
1130 return (0); |
|
1131 } |
|
1132 ctxt->valueTab = tmp; |
|
1133 ctxt->valueMax *= 2; |
|
1134 } |
|
1135 ctxt->valueTab[ctxt->valueNr] = value; |
|
1136 ctxt->value = value; |
|
1137 return (ctxt->valueNr++); |
|
1138 } |
|
1139 |
|
1140 /** |
|
1141 * xmlXPathPopBoolean: |
|
1142 * @param ctxt an XPath parser context |
|
1143 * |
|
1144 * Pops a boolean from the stack, handling conversion if needed. |
|
1145 * Check error with #xmlXPathCheckError. |
|
1146 * |
|
1147 * Returns the boolean |
|
1148 */ |
|
1149 XMLPUBFUNEXPORT int |
|
1150 xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) { |
|
1151 xmlXPathObjectPtr obj; |
|
1152 int ret; |
|
1153 |
|
1154 obj = valuePop(ctxt); |
|
1155 if (obj == NULL) { |
|
1156 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1157 return(0); |
|
1158 } |
|
1159 if (obj->type != XPATH_BOOLEAN) |
|
1160 ret = xmlXPathCastToBoolean(obj); |
|
1161 else |
|
1162 ret = obj->boolval; |
|
1163 xmlXPathFreeObject(obj); |
|
1164 return(ret); |
|
1165 } |
|
1166 |
|
1167 /** |
|
1168 * xmlXPathPopNumber: |
|
1169 * @param ctxt an XPath parser context |
|
1170 * |
|
1171 * Pops a number from the stack, handling conversion if needed. |
|
1172 * Check error with #xmlXPathCheckError. |
|
1173 * |
|
1174 * Returns the number |
|
1175 */ |
|
1176 XMLPUBFUNEXPORT double |
|
1177 xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) { |
|
1178 xmlXPathObjectPtr obj; |
|
1179 double ret; |
|
1180 |
|
1181 obj = valuePop(ctxt); |
|
1182 if (obj == NULL) { |
|
1183 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1184 return(0); |
|
1185 } |
|
1186 if (obj->type != XPATH_NUMBER) |
|
1187 ret = xmlXPathCastToNumber(obj); |
|
1188 else |
|
1189 ret = obj->floatval; |
|
1190 xmlXPathFreeObject(obj); |
|
1191 return(ret); |
|
1192 } |
|
1193 |
|
1194 /** |
|
1195 * xmlXPathPopString: |
|
1196 * @param ctxt an XPath parser context |
|
1197 * |
|
1198 * Pops a string from the stack, handling conversion if needed. |
|
1199 * Check error with #xmlXPathCheckError. |
|
1200 * |
|
1201 * Returns the string |
|
1202 */ |
|
1203 XMLPUBFUNEXPORT xmlChar * |
|
1204 xmlXPathPopString (xmlXPathParserContextPtr ctxt) |
|
1205 { |
|
1206 xmlXPathObjectPtr obj; |
|
1207 xmlChar* ret; |
|
1208 |
|
1209 obj = valuePop(ctxt); |
|
1210 if (!obj) { |
|
1211 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1212 return(NULL); |
|
1213 } |
|
1214 ret = xmlXPathCastToString(obj); /* this does required strdup */ |
|
1215 |
|
1216 if (obj->stringval == ret) |
|
1217 obj->stringval = NULL; |
|
1218 xmlXPathFreeObject(obj); |
|
1219 return(ret); |
|
1220 } |
|
1221 |
|
1222 /** |
|
1223 * xmlXPathPopNodeSet: |
|
1224 * @param ctxt an XPath parser context |
|
1225 * |
|
1226 * Pops a node-set from the stack, handling conversion if needed. |
|
1227 * Check error with #xmlXPathCheckError. |
|
1228 * |
|
1229 * Returns the node-set |
|
1230 */ |
|
1231 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
1232 xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) { |
|
1233 xmlXPathObjectPtr obj; |
|
1234 xmlNodeSetPtr ret; |
|
1235 |
|
1236 if (!ctxt->value) { |
|
1237 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1238 return(NULL); |
|
1239 } |
|
1240 if (!xmlXPathStackIsNodeSet(ctxt)) { |
|
1241 xmlXPathSetTypeError(ctxt); |
|
1242 return(NULL); |
|
1243 } |
|
1244 obj = valuePop(ctxt); |
|
1245 ret = obj->nodesetval; |
|
1246 /* to fix memory leak of not clearing obj->user */ |
|
1247 if (obj->boolval && obj->user) |
|
1248 xmlFreeNodeList((xmlNodePtr) obj->user); |
|
1249 |
|
1250 xmlXPathFreeNodeSetList(obj); |
|
1251 return(ret); |
|
1252 } |
|
1253 |
|
1254 /** |
|
1255 * xmlXPathPopExternal: |
|
1256 * @param ctxt an XPath parser context |
|
1257 * |
|
1258 * Pops an external object from the stack, handling conversion if needed. |
|
1259 * Check error with #xmlXPathCheckError. |
|
1260 * |
|
1261 * Returns the object |
|
1262 */ |
|
1263 XMLPUBFUNEXPORT void * |
|
1264 xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) { |
|
1265 xmlXPathObjectPtr obj; |
|
1266 void* ret; |
|
1267 |
|
1268 if (!ctxt->value) { |
|
1269 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1270 return(NULL); |
|
1271 } |
|
1272 |
|
1273 if (ctxt->value->type != XPATH_USERS) { |
|
1274 xmlXPathSetTypeError(ctxt); |
|
1275 return(NULL); |
|
1276 } |
|
1277 obj = valuePop(ctxt); |
|
1278 ret = obj->user; |
|
1279 xmlXPathFreeObject(obj); |
|
1280 return(ret); |
|
1281 } |
|
1282 |
|
1283 /* |
|
1284 * Macros for accessing the content. Those should be used only by the parser, |
|
1285 * and not exported. |
|
1286 * |
|
1287 * Dirty macros, i.e. one need to make assumption on the context to use them |
|
1288 * |
|
1289 * CUR_PTR return the current pointer to the xmlChar to be parsed. |
|
1290 * CUR returns the current xmlChar value, i.e. a 8 bit value |
|
1291 * in ISO-Latin or UTF-8. |
|
1292 * This should be used internally by the parser |
|
1293 * only to compare to ASCII values otherwise it would break when |
|
1294 * running with UTF-8 encoding. |
|
1295 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only |
|
1296 * to compare on ASCII based substring. |
|
1297 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined |
|
1298 * strings within the parser. |
|
1299 * CURRENT Returns the current char value, with the full decoding of |
|
1300 * UTF-8 if we are using this mode. It returns an int. |
|
1301 * NEXT Skip to the next character, this does the proper decoding |
|
1302 * in UTF-8 mode. It also pop-up unfinished entities on the fly. |
|
1303 * It returns the pointer to the current xmlChar. |
|
1304 */ |
|
1305 |
|
1306 #define CUR (*ctxt->cur) |
|
1307 #define SKIP(val) ctxt->cur += (val) |
|
1308 #define NXT(val) ctxt->cur[(val)] |
|
1309 #define CUR_PTR ctxt->cur |
|
1310 #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l) |
|
1311 |
|
1312 #define COPY_BUF(len,b,i,v) \ |
|
1313 if (len == 1) b[i++] = (xmlChar) v; \ |
|
1314 else i += xmlCopyChar(len,&b[i],v) |
|
1315 |
|
1316 #define NEXTL(k) ctxt->cur += k |
|
1317 |
|
1318 // XMLENGINE: NEXT was replaced with ctxt->cur++ since *ctxt->cur always !=0 there |
|
1319 // OOM: never |
|
1320 #define SKIP_BLANKS while (IS_BLANK_CH(*(ctxt->cur))) ctxt->cur++ |
|
1321 |
|
1322 #define CURRENT (*ctxt->cur) |
|
1323 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) |
|
1324 |
|
1325 |
|
1326 #ifndef DBL_DIG |
|
1327 #define DBL_DIG 16 |
|
1328 #endif |
|
1329 #ifndef DBL_EPSILON |
|
1330 #define DBL_EPSILON 1E-9 |
|
1331 #endif |
|
1332 |
|
1333 #define UPPER_DOUBLE 1E9 |
|
1334 #define LOWER_DOUBLE 1E-5 |
|
1335 |
|
1336 #define INTEGER_DIGITS DBL_DIG |
|
1337 #define FRACTION_DIGITS (DBL_DIG + 1) |
|
1338 #define EXPONENT_DIGITS (3 + 2) |
|
1339 |
|
1340 #if (_MSC_VER >= 1300) && (WINVER < 0x0500) |
|
1341 //VC7 or later, building with pre-VC7 runtime libraries |
|
1342 //extern "C" long _ftol( double ); //defined by VC6 C libs |
|
1343 //extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource ); } |
|
1344 #endif |
|
1345 |
|
1346 /** |
|
1347 * xmlXPathFormatNumber: |
|
1348 * @param number number to format |
|
1349 * @param buffer output buffer |
|
1350 * @param buffersize size of output buffer |
|
1351 * |
|
1352 * Convert the number into a string representation. |
|
1353 * |
|
1354 * OOM: never |
|
1355 */ |
|
1356 static void |
|
1357 xmlXPathFormatNumber(double number, char buffer[], int buffersize) |
|
1358 { |
|
1359 |
|
1360 switch (xmlXPathIsInf(number)) { |
|
1361 case 1: |
|
1362 if (buffersize > (int)sizeof("Infinity")) |
|
1363 snprintf(buffer, buffersize, "Infinity"); |
|
1364 break; |
|
1365 case -1: |
|
1366 if (buffersize > (int)sizeof("-Infinity")) |
|
1367 snprintf(buffer, buffersize, "-Infinity"); |
|
1368 break; |
|
1369 default: |
|
1370 if (xmlXPathIsNaN(number)) { |
|
1371 if (buffersize > (int)sizeof("NaN")) |
|
1372 snprintf(buffer, buffersize, "NaN"); |
|
1373 } else |
|
1374 if (number == 0 && xmlXPathGetSign(number) != 0) { |
|
1375 snprintf(buffer, buffersize, "0"); |
|
1376 } else |
|
1377 if (number == ((int) number)) { |
|
1378 char work[30]; |
|
1379 char *ptr, *cur; |
|
1380 int res, value = (int) number; |
|
1381 |
|
1382 ptr = &buffer[0]; |
|
1383 if (value < 0) { |
|
1384 *ptr++ = '-'; |
|
1385 value = -value; |
|
1386 } |
|
1387 if (value == 0) { |
|
1388 *ptr++ = '0'; |
|
1389 } else { |
|
1390 cur = &work[0]; |
|
1391 while (value != 0) { |
|
1392 res = value % 10; |
|
1393 value = value / 10; |
|
1394 *cur++ = '0' + res; |
|
1395 } |
|
1396 cur--; |
|
1397 while ((cur >= &work[0]) && (ptr - buffer < buffersize)) { |
|
1398 *ptr++ = *cur--; |
|
1399 } |
|
1400 } |
|
1401 if (ptr - buffer < buffersize) { |
|
1402 *ptr = 0; |
|
1403 } else if (buffersize > 0) { |
|
1404 ptr--; |
|
1405 *ptr = 0; |
|
1406 } |
|
1407 } else { |
|
1408 /* 3 is sign, decimal point, and terminating zero */ |
|
1409 char work[DBL_DIG + EXPONENT_DIGITS + 3]; |
|
1410 int integer_place, fraction_place; |
|
1411 char *ptr; |
|
1412 char *after_fraction; |
|
1413 double absolute_value; |
|
1414 int size; |
|
1415 |
|
1416 absolute_value = fabs(number); |
|
1417 |
|
1418 /* |
|
1419 * First choose format - scientific or regular floating point. |
|
1420 * In either case, result is in work, and after_fraction points |
|
1421 * just past the fractional part. |
|
1422 */ |
|
1423 if (((absolute_value > UPPER_DOUBLE) || (absolute_value < LOWER_DOUBLE)) && |
|
1424 (absolute_value != 0.0)) |
|
1425 { |
|
1426 /* Use scientific notation */ |
|
1427 integer_place = DBL_DIG + EXPONENT_DIGITS + 1; |
|
1428 fraction_place = DBL_DIG - 1; |
|
1429 snprintf(work, sizeof(work),"%*.*e", |
|
1430 integer_place, fraction_place, number); |
|
1431 after_fraction = strchr(work + DBL_DIG, 'e'); |
|
1432 } |
|
1433 else { |
|
1434 /* Use regular notation */ |
|
1435 if (absolute_value > 0.0) |
|
1436 integer_place = 1 + (int)log10(absolute_value); |
|
1437 else |
|
1438 integer_place = 0; |
|
1439 |
|
1440 fraction_place = (integer_place > 0) |
|
1441 ? DBL_DIG - integer_place |
|
1442 : DBL_DIG; |
|
1443 size = snprintf(work, sizeof(work), "%0.*f", fraction_place, number); |
|
1444 after_fraction = work + size; |
|
1445 } |
|
1446 |
|
1447 /* Remove fractional trailing zeroes */ |
|
1448 ptr = after_fraction; |
|
1449 while (*(--ptr) == '0') {}// EMPTY LOOP |
|
1450 |
|
1451 if (*ptr != '.') |
|
1452 ptr++; |
|
1453 while ((*ptr++ = *after_fraction++) != 0){} // EMPTY LOOP |
|
1454 |
|
1455 /* Finally copy result back to caller */ |
|
1456 size = strlen(work) + 1; |
|
1457 if (size > buffersize) { |
|
1458 work[buffersize - 1] = 0; |
|
1459 size = buffersize; |
|
1460 } |
|
1461 memmove(buffer, work, size); |
|
1462 } |
|
1463 break; |
|
1464 } |
|
1465 } |
|
1466 |
|
1467 |
|
1468 /************************************************************************ |
|
1469 * * |
|
1470 * Routines to handle NodeSets * |
|
1471 * * |
|
1472 ************************************************************************/ |
|
1473 |
|
1474 /** |
|
1475 * xmlXPathOrderDocElems: |
|
1476 * @param doc an input document |
|
1477 * |
|
1478 * Call this routine to speed up XPath computation on static documents. |
|
1479 * This stamps all the element nodes with the document order |
|
1480 * Like for line information, the order is kept in the element->content |
|
1481 * field, the value stored is actually - the node number (starting at -1) |
|
1482 * to be able to differentiate from line numbers. |
|
1483 * |
|
1484 * Returns the number of elements found in the document or -1 in case |
|
1485 * of error. |
|
1486 */ |
|
1487 XMLPUBFUNEXPORT long |
|
1488 xmlXPathOrderDocElems(xmlDocPtr doc) { |
|
1489 long count = 0; |
|
1490 xmlNodePtr cur; |
|
1491 |
|
1492 if (!doc) |
|
1493 return(-1); |
|
1494 cur = doc->children; |
|
1495 while (cur) { |
|
1496 if (cur->type == XML_ELEMENT_NODE) { |
|
1497 cur->content = (xmlChar*)((void *) (-(++count))); |
|
1498 if (cur->children != NULL) { |
|
1499 cur = cur->children; |
|
1500 continue; |
|
1501 } |
|
1502 } |
|
1503 if (cur->next) { |
|
1504 cur = cur->next; |
|
1505 continue; |
|
1506 } |
|
1507 do { |
|
1508 cur = cur->parent; |
|
1509 if (!cur) |
|
1510 break; |
|
1511 if (cur == (xmlNodePtr) doc) { |
|
1512 cur = NULL; |
|
1513 break; |
|
1514 } |
|
1515 if (cur->next) { |
|
1516 cur = cur->next; |
|
1517 break; |
|
1518 } |
|
1519 } while (cur); |
|
1520 } |
|
1521 return(count); |
|
1522 } |
|
1523 |
|
1524 /** |
|
1525 * xmlXPathCmpNodes: |
|
1526 * @param node1 the first node |
|
1527 * @param node2 the second node |
|
1528 * |
|
1529 * Compare two nodes w.r.t document order |
|
1530 * |
|
1531 * Returns -2 in case of error 1 if first point < second point, 0 if |
|
1532 * it's the same node, -1 otherwise |
|
1533 * |
|
1534 * OOM: never |
|
1535 */ |
|
1536 XMLPUBFUNEXPORT int |
|
1537 xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) { |
|
1538 int depth1, depth2; |
|
1539 int attr1 = 0, attr2 = 0; |
|
1540 xmlNodePtr attrNode1 = NULL, attrNode2 = NULL; |
|
1541 xmlNodePtr cur, root; |
|
1542 |
|
1543 if ((node1 == NULL) || (node2 == NULL)) |
|
1544 return(-2); |
|
1545 /* |
|
1546 * a couple of optimizations which will avoid computations in most cases |
|
1547 */ |
|
1548 if (node1->type == XML_ATTRIBUTE_NODE) { |
|
1549 attr1 = 1; |
|
1550 attrNode1 = node1; |
|
1551 node1 = node1->parent; |
|
1552 } |
|
1553 if (node2->type == XML_ATTRIBUTE_NODE) { |
|
1554 attr2 = 1; |
|
1555 attrNode2 = node2; |
|
1556 node2 = node2->parent; |
|
1557 } |
|
1558 if (node1 == node2) { |
|
1559 if (attr1 == attr2) { |
|
1560 /* not required, but we keep attributes in order */ |
|
1561 if (attr1 != 0) { |
|
1562 cur = attrNode2->prev; |
|
1563 while (cur != NULL) { |
|
1564 if (cur == attrNode1) |
|
1565 return (1); |
|
1566 cur = cur->prev; |
|
1567 } |
|
1568 return (-1); |
|
1569 } |
|
1570 return(0); |
|
1571 } |
|
1572 if (attr2 == 1) |
|
1573 return(1); |
|
1574 return(-1); |
|
1575 } |
|
1576 if ((node1->type == XML_NAMESPACE_DECL) || |
|
1577 (node2->type == XML_NAMESPACE_DECL)) |
|
1578 return(1); |
|
1579 if (node1 == node2->prev) |
|
1580 return(1); |
|
1581 if (node1 == node2->next) |
|
1582 return(-1); |
|
1583 |
|
1584 /* |
|
1585 * Speedup using document order if availble. |
|
1586 */ |
|
1587 if ((node1->type == XML_ELEMENT_NODE) && |
|
1588 (node2->type == XML_ELEMENT_NODE) && |
|
1589 (0 > (long) node1->content) && |
|
1590 (0 > (long) node2->content) && |
|
1591 (node1->doc == node2->doc)) { |
|
1592 long l1, l2; |
|
1593 |
|
1594 l1 = -((long) node1->content); |
|
1595 l2 = -((long) node2->content); |
|
1596 if (l1 < l2) |
|
1597 return(1); |
|
1598 if (l1 > l2) |
|
1599 return(-1); |
|
1600 } |
|
1601 |
|
1602 /* |
|
1603 * compute depth to root |
|
1604 */ |
|
1605 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { |
|
1606 if (cur == node1) |
|
1607 return(1); |
|
1608 depth2++; |
|
1609 } |
|
1610 root = cur; |
|
1611 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { |
|
1612 if (cur == node2) |
|
1613 return(-1); |
|
1614 depth1++; |
|
1615 } |
|
1616 /* |
|
1617 * Distinct document (or distinct entities :-( ) case. |
|
1618 */ |
|
1619 if (root != cur) { |
|
1620 return(-2); |
|
1621 } |
|
1622 /* |
|
1623 * get the nearest common ancestor. |
|
1624 */ |
|
1625 while (depth1 > depth2) { |
|
1626 depth1--; |
|
1627 node1 = node1->parent; |
|
1628 } |
|
1629 while (depth2 > depth1) { |
|
1630 depth2--; |
|
1631 node2 = node2->parent; |
|
1632 } |
|
1633 while (node1->parent != node2->parent) { |
|
1634 node1 = node1->parent; |
|
1635 node2 = node2->parent; |
|
1636 /* should not happen but just in case ... */ |
|
1637 if ((node1 == NULL) || (node2 == NULL)) |
|
1638 return(-2); |
|
1639 } |
|
1640 /* |
|
1641 * Find who's first. |
|
1642 */ |
|
1643 if (node1 == node2->prev) |
|
1644 return(1); |
|
1645 if (node1 == node2->next) |
|
1646 return(-1); |
|
1647 /* |
|
1648 * Speedup using document order if availble. |
|
1649 */ |
|
1650 if ((node1->type == XML_ELEMENT_NODE) && |
|
1651 (node2->type == XML_ELEMENT_NODE) && |
|
1652 (0 > (long) node1->content) && |
|
1653 (0 > (long) node2->content) && |
|
1654 (node1->doc == node2->doc)) { |
|
1655 long l1, l2; |
|
1656 |
|
1657 l1 = -((long) node1->content); |
|
1658 l2 = -((long) node2->content); |
|
1659 if (l1 < l2) |
|
1660 return(1); |
|
1661 if (l1 > l2) |
|
1662 return(-1); |
|
1663 } |
|
1664 |
|
1665 for (cur = node1->next;cur != NULL;cur = cur->next) |
|
1666 if (cur == node2) |
|
1667 return(1); |
|
1668 return(-1); /* assume there is no sibling list corruption */ |
|
1669 } |
|
1670 |
|
1671 /** |
|
1672 * xmlXPathNodeSetSort: |
|
1673 * @param set the node set |
|
1674 * |
|
1675 * Sort the node set in document order |
|
1676 * |
|
1677 * OOM: never |
|
1678 */ |
|
1679 XMLPUBFUNEXPORT void |
|
1680 xmlXPathNodeSetSort(xmlNodeSetPtr set) { |
|
1681 int i, j, incr, len; |
|
1682 xmlNodePtr tmp; |
|
1683 |
|
1684 if (set == NULL) |
|
1685 return; |
|
1686 |
|
1687 /* Use Shell's sort to sort the node-set */ |
|
1688 len = set->nodeNr; |
|
1689 for (incr = len / 2; incr > 0; incr /= 2) { |
|
1690 for (i = incr; i < len; i++) { |
|
1691 j = i - incr; |
|
1692 while (j >= 0) { |
|
1693 if (xmlXPathCmpNodes(set->nodeTab[j], set->nodeTab[j + incr]) == -1) |
|
1694 { |
|
1695 tmp = set->nodeTab[j]; |
|
1696 set->nodeTab[j] = set->nodeTab[j + incr]; |
|
1697 set->nodeTab[j + incr] = tmp; |
|
1698 j -= incr; |
|
1699 } else |
|
1700 break; |
|
1701 } |
|
1702 } |
|
1703 } |
|
1704 } |
|
1705 |
|
1706 #define XML_NODESET_DEFAULT 10 |
|
1707 /** |
|
1708 * xmlXPathNodeSetDupNs: |
|
1709 * @param node the parent node of the namespace XPath node |
|
1710 * @param ns the libxml namespace declaration node. |
|
1711 * |
|
1712 * Namespace node in libxml don't match the XPath semantic. In a node set |
|
1713 * the namespace nodes are duplicated and the next pointer is set to the |
|
1714 * parent node in the XPath semantic. |
|
1715 * |
|
1716 * Returns the newly created object. |
|
1717 * |
|
1718 * OOM: possible --> returns NULL for valid arguments, sets OOM flag |
|
1719 */ |
|
1720 static xmlNodePtr |
|
1721 xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) { |
|
1722 xmlNsPtr cur; |
|
1723 |
|
1724 if (!ns || ns->type != XML_NAMESPACE_DECL) |
|
1725 return(NULL); |
|
1726 if (!node || node->type == XML_NAMESPACE_DECL) |
|
1727 return((xmlNodePtr) ns); |
|
1728 |
|
1729 /* |
|
1730 * Allocate a new Namespace and fill the fields. |
|
1731 */ |
|
1732 cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); |
|
1733 if (!cur) { |
|
1734 xmlXPathErrMemory(NULL, EMBED_ERRTXT("duplicating namespace\n")); |
|
1735 return(NULL); |
|
1736 } |
|
1737 memset(cur, 0, sizeof(xmlNs)); |
|
1738 cur->type = XML_NAMESPACE_DECL; |
|
1739 if (ns->href) |
|
1740 { |
|
1741 cur->href = xmlStrdup(ns->href); |
|
1742 if(!cur->href) |
|
1743 goto OOM; |
|
1744 } |
|
1745 if (ns->prefix) |
|
1746 { |
|
1747 cur->prefix = xmlStrdup(ns->prefix); |
|
1748 if(!cur->prefix) |
|
1749 goto OOM; |
|
1750 } |
|
1751 cur->next = (xmlNsPtr) node; |
|
1752 return((xmlNodePtr) cur); |
|
1753 //--------------------- |
|
1754 OOM: |
|
1755 xmlFree(cur); |
|
1756 return NULL; |
|
1757 } |
|
1758 |
|
1759 /** |
|
1760 * xmlXPathNodeSetFreeNs: |
|
1761 * @param ns the XPath namespace node found in a nodeset. |
|
1762 * |
|
1763 * Namespace nodes in libxml don't match the XPath semantic. In a node set |
|
1764 * the namespace nodes are duplicated and the next pointer is set to the |
|
1765 * parent node in the XPath semantic. Check if such a node needs to be freed |
|
1766 * |
|
1767 * OOM: never |
|
1768 */ |
|
1769 XMLPUBFUNEXPORT void |
|
1770 xmlXPathNodeSetFreeNs(xmlNsPtr ns) { |
|
1771 if (!ns || ns->type != XML_NAMESPACE_DECL) |
|
1772 return; |
|
1773 |
|
1774 if (ns->next && ns->next->type != XML_NAMESPACE_DECL) { |
|
1775 if (ns->href) |
|
1776 xmlFree((xmlChar*)ns->href); |
|
1777 if (ns->prefix) |
|
1778 xmlFree((xmlChar*)ns->prefix); |
|
1779 xmlFree(ns); |
|
1780 } |
|
1781 } |
|
1782 |
|
1783 /** |
|
1784 * xmlXPathNodeSetCreate: |
|
1785 * @param val an initial xmlNodePtr, or NULL |
|
1786 * |
|
1787 * Create a new xmlNodeSetPtr of type double and of value val |
|
1788 * |
|
1789 * Returns the newly created object. |
|
1790 * |
|
1791 * OOM: possible --> returns NULL and sets OOM flag |
|
1792 */ |
|
1793 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
1794 xmlXPathNodeSetCreate(xmlNodePtr val) { |
|
1795 xmlNodeSetPtr ret; |
|
1796 LOAD_GS_DIRECT |
|
1797 |
|
1798 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet)); |
|
1799 if (!ret) { |
|
1800 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n")); |
|
1801 return(NULL); |
|
1802 } |
|
1803 memset(ret, 0 , (size_t) sizeof(xmlNodeSet)); |
|
1804 |
|
1805 if (val) { |
|
1806 xmlNodePtr nval; |
|
1807 |
|
1808 ret->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); |
|
1809 if (!ret->nodeTab) { |
|
1810 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n")); |
|
1811 goto OOM; |
|
1812 } |
|
1813 memset(ret->nodeTab, 0, XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
1814 ret->nodeMax = XML_NODESET_DEFAULT; |
|
1815 if (val->type == XML_NAMESPACE_DECL) { |
|
1816 xmlNsPtr ns = (xmlNsPtr) val; |
|
1817 nval = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
1818 |
|
1819 if(OOM_FLAG) |
|
1820 goto OOM; |
|
1821 } else { |
|
1822 nval = val; |
|
1823 } |
|
1824 ret->nodeTab[ret->nodeNr++] = nval; |
|
1825 } |
|
1826 return(ret); |
|
1827 //------------------- |
|
1828 OOM: |
|
1829 xmlFree(ret); |
|
1830 return(NULL); |
|
1831 } |
|
1832 |
|
1833 /** |
|
1834 * xmlXPathNodeSetContains: |
|
1835 * @param cur the node-set |
|
1836 * @param val the node |
|
1837 * |
|
1838 * checks whether cur contains val |
|
1839 * |
|
1840 * Returns true (1) if cur contains val, false (0) otherwise |
|
1841 * |
|
1842 * OOM: never |
|
1843 */ |
|
1844 XMLPUBFUNEXPORT int |
|
1845 xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) { |
|
1846 int i; |
|
1847 |
|
1848 if (val->type == XML_NAMESPACE_DECL) { |
|
1849 for (i = 0; i < cur->nodeNr; i++) { |
|
1850 if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
1851 xmlNsPtr ns1, ns2; |
|
1852 |
|
1853 ns1 = (xmlNsPtr) val; |
|
1854 ns2 = (xmlNsPtr) cur->nodeTab[i]; |
|
1855 if (ns1 == ns2) |
|
1856 return(1); |
|
1857 if ((ns1->next != NULL) && |
|
1858 (ns2->next == ns1->next) && |
|
1859 (xmlStrEqual(ns1->prefix, ns2->prefix))) |
|
1860 return(1); |
|
1861 } |
|
1862 } |
|
1863 } else { |
|
1864 for (i = 0; i < cur->nodeNr; i++) { |
|
1865 if (cur->nodeTab[i] == val) |
|
1866 return(1); |
|
1867 } |
|
1868 } |
|
1869 return(0); |
|
1870 } |
|
1871 |
|
1872 |
|
1873 |
|
1874 /* |
|
1875 OPTIMIZATION: xmlXPathNodeSetAddNs, xmlXPathNodeSetAdd and xmlXPathNodeSetAddUnique are the same code |
|
1876 |
|
1877 */ |
|
1878 |
|
1879 /** |
|
1880 * xmlXPathNodeSetAddNs: |
|
1881 * @param cur the initial node set |
|
1882 * @param node the hosting node |
|
1883 * @param ns a the namespace node |
|
1884 * |
|
1885 * add a new namespace node to an existing NodeSet |
|
1886 */ |
|
1887 XMLPUBFUNEXPORT void |
|
1888 xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) |
|
1889 { |
|
1890 int i; |
|
1891 |
|
1892 if (!ns || !node || |
|
1893 (ns->type != XML_NAMESPACE_DECL) || |
|
1894 (node->type != XML_ELEMENT_NODE)) |
|
1895 return; |
|
1896 |
|
1897 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
1898 /* |
|
1899 * prevent duplicates |
|
1900 */ |
|
1901 for (i = 0;i < cur->nodeNr;i++) { |
|
1902 if (cur->nodeTab[i] && |
|
1903 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) && |
|
1904 (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) && |
|
1905 (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix))) |
|
1906 { |
|
1907 return; |
|
1908 } |
|
1909 } |
|
1910 |
|
1911 /* |
|
1912 * grow the nodeTab if needed |
|
1913 */ |
|
1914 if (cur->nodeMax == 0) { |
|
1915 cur->nodeTab = (xmlNodePtr*) xmlMalloc(XML_NODESET_DEFAULT * |
|
1916 sizeof(xmlNodePtr)); |
|
1917 if (!cur->nodeTab) { |
|
1918 OOM_exit: |
|
1919 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1920 return; |
|
1921 } |
|
1922 memset(cur->nodeTab, 0 , |
|
1923 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
1924 cur->nodeMax = XML_NODESET_DEFAULT; |
|
1925 } else if (cur->nodeNr == cur->nodeMax) { |
|
1926 xmlNodePtr *temp; |
|
1927 |
|
1928 cur->nodeMax *= 2; |
|
1929 temp = (xmlNodePtr*) xmlRealloc(cur->nodeTab, cur->nodeMax * |
|
1930 sizeof(xmlNodePtr)); |
|
1931 if (!temp) { |
|
1932 cur->nodeMax /= 2; |
|
1933 goto OOM_exit; |
|
1934 } |
|
1935 cur->nodeTab = temp; |
|
1936 } |
|
1937 cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns); |
|
1938 } |
|
1939 |
|
1940 /** |
|
1941 * xmlXPathNodeSetAdd: |
|
1942 * @param cur the initial node set |
|
1943 * @param val a new xmlNodePtr |
|
1944 * |
|
1945 * add a new xmlNodePtr to an existing NodeSet |
|
1946 */ |
|
1947 XMLPUBFUNEXPORT void |
|
1948 xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
|
1949 int i; |
|
1950 |
|
1951 if (!val) return; |
|
1952 |
|
1953 #if 0 |
|
1954 if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) |
|
1955 return; /* an XSLT fake node */ |
|
1956 #endif |
|
1957 |
|
1958 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
1959 /* |
|
1960 * prevent duplcates |
|
1961 */ |
|
1962 for (i = 0;i < cur->nodeNr;i++) |
|
1963 if (cur->nodeTab[i] == val) return; |
|
1964 |
|
1965 /* |
|
1966 * grow the nodeTab if needed |
|
1967 */ |
|
1968 if (cur->nodeMax == 0) |
|
1969 { |
|
1970 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); |
|
1971 if (!cur->nodeTab) { |
|
1972 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1973 return; |
|
1974 } |
|
1975 memset(cur->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
1976 cur->nodeMax = XML_NODESET_DEFAULT; |
|
1977 } |
|
1978 else if (cur->nodeNr == cur->nodeMax) |
|
1979 { |
|
1980 xmlNodePtr* temp; |
|
1981 |
|
1982 cur->nodeMax *= 2; |
|
1983 temp = (xmlNodePtr*) xmlRealloc(cur->nodeTab, cur->nodeMax * sizeof(xmlNodePtr)); |
|
1984 if (!temp) { |
|
1985 cur->nodeMax /= 2; |
|
1986 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1987 return; |
|
1988 } |
|
1989 cur->nodeTab = temp; |
|
1990 } |
|
1991 |
|
1992 if (val->type == XML_NAMESPACE_DECL) |
|
1993 { |
|
1994 xmlNsPtr ns = (xmlNsPtr) val; |
|
1995 |
|
1996 cur->nodeTab[cur->nodeNr++] = |
|
1997 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
1998 } |
|
1999 else |
|
2000 { |
|
2001 cur->nodeTab[cur->nodeNr++] = val; |
|
2002 } |
|
2003 } |
|
2004 |
|
2005 /** |
|
2006 * xmlXPathNodeSetAddUnique: |
|
2007 * @param cur the initial node set |
|
2008 * @param val a new xmlNodePtr |
|
2009 * |
|
2010 * add a new xmlNodePtr to an existing NodeSet, optimized version |
|
2011 * when we are sure the node is not already in the set. |
|
2012 */ |
|
2013 XMLPUBFUNEXPORT void |
|
2014 xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { |
|
2015 if (val == NULL) return; |
|
2016 |
|
2017 |
|
2018 #if 0 |
|
2019 if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) |
|
2020 return; /* an XSLT fake node */ |
|
2021 #endif |
|
2022 |
|
2023 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2024 /* |
|
2025 * grow the nodeTab if needed |
|
2026 */ |
|
2027 if (cur->nodeMax == 0) { |
|
2028 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); |
|
2029 |
|
2030 if (cur->nodeTab == NULL) { |
|
2031 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
2032 return; |
|
2033 } |
|
2034 memset(cur->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
2035 cur->nodeMax = XML_NODESET_DEFAULT; |
|
2036 } else if (cur->nodeNr == cur->nodeMax) { |
|
2037 xmlNodePtr *temp; |
|
2038 |
|
2039 cur->nodeMax *= 2; |
|
2040 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * sizeof(xmlNodePtr)); |
|
2041 if (temp == NULL) { |
|
2042 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
2043 return; |
|
2044 } |
|
2045 cur->nodeTab = temp; |
|
2046 } |
|
2047 if (val->type == XML_NAMESPACE_DECL) { |
|
2048 xmlNsPtr ns = (xmlNsPtr) val; |
|
2049 |
|
2050 cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
2051 } else |
|
2052 cur->nodeTab[cur->nodeNr++] = val; |
|
2053 } |
|
2054 |
|
2055 /** |
|
2056 * xmlXPathNodeSetMerge: |
|
2057 * @param val1 the first NodeSet or NULL |
|
2058 * @param val2 the second NodeSet |
|
2059 * |
|
2060 * Merges two nodesets, all nodes from val2 are added to val1 |
|
2061 * if val1 is NULL, a new set is created and copied from val2 |
|
2062 * |
|
2063 * Returns val1 once extended or NULL in case of error. |
|
2064 */ |
|
2065 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2066 xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
|
2067 int i, j, initNr, skip; |
|
2068 |
|
2069 if (!val2) |
|
2070 return(val1); |
|
2071 if (!val1) { |
|
2072 val1 = xmlXPathNodeSetCreate(NULL); |
|
2073 } |
|
2074 |
|
2075 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2076 initNr = val1->nodeNr; |
|
2077 |
|
2078 for (i = 0;i < val2->nodeNr;i++) { |
|
2079 /* |
|
2080 * check against duplicates |
|
2081 */ |
|
2082 skip = 0; |
|
2083 for (j = 0; j < initNr; j++) { |
|
2084 if (val1->nodeTab[j] == val2->nodeTab[i]) { |
|
2085 skip = 1; |
|
2086 break; |
|
2087 } else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) && |
|
2088 (val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) { |
|
2089 xmlNsPtr ns1, ns2; |
|
2090 ns1 = (xmlNsPtr) val1->nodeTab[j]; |
|
2091 ns2 = (xmlNsPtr) val2->nodeTab[i]; |
|
2092 if ((ns1->next == ns2->next) && |
|
2093 (xmlStrEqual(ns1->prefix, ns2->prefix))) { |
|
2094 skip = 1; |
|
2095 break; |
|
2096 } |
|
2097 } |
|
2098 } |
|
2099 if (skip) |
|
2100 continue; |
|
2101 |
|
2102 /* |
|
2103 * grow the nodeTab if needed |
|
2104 */ |
|
2105 if (val1->nodeMax == 0) { |
|
2106 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
|
2107 sizeof(xmlNodePtr)); |
|
2108 if (val1->nodeTab == NULL) { |
|
2109 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2110 return(NULL); |
|
2111 } |
|
2112 memset(val1->nodeTab, 0 , |
|
2113 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
2114 val1->nodeMax = XML_NODESET_DEFAULT; |
|
2115 } else if (val1->nodeNr == val1->nodeMax) { |
|
2116 xmlNodePtr *temp; |
|
2117 |
|
2118 val1->nodeMax *= 2; |
|
2119 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * |
|
2120 sizeof(xmlNodePtr)); |
|
2121 if (temp == NULL) { |
|
2122 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2123 return(NULL); |
|
2124 } |
|
2125 val1->nodeTab = temp; |
|
2126 } |
|
2127 if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
2128 xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; |
|
2129 |
|
2130 val1->nodeTab[val1->nodeNr++] = |
|
2131 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
2132 } else |
|
2133 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; |
|
2134 } |
|
2135 |
|
2136 return(val1); |
|
2137 } |
|
2138 |
|
2139 /** |
|
2140 * xmlXPathNodeSetMergeUnique: |
|
2141 * @param val1 the first NodeSet or NULL |
|
2142 * @param val2 the second NodeSet |
|
2143 * |
|
2144 * Merges two nodesets, all nodes from val2 are added to val1 |
|
2145 * if val1 is NULL, a new set is created and copied from val2 |
|
2146 * |
|
2147 * Returns val1 once extended or NULL in case of error. |
|
2148 */ |
|
2149 static xmlNodeSetPtr |
|
2150 xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) |
|
2151 { |
|
2152 int i; |
|
2153 |
|
2154 if (val2 == NULL) return(val1); |
|
2155 if (val1 == NULL) { |
|
2156 val1 = xmlXPathNodeSetCreate(NULL); |
|
2157 } |
|
2158 |
|
2159 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2160 |
|
2161 for (i = 0;i < val2->nodeNr;i++) { |
|
2162 /* |
|
2163 * grow the nodeTab if needed |
|
2164 */ |
|
2165 if (val1->nodeMax == 0) { |
|
2166 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
|
2167 sizeof(xmlNodePtr)); |
|
2168 if (val1->nodeTab == NULL) { |
|
2169 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2170 return(NULL); |
|
2171 } |
|
2172 memset(val1->nodeTab, 0 , |
|
2173 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
2174 val1->nodeMax = XML_NODESET_DEFAULT; |
|
2175 } else if (val1->nodeNr == val1->nodeMax) { |
|
2176 xmlNodePtr *temp; |
|
2177 |
|
2178 val1->nodeMax *= 2; |
|
2179 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * |
|
2180 sizeof(xmlNodePtr)); |
|
2181 if (temp == NULL) { |
|
2182 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2183 return(NULL); |
|
2184 } |
|
2185 val1->nodeTab = temp; |
|
2186 } |
|
2187 if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
2188 xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; |
|
2189 |
|
2190 val1->nodeTab[val1->nodeNr++] = |
|
2191 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
2192 } else |
|
2193 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; |
|
2194 } |
|
2195 |
|
2196 return(val1); |
|
2197 } |
|
2198 |
|
2199 /** |
|
2200 * xmlXPathNodeSetDel: |
|
2201 * @param cur the initial node set |
|
2202 * @param val an xmlNodePtr |
|
2203 * |
|
2204 * Removes an xmlNodePtr from an existing NodeSet |
|
2205 */ |
|
2206 XMLPUBFUNEXPORT void |
|
2207 xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) { |
|
2208 int i; |
|
2209 |
|
2210 if (cur == NULL) return; |
|
2211 if (val == NULL) return; |
|
2212 |
|
2213 /* |
|
2214 * find node in nodeTab |
|
2215 */ |
|
2216 for (i = 0;i < cur->nodeNr;i++) |
|
2217 if (cur->nodeTab[i] == val) break; |
|
2218 |
|
2219 if (i >= cur->nodeNr) { /* not found */ |
|
2220 #ifdef DEBUG |
|
2221 xmlGenericError(xmlGenericErrorContext, |
|
2222 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n", |
|
2223 val->name); |
|
2224 #endif |
|
2225 return; |
|
2226 } |
|
2227 if ((cur->nodeTab[i] != NULL) && |
|
2228 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL)) |
|
2229 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]); |
|
2230 cur->nodeNr--; |
|
2231 for (;i < cur->nodeNr;i++) |
|
2232 cur->nodeTab[i] = cur->nodeTab[i + 1]; |
|
2233 cur->nodeTab[cur->nodeNr] = NULL; |
|
2234 } |
|
2235 |
|
2236 /** |
|
2237 * xmlXPathNodeSetRemove: |
|
2238 * @param cur the initial node set |
|
2239 * @param val the index to remove |
|
2240 * |
|
2241 * Removes an entry from an existing NodeSet list. |
|
2242 */ |
|
2243 XMLPUBFUNEXPORT void |
|
2244 xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) { |
|
2245 if (!cur || val >= cur->nodeNr) |
|
2246 return; |
|
2247 |
|
2248 if (cur->nodeTab[val] && |
|
2249 cur->nodeTab[val]->type == XML_NAMESPACE_DECL) |
|
2250 { |
|
2251 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]); |
|
2252 } |
|
2253 cur->nodeNr--; |
|
2254 for (;val < cur->nodeNr;val++) |
|
2255 cur->nodeTab[val] = cur->nodeTab[val + 1]; |
|
2256 cur->nodeTab[cur->nodeNr] = NULL; |
|
2257 } |
|
2258 |
|
2259 /** |
|
2260 * xmlXPathFreeNodeSet: |
|
2261 * @param obj the xmlNodeSetPtr to free |
|
2262 * |
|
2263 * Free the NodeSet compound (not the actual nodes !). |
|
2264 */ |
|
2265 XMLPUBFUNEXPORT void |
|
2266 xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { |
|
2267 if (!obj) |
|
2268 return; |
|
2269 if (obj->nodeTab) |
|
2270 { |
|
2271 int i; |
|
2272 |
|
2273 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2274 for (i = 0; i < obj->nodeNr; i++) |
|
2275 if (obj->nodeTab[i] && |
|
2276 obj->nodeTab[i]->type == XML_NAMESPACE_DECL) |
|
2277 { |
|
2278 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); |
|
2279 } |
|
2280 xmlFree(obj->nodeTab); |
|
2281 } |
|
2282 xmlFree(obj); |
|
2283 } |
|
2284 |
|
2285 /** |
|
2286 * xmlXPathFreeValueTree: |
|
2287 * @param obj the xmlNodeSetPtr to free |
|
2288 * |
|
2289 * Free the NodeSet compound and the actual tree, this is different |
|
2290 * from xmlXPathFreeNodeSet() |
|
2291 */ |
|
2292 static void |
|
2293 xmlXPathFreeValueTree(xmlNodeSetPtr obj) { |
|
2294 int i; |
|
2295 |
|
2296 if (!obj) |
|
2297 return; |
|
2298 |
|
2299 if (obj->nodeTab) { |
|
2300 for (i = 0;i < obj->nodeNr;i++) { |
|
2301 if (obj->nodeTab[i]) { |
|
2302 if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
2303 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); |
|
2304 } else { |
|
2305 xmlFreeNodeList(obj->nodeTab[i]); |
|
2306 } |
|
2307 } |
|
2308 } |
|
2309 xmlFree(obj->nodeTab); |
|
2310 } |
|
2311 xmlFree(obj); |
|
2312 } |
|
2313 |
|
2314 #if defined(DEBUG) || defined(DEBUG_STEP) |
|
2315 /** |
|
2316 * xmlGenericErrorContextNodeSet: |
|
2317 * @param output a FILE * for the output |
|
2318 * @param obj the xmlNodeSetPtr to display |
|
2319 * |
|
2320 * Quick display of a NodeSet |
|
2321 */ |
|
2322 void |
|
2323 xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) { |
|
2324 int i; |
|
2325 |
|
2326 if (output == NULL) output = xmlGenericErrorContext; |
|
2327 if (obj == NULL) { |
|
2328 fprintf(output, "NodeSet == NULL !\n"); |
|
2329 return; |
|
2330 } |
|
2331 if (obj->nodeNr == 0) { |
|
2332 fprintf(output, "NodeSet is empty\n"); |
|
2333 return; |
|
2334 } |
|
2335 if (obj->nodeTab == NULL) { |
|
2336 fprintf(output, " nodeTab == NULL !\n"); |
|
2337 return; |
|
2338 } |
|
2339 for (i = 0; i < obj->nodeNr; i++) { |
|
2340 if (obj->nodeTab[i] == NULL) { |
|
2341 fprintf(output, " NULL !\n"); |
|
2342 return; |
|
2343 } |
|
2344 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) || |
|
2345 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) |
|
2346 fprintf(output, " /"); |
|
2347 else if (obj->nodeTab[i]->name == NULL) |
|
2348 fprintf(output, " noname!"); |
|
2349 else fprintf(output, " %s", obj->nodeTab[i]->name); |
|
2350 } |
|
2351 fprintf(output, "\n"); |
|
2352 } |
|
2353 #endif |
|
2354 |
|
2355 /** |
|
2356 * xmlXPathNewNodeSet: |
|
2357 * @param val the NodePtr value |
|
2358 * |
|
2359 * Create a new xmlXPathObjectPtr of type NodeSet and initialize |
|
2360 * it with the single Node val |
|
2361 * |
|
2362 * Returns the newly created object. |
|
2363 */ |
|
2364 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
2365 xmlXPathNewNodeSet(xmlNodePtr val) { |
|
2366 xmlXPathObjectPtr ret; |
|
2367 LOAD_GS_DIRECT |
|
2368 |
|
2369 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
2370 if (!ret) { |
|
2371 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n")); |
|
2372 return(NULL); |
|
2373 } |
|
2374 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
2375 ret->type = XPATH_NODESET; |
|
2376 ret->boolval = 0; |
|
2377 ret->nodesetval = xmlXPathNodeSetCreate(val); |
|
2378 if(OOM_FLAG) |
|
2379 { |
|
2380 xmlXPathFreeObject(ret); |
|
2381 return(NULL); |
|
2382 } |
|
2383 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2384 return(ret); |
|
2385 } |
|
2386 |
|
2387 /** |
|
2388 * xmlXPathNewValueTree: |
|
2389 * @param val the NodePtr value |
|
2390 * |
|
2391 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize |
|
2392 * it with the tree root val |
|
2393 * |
|
2394 * Returns the newly created object. |
|
2395 */ |
|
2396 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
2397 xmlXPathNewValueTree(xmlNodePtr val) { |
|
2398 xmlXPathObjectPtr ret; |
|
2399 LOAD_GS_DIRECT |
|
2400 |
|
2401 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
2402 if (!ret) { |
|
2403 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating result value tree\n")); |
|
2404 return(NULL); |
|
2405 } |
|
2406 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
2407 ret->type = XPATH_XSLT_TREE; |
|
2408 ret->boolval = 1; |
|
2409 ret->user = (void *) val; |
|
2410 ret->nodesetval = xmlXPathNodeSetCreate(val); |
|
2411 if(OOM_FLAG) |
|
2412 { |
|
2413 xmlXPathFreeObject(ret); |
|
2414 return(NULL); |
|
2415 } |
|
2416 return(ret); |
|
2417 } |
|
2418 |
|
2419 /** |
|
2420 * xmlXPathNewNodeSetList: |
|
2421 * @param val an existing NodeSet |
|
2422 * |
|
2423 * Create a new xmlXPathObjectPtr of type NodeSet and initialize |
|
2424 * it with the Nodeset val |
|
2425 * |
|
2426 * Returns the newly created object. |
|
2427 */ |
|
2428 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
2429 xmlXPathNewNodeSetList(xmlNodeSetPtr val) |
|
2430 { |
|
2431 xmlXPathObjectPtr ret; |
|
2432 int i; |
|
2433 |
|
2434 if (!val) |
|
2435 ret = NULL; |
|
2436 else if (!val->nodeTab) |
|
2437 ret = xmlXPathNewNodeSet(NULL); |
|
2438 else { |
|
2439 ret = xmlXPathNewNodeSet(val->nodeTab[0]); |
|
2440 for (i = 1; i < val->nodeNr; ++i) |
|
2441 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]); |
|
2442 } |
|
2443 |
|
2444 return (ret); |
|
2445 } |
|
2446 |
|
2447 /** |
|
2448 * xmlXPathWrapNodeSet: |
|
2449 * @param val the NodePtr value |
|
2450 * |
|
2451 * Wrap the Nodeset val in a new xmlXPathObjectPtr |
|
2452 * |
|
2453 * Returns the newly created object. |
|
2454 */ |
|
2455 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
2456 xmlXPathWrapNodeSet(xmlNodeSetPtr val) { |
|
2457 xmlXPathObjectPtr ret; |
|
2458 |
|
2459 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
2460 if (!ret) { |
|
2461 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating node set object\n")); |
|
2462 return(NULL); |
|
2463 } |
|
2464 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
2465 ret->type = XPATH_NODESET; |
|
2466 ret->nodesetval = val; |
|
2467 return(ret); |
|
2468 } |
|
2469 |
|
2470 /** |
|
2471 * xmlXPathFreeNodeSetList: |
|
2472 * @param obj an existing NodeSetList object |
|
2473 * |
|
2474 * Free up the xmlXPathObjectPtr obj but don't deallocate the objects in |
|
2475 * the list contrary to xmlXPathFreeObject(). |
|
2476 */ |
|
2477 XMLPUBFUNEXPORT void |
|
2478 xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) { |
|
2479 if (obj) |
|
2480 xmlFree(obj); |
|
2481 } |
|
2482 |
|
2483 /** |
|
2484 * xmlXPathDifference: |
|
2485 * @param nodes1 a node-set |
|
2486 * @param nodes2 a node-set |
|
2487 * |
|
2488 * Implements the EXSLT - Sets difference() function: |
|
2489 * node-set set:difference (node-set, node-set) |
|
2490 * |
|
2491 * Returns the difference between the two node sets, or nodes1 if |
|
2492 * nodes2 is empty |
|
2493 */ |
|
2494 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2495 xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2496 xmlNodeSetPtr ret; |
|
2497 int i, l1; |
|
2498 xmlNodePtr cur; |
|
2499 |
|
2500 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2501 return(nodes1); |
|
2502 |
|
2503 ret = xmlXPathNodeSetCreate(NULL); |
|
2504 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2505 return(ret); |
|
2506 |
|
2507 l1 = xmlXPathNodeSetGetLength(nodes1); |
|
2508 |
|
2509 for (i = 0; i < l1; i++) { |
|
2510 cur = xmlXPathNodeSetItem(nodes1, i); |
|
2511 if (!xmlXPathNodeSetContains(nodes2, cur)) |
|
2512 xmlXPathNodeSetAddUnique(ret, cur); |
|
2513 } |
|
2514 return(ret); |
|
2515 } |
|
2516 |
|
2517 /** |
|
2518 * xmlXPathIntersection: |
|
2519 * @param nodes1 a node-set |
|
2520 * @param nodes2 a node-set |
|
2521 * |
|
2522 * Implements the EXSLT - Sets intersection() function: |
|
2523 * node-set set:intersection (node-set, node-set) |
|
2524 * |
|
2525 * Returns a node set comprising the nodes that are within both the |
|
2526 * node sets passed as arguments |
|
2527 */ |
|
2528 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2529 xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2530 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL); |
|
2531 int i, len; |
|
2532 xmlNodePtr cur; |
|
2533 |
|
2534 if (xmlXPathNodeSetIsEmpty(nodes1) || |
|
2535 xmlXPathNodeSetIsEmpty(nodes2)) |
|
2536 { |
|
2537 return(ret); |
|
2538 } |
|
2539 len = xmlXPathNodeSetGetLength(nodes1); |
|
2540 |
|
2541 // [more efficient search in xmlXPathNodeSetContains() with smaller nodeset ] |
|
2542 for (i = 0; i < len; i++) { |
|
2543 cur = xmlXPathNodeSetItem(nodes1, i); |
|
2544 if (xmlXPathNodeSetContains(nodes2, cur)) |
|
2545 xmlXPathNodeSetAddUnique(ret, cur); |
|
2546 } |
|
2547 return(ret); |
|
2548 } |
|
2549 |
|
2550 /** |
|
2551 * xmlXPathDistinctSorted: |
|
2552 * @param nodes a node-set, sorted by document order |
|
2553 * |
|
2554 * Implements the EXSLT - Sets distinct() function: |
|
2555 * node-set set:distinct (node-set) |
|
2556 * |
|
2557 * Returns a subset of the nodes contained in nodes, or nodes if |
|
2558 * it is empty |
|
2559 */ |
|
2560 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2561 xmlXPathDistinctSorted (xmlNodeSetPtr nodes) |
|
2562 { |
|
2563 xmlNodeSetPtr ret; |
|
2564 xmlHashTablePtr hash; |
|
2565 int i, l; |
|
2566 xmlChar * strval; |
|
2567 xmlNodePtr cur; |
|
2568 |
|
2569 if (xmlXPathNodeSetIsEmpty(nodes)) |
|
2570 return(nodes); |
|
2571 |
|
2572 ret = xmlXPathNodeSetCreate(NULL); |
|
2573 l = xmlXPathNodeSetGetLength(nodes); |
|
2574 hash = xmlHashCreate (l); |
|
2575 for (i = 0; i < l; i++) { |
|
2576 cur = xmlXPathNodeSetItem(nodes, i); |
|
2577 strval = xmlXPathCastNodeToString(cur); |
|
2578 if (xmlHashLookup(hash, strval) == NULL) { |
|
2579 xmlHashAddEntry(hash, strval, strval); |
|
2580 xmlXPathNodeSetAddUnique(ret, cur); |
|
2581 } else { |
|
2582 xmlFree(strval); |
|
2583 } |
|
2584 } |
|
2585 xmlHashFree(hash, (xmlHashDeallocator) xmlFree); |
|
2586 return(ret); |
|
2587 } |
|
2588 |
|
2589 /** |
|
2590 * xmlXPathDistinct: |
|
2591 * @param nodes a node-set |
|
2592 * |
|
2593 * Implements the EXSLT - Sets distinct() function: |
|
2594 * node-set set:distinct (node-set) |
|
2595 * nodes is sorted by document order, then #exslSetsDistinctSorted |
|
2596 * is called with the sorted node-set |
|
2597 * |
|
2598 * Returns a subset of the nodes contained in nodes, or nodes if |
|
2599 * it is empty |
|
2600 */ |
|
2601 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2602 xmlXPathDistinct (xmlNodeSetPtr nodes) { |
|
2603 if (xmlXPathNodeSetIsEmpty(nodes)) |
|
2604 return(nodes); |
|
2605 |
|
2606 xmlXPathNodeSetSort(nodes); |
|
2607 return(xmlXPathDistinctSorted(nodes)); |
|
2608 } |
|
2609 |
|
2610 /** |
|
2611 * xmlXPathHasSameNodes: |
|
2612 * @param nodes1 a node-set |
|
2613 * @param nodes2 a node-set |
|
2614 * |
|
2615 * Implements the EXSLT - Sets has-same-nodes function: |
|
2616 * boolean set:has-same-node(node-set, node-set) |
|
2617 * |
|
2618 * Returns true (1) if nodes1 shares any node with nodes2, false (0) |
|
2619 * otherwise |
|
2620 */ |
|
2621 XMLPUBFUNEXPORT int |
|
2622 xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2623 int i, l; |
|
2624 xmlNodePtr cur; |
|
2625 |
|
2626 if (xmlXPathNodeSetIsEmpty(nodes1) || |
|
2627 xmlXPathNodeSetIsEmpty(nodes2)) |
|
2628 return(0); |
|
2629 |
|
2630 l = xmlXPathNodeSetGetLength(nodes1); |
|
2631 for (i = 0; i < l; i++) { |
|
2632 cur = xmlXPathNodeSetItem(nodes1, i); |
|
2633 if (xmlXPathNodeSetContains(nodes2, cur)) |
|
2634 return(1); |
|
2635 } |
|
2636 return(0); |
|
2637 } |
|
2638 |
|
2639 /** |
|
2640 * xmlXPathNodeLeadingSorted: |
|
2641 * @param nodes a node-set, sorted by document order |
|
2642 * @param node a node |
|
2643 * |
|
2644 * Implements the EXSLT - Sets leading() function: |
|
2645 * node-set set:leading (node-set, node-set) |
|
2646 * |
|
2647 * Returns the nodes in nodes that precede node in document order, |
|
2648 * nodes if node is NULL or an empty node-set if nodes |
|
2649 * doesn't contain node |
|
2650 */ |
|
2651 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2652 xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2653 int i, l; |
|
2654 xmlNodePtr cur; |
|
2655 xmlNodeSetPtr ret; |
|
2656 |
|
2657 if (node == NULL) |
|
2658 return(nodes); |
|
2659 |
|
2660 ret = xmlXPathNodeSetCreate(NULL); |
|
2661 if (xmlXPathNodeSetIsEmpty(nodes) || |
|
2662 (!xmlXPathNodeSetContains(nodes, node))) |
|
2663 return(ret); |
|
2664 |
|
2665 l = xmlXPathNodeSetGetLength(nodes); |
|
2666 for (i = 0; i < l; i++) { |
|
2667 cur = xmlXPathNodeSetItem(nodes, i); |
|
2668 if (cur == node) |
|
2669 break; |
|
2670 xmlXPathNodeSetAddUnique(ret, cur); |
|
2671 } |
|
2672 return(ret); |
|
2673 } |
|
2674 |
|
2675 /** |
|
2676 * xmlXPathNodeLeading: |
|
2677 * @param nodes a node-set |
|
2678 * @param node a node |
|
2679 * |
|
2680 * Implements the EXSLT - Sets leading() function: |
|
2681 * node-set set:leading (node-set, node-set) |
|
2682 * nodes is sorted by document order, then #exslSetsNodeLeadingSorted |
|
2683 * is called. |
|
2684 * |
|
2685 * Returns the nodes in nodes that precede node in document order, |
|
2686 * nodes if node is NULL or an empty node-set if nodes |
|
2687 * doesn't contain node |
|
2688 */ |
|
2689 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2690 xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2691 xmlXPathNodeSetSort(nodes); |
|
2692 return(xmlXPathNodeLeadingSorted(nodes, node)); |
|
2693 } |
|
2694 |
|
2695 /** |
|
2696 * xmlXPathLeadingSorted: |
|
2697 * @param nodes1 a node-set, sorted by document order |
|
2698 * @param nodes2 a node-set, sorted by document order |
|
2699 * |
|
2700 * Implements the EXSLT - Sets leading() function: |
|
2701 * node-set set:leading (node-set, node-set) |
|
2702 * |
|
2703 * Returns the nodes in nodes1 that precede the first node in nodes2 |
|
2704 * in document order, nodes1 if nodes2 is NULL or empty or |
|
2705 * an empty node-set if nodes1 doesn't contain nodes2 |
|
2706 */ |
|
2707 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2708 xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2709 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2710 return(nodes1); |
|
2711 return(xmlXPathNodeLeadingSorted(nodes1, |
|
2712 xmlXPathNodeSetItem(nodes2, 1))); |
|
2713 } |
|
2714 |
|
2715 /** |
|
2716 * xmlXPathLeading: |
|
2717 * @param nodes1 a node-set |
|
2718 * @param nodes2 a node-set |
|
2719 * |
|
2720 * Implements the EXSLT - Sets leading() function: |
|
2721 * node-set set:leading (node-set, node-set) |
|
2722 * nodes1 and nodes2 are sorted by document order, then |
|
2723 * #exslSetsLeadingSorted is called. |
|
2724 * |
|
2725 * Returns the nodes in nodes1 that precede the first node in nodes2 |
|
2726 * in document order, nodes1 if nodes2 is NULL or empty or |
|
2727 * an empty node-set if nodes1 doesn't contain nodes2 |
|
2728 */ |
|
2729 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2730 xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2731 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2732 return(nodes1); |
|
2733 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2734 return(xmlXPathNodeSetCreate(NULL)); |
|
2735 xmlXPathNodeSetSort(nodes1); |
|
2736 xmlXPathNodeSetSort(nodes2); |
|
2737 return(xmlXPathNodeLeadingSorted(nodes1, |
|
2738 xmlXPathNodeSetItem(nodes2, 1))); |
|
2739 } |
|
2740 |
|
2741 /** |
|
2742 * xmlXPathNodeTrailingSorted: |
|
2743 * @param nodes a node-set, sorted by document order |
|
2744 * @param node a node |
|
2745 * |
|
2746 * Implements the EXSLT - Sets trailing() function: |
|
2747 * node-set set:trailing (node-set, node-set) |
|
2748 * |
|
2749 * Returns the nodes in nodes that follow node in document order, |
|
2750 * nodes if node is NULL or an empty node-set if nodes |
|
2751 * doesn't contain node |
|
2752 */ |
|
2753 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2754 xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2755 int i, l; |
|
2756 xmlNodePtr cur; |
|
2757 xmlNodeSetPtr ret; |
|
2758 |
|
2759 if (node == NULL) |
|
2760 return(nodes); |
|
2761 |
|
2762 ret = xmlXPathNodeSetCreate(NULL); |
|
2763 if (xmlXPathNodeSetIsEmpty(nodes) || |
|
2764 (!xmlXPathNodeSetContains(nodes, node))) |
|
2765 return(ret); |
|
2766 |
|
2767 l = xmlXPathNodeSetGetLength(nodes); |
|
2768 for (i = l; i > 0; i--) { |
|
2769 cur = xmlXPathNodeSetItem(nodes, i); |
|
2770 if (cur == node) |
|
2771 break; |
|
2772 xmlXPathNodeSetAddUnique(ret, cur); |
|
2773 } |
|
2774 return(ret); |
|
2775 } |
|
2776 |
|
2777 /** |
|
2778 * xmlXPathNodeTrailing: |
|
2779 * @param nodes a node-set |
|
2780 * @param node a node |
|
2781 * |
|
2782 * Implements the EXSLT - Sets trailing() function: |
|
2783 * node-set set:trailing (node-set, node-set) |
|
2784 * nodes is sorted by document order, then #xmlXPathNodeTrailingSorted |
|
2785 * is called. |
|
2786 * |
|
2787 * Returns the nodes in nodes that follow node in document order, |
|
2788 * nodes if node is NULL or an empty node-set if nodes |
|
2789 * doesn't contain node |
|
2790 */ |
|
2791 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2792 xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2793 xmlXPathNodeSetSort(nodes); |
|
2794 return(xmlXPathNodeTrailingSorted(nodes, node)); |
|
2795 } |
|
2796 |
|
2797 /** |
|
2798 * xmlXPathTrailingSorted: |
|
2799 * @param nodes1 a node-set, sorted by document order |
|
2800 * @param nodes2 a node-set, sorted by document order |
|
2801 * |
|
2802 * Implements the EXSLT - Sets trailing() function: |
|
2803 * node-set set:trailing (node-set, node-set) |
|
2804 * |
|
2805 * Returns the nodes in nodes1 that follow the first node in nodes2 |
|
2806 * in document order, nodes1 if nodes2 is NULL or empty or |
|
2807 * an empty node-set if nodes1 doesn't contain nodes2 |
|
2808 */ |
|
2809 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2810 xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2811 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2812 return(nodes1); |
|
2813 return(xmlXPathNodeTrailingSorted(nodes1, |
|
2814 xmlXPathNodeSetItem(nodes2, 0))); |
|
2815 } |
|
2816 |
|
2817 /** |
|
2818 * xmlXPathTrailing: |
|
2819 * @param nodes1 a node-set |
|
2820 * @param nodes2 a node-set |
|
2821 * |
|
2822 * Implements the EXSLT - Sets trailing() function: |
|
2823 * node-set set:trailing (node-set, node-set) |
|
2824 * nodes1 and nodes2 are sorted by document order, then |
|
2825 * #xmlXPathTrailingSorted is called. |
|
2826 * |
|
2827 * Returns the nodes in nodes1 that follow the first node in nodes2 |
|
2828 * in document order, nodes1 if nodes2 is NULL or empty or |
|
2829 * an empty node-set if nodes1 doesn't contain nodes2 |
|
2830 */ |
|
2831 XMLPUBFUNEXPORT xmlNodeSetPtr |
|
2832 xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) |
|
2833 { |
|
2834 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2835 return(nodes1); |
|
2836 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2837 return(xmlXPathNodeSetCreate(NULL)); |
|
2838 xmlXPathNodeSetSort(nodes1); |
|
2839 xmlXPathNodeSetSort(nodes2); |
|
2840 return xmlXPathNodeTrailingSorted(nodes1, xmlXPathNodeSetItem(nodes2, 0)); |
|
2841 } |
|
2842 |
|
2843 /************************************************************************ |
|
2844 * * |
|
2845 * Routines to handle extra functions * |
|
2846 * * |
|
2847 ************************************************************************/ |
|
2848 |
|
2849 /** |
|
2850 * xmlXPathRegisterFunc: |
|
2851 * @param ctxt the XPath context |
|
2852 * @param name the function name |
|
2853 * @param f the function implementation or NULL |
|
2854 * |
|
2855 * Register a new function. If f is NULL it unregisters the function |
|
2856 * |
|
2857 * Returns 0 in case of success, -1 in case of error |
|
2858 * |
|
2859 * Prerequisites: TRUE( ctxt && ctxt->funcHash ) |
|
2860 */ |
|
2861 XMLPUBFUNEXPORT int |
|
2862 xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name, xmlXPathFunction f) |
|
2863 { |
|
2864 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f)); |
|
2865 } |
|
2866 |
|
2867 /** |
|
2868 * xmlXPathRegisterFuncNS: |
|
2869 * @param ctxt the XPath context |
|
2870 * @param name the function name |
|
2871 * @param ns_uri the function namespace URI |
|
2872 * @param f the function implementation or NULL |
|
2873 * |
|
2874 * Register a new function. If f is NULL it unregisters the function |
|
2875 * |
|
2876 * Returns 0 in case of success, -1 in case of error |
|
2877 * |
|
2878 * Prerequisites: TRUE( ctxt && ctxt->funcHash ) |
|
2879 */ |
|
2880 XMLPUBFUNEXPORT int |
|
2881 xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
2882 const xmlChar *ns_uri, xmlXPathFunction f) |
|
2883 { |
|
2884 LOAD_GS_DIRECT |
|
2885 if (OOM_FLAG || !name) |
|
2886 return -1; |
|
2887 |
|
2888 //if(!ctxt || !ctxt->funcHash) |
|
2889 // return(-1); |
|
2890 |
|
2891 // Disabled: ctxt->funcHash is always initialized prior registering functions |
|
2892 // if (ctxt->funcHash == NULL) |
|
2893 // ctxt->funcHash = xmlHashCreate(0); |
|
2894 |
|
2895 return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *)f)); |
|
2896 } |
|
2897 |
|
2898 /** |
|
2899 * xmlXPathRegisterFuncLookup: |
|
2900 * @param ctxt the XPath context |
|
2901 * @param f the lookup function |
|
2902 * @param funcCtxt the lookup data |
|
2903 * |
|
2904 * Registers an external mechanism to do function lookup. |
|
2905 */ |
|
2906 XMLPUBFUNEXPORT void |
|
2907 xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt, xmlXPathFuncLookupFunc f, void *funcCtxt) |
|
2908 { |
|
2909 if (!ctxt) |
|
2910 return; |
|
2911 ctxt->funcLookupFunc = (void *) f; |
|
2912 ctxt->funcLookupData = funcCtxt; |
|
2913 } |
|
2914 |
|
2915 /** |
|
2916 * xmlXPathFunctionLookup: |
|
2917 * @param ctxt the XPath context |
|
2918 * @param name the function name |
|
2919 * |
|
2920 * Search in the Function array of the context for the given |
|
2921 * function. |
|
2922 * |
|
2923 * Returns the xmlXPathFunction or NULL if not found |
|
2924 */ |
|
2925 XMLPUBFUNEXPORT xmlXPathFunction |
|
2926 xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) |
|
2927 { |
|
2928 if (!ctxt) |
|
2929 return (NULL); |
|
2930 |
|
2931 if (ctxt->funcLookupFunc) { |
|
2932 xmlXPathFunction ret; |
|
2933 xmlXPathFuncLookupFunc f; |
|
2934 |
|
2935 f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc; |
|
2936 ret = f(ctxt->funcLookupData, name, NULL); |
|
2937 if (ret) |
|
2938 return(ret); |
|
2939 } |
|
2940 return(xmlXPathFunctionLookupNS(ctxt, name, NULL)); |
|
2941 } |
|
2942 |
|
2943 /** |
|
2944 * xmlXPathFunctionLookupNS: |
|
2945 * @param ctxt the XPath context |
|
2946 * @param name the function name |
|
2947 * @param ns_uri the function namespace URI |
|
2948 * |
|
2949 * Search in the Function array of the context for the given |
|
2950 * function. |
|
2951 * |
|
2952 * Returns the xmlXPathFunction or NULL if not found |
|
2953 */ |
|
2954 XMLPUBFUNEXPORT xmlXPathFunction |
|
2955 xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri) |
|
2956 { |
|
2957 if (!ctxt || !name) |
|
2958 return(NULL); |
|
2959 |
|
2960 if (ctxt->funcLookupFunc) { |
|
2961 xmlXPathFunction ret; |
|
2962 xmlXPathFuncLookupFunc f; |
|
2963 |
|
2964 f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc; |
|
2965 ret = f(ctxt->funcLookupData, name, ns_uri); |
|
2966 if (ret != NULL) |
|
2967 return(ret); |
|
2968 } |
|
2969 |
|
2970 if (!ctxt->funcHash) |
|
2971 return(NULL); |
|
2972 |
|
2973 return (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
|
2974 } |
|
2975 |
|
2976 /** |
|
2977 * xmlXPathRegisteredFuncsCleanup: |
|
2978 * @param ctxt the XPath context |
|
2979 * |
|
2980 * Cleanup the XPath context data associated to registered functions |
|
2981 */ |
|
2982 XMLPUBFUNEXPORT void |
|
2983 xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) { |
|
2984 if (!ctxt) |
|
2985 return; |
|
2986 |
|
2987 xmlHashFree(ctxt->funcHash, NULL); |
|
2988 ctxt->funcHash = NULL; |
|
2989 } |
|
2990 |
|
2991 /************************************************************************ |
|
2992 * * |
|
2993 * Routines to handle Variables * |
|
2994 * * |
|
2995 ************************************************************************/ |
|
2996 |
|
2997 /** |
|
2998 * xmlXPathRegisterVariable: |
|
2999 * @param ctxt the XPath context |
|
3000 * @param name the variable name |
|
3001 * @param value the variable value or NULL |
|
3002 * |
|
3003 * Register a new variable value. If value is NULL it unregisters |
|
3004 * the variable |
|
3005 * |
|
3006 * Returns 0 in case of success, -1 in case of error |
|
3007 */ |
|
3008 XMLPUBFUNEXPORT int |
|
3009 xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
3010 xmlXPathObjectPtr value) { |
|
3011 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value)); |
|
3012 } |
|
3013 |
|
3014 /** |
|
3015 * xmlXPathRegisterVariableNS: |
|
3016 * @param ctxt the XPath context |
|
3017 * @param name the variable name |
|
3018 * @param ns_uri the variable namespace URI |
|
3019 * @param value the variable value or NULL |
|
3020 * |
|
3021 * Register a new variable value. If value is NULL it unregisters |
|
3022 * the variable |
|
3023 * |
|
3024 * Returns 0 in case of success, -1 in case of error |
|
3025 */ |
|
3026 XMLPUBFUNEXPORT int |
|
3027 xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
3028 const xmlChar *ns_uri, |
|
3029 xmlXPathObjectPtr value) { |
|
3030 if (ctxt == NULL) |
|
3031 return(-1); |
|
3032 if (name == NULL) |
|
3033 return(-1); |
|
3034 |
|
3035 if (ctxt->varHash == NULL) |
|
3036 ctxt->varHash = xmlHashCreate(0); |
|
3037 if (ctxt->varHash == NULL) |
|
3038 return(-1); |
|
3039 if (value == NULL) |
|
3040 return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri, |
|
3041 (xmlHashDeallocator)xmlXPathFreeObject)); |
|
3042 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri, |
|
3043 (void *) value, |
|
3044 (xmlHashDeallocator)xmlXPathFreeObject)); |
|
3045 } |
|
3046 |
|
3047 /** |
|
3048 * xmlXPathRegisterVariableLookup: |
|
3049 * @param ctxt the XPath context |
|
3050 * @param f the lookup function |
|
3051 * @param data the lookup data |
|
3052 * |
|
3053 * register an external mechanism to do variable lookup |
|
3054 */ |
|
3055 XMLPUBFUNEXPORT void |
|
3056 xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt, |
|
3057 xmlXPathVariableLookupFunc f, void *data) { |
|
3058 if (!ctxt) |
|
3059 return; |
|
3060 ctxt->varLookupFunc = (void *) f; |
|
3061 ctxt->varLookupData = data; |
|
3062 } |
|
3063 |
|
3064 /** |
|
3065 * xmlXPathVariableLookup: |
|
3066 * @param ctxt the XPath context |
|
3067 * @param name the variable name |
|
3068 * |
|
3069 * Search in the Variable array of the context for the given |
|
3070 * variable value. |
|
3071 * |
|
3072 * Returns a copy of the value or NULL if not found |
|
3073 */ |
|
3074 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3075 xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { |
|
3076 if (ctxt == NULL) |
|
3077 return(NULL); |
|
3078 |
|
3079 if (ctxt->varLookupFunc) { |
|
3080 xmlXPathVariableLookupFunc func = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc; |
|
3081 xmlXPathObjectPtr ret = func(ctxt->varLookupData, name, NULL); |
|
3082 return(ret); |
|
3083 } |
|
3084 return(xmlXPathVariableLookupNS(ctxt, name, NULL)); |
|
3085 } |
|
3086 |
|
3087 /** |
|
3088 * xmlXPathVariableLookupNS: |
|
3089 * @param ctxt the XPath context |
|
3090 * @param name the variable name |
|
3091 * @param ns_uri the variable namespace URI |
|
3092 * |
|
3093 * Search in the Variable array of the context for the given |
|
3094 * variable value. |
|
3095 * |
|
3096 * Returns the a copy of the value or NULL if not found |
|
3097 */ |
|
3098 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3099 xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
3100 const xmlChar *ns_uri) { |
|
3101 if (ctxt == NULL) |
|
3102 return(NULL); |
|
3103 |
|
3104 if (ctxt->varLookupFunc != NULL) { |
|
3105 xmlXPathVariableLookupFunc lookupFunc = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc; |
|
3106 xmlXPathObjectPtr ret = lookupFunc(ctxt->varLookupData, name, ns_uri); |
|
3107 if (ret != NULL) |
|
3108 return(ret); |
|
3109 } |
|
3110 |
|
3111 if (ctxt->varHash == NULL) |
|
3112 return(NULL); |
|
3113 if (name == NULL) |
|
3114 return(NULL); |
|
3115 |
|
3116 return(xmlXPathObjectCopy((xmlXPathObjectPtr) |
|
3117 xmlHashLookup2(ctxt->varHash, name, ns_uri))); |
|
3118 } |
|
3119 |
|
3120 /** |
|
3121 * xmlXPathRegisteredVariablesCleanup: |
|
3122 * @param ctxt the XPath context |
|
3123 * |
|
3124 * Cleanup the XPath context data associated to registered variables |
|
3125 */ |
|
3126 XMLPUBFUNEXPORT void |
|
3127 xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) |
|
3128 { |
|
3129 if (ctxt == NULL) |
|
3130 return; |
|
3131 |
|
3132 xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject); |
|
3133 ctxt->varHash = NULL; |
|
3134 } |
|
3135 |
|
3136 /** |
|
3137 * xmlXPathRegisterNs: |
|
3138 * @param ctxt the XPath context |
|
3139 * @param prefix the namespace prefix |
|
3140 * @param ns_uri the namespace name |
|
3141 * |
|
3142 * Register a new namespace. If ns_uri is NULL it unregisters |
|
3143 * the namespace |
|
3144 * |
|
3145 * Returns 0 in case of success, -1 in case of error |
|
3146 * |
|
3147 * OOM: possible --> returns -1 |
|
3148 */ |
|
3149 XMLPUBFUNEXPORT int |
|
3150 xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar* prefix, |
|
3151 const xmlChar* ns_uri) |
|
3152 { |
|
3153 int result = -1; |
|
3154 xmlChar* uri; |
|
3155 if (!ctxt || !prefix) |
|
3156 return(-1); |
|
3157 |
|
3158 if (ctxt->nsHash == NULL) |
|
3159 ctxt->nsHash = xmlHashCreate(10); |
|
3160 if (ctxt->nsHash == NULL) |
|
3161 return(-1); // OOM |
|
3162 if (!ns_uri) |
|
3163 return(xmlHashRemoveEntry(ctxt->nsHash, prefix,(xmlHashDeallocator)xmlFree)); |
|
3164 uri = xmlStrdup(ns_uri); |
|
3165 if (uri) |
|
3166 { |
|
3167 result = xmlHashUpdateEntry(ctxt->nsHash, prefix, (void*)uri, |
|
3168 (xmlHashDeallocator)xmlFree); |
|
3169 if ( result == -1) |
|
3170 xmlFree( uri ); |
|
3171 } |
|
3172 return( result ); |
|
3173 } |
|
3174 |
|
3175 /** |
|
3176 * xmlXPathNsLookup: |
|
3177 * @param ctxt the XPath context |
|
3178 * @param prefix the namespace prefix value |
|
3179 * |
|
3180 * Search in the namespace declaration array of the context for the given |
|
3181 * namespace name associated to the given prefix |
|
3182 * |
|
3183 * Returns the value or NULL if not found |
|
3184 */ |
|
3185 XMLPUBFUNEXPORT const xmlChar* |
|
3186 xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) |
|
3187 { |
|
3188 if (!ctxt || !prefix) |
|
3189 return(NULL); |
|
3190 |
|
3191 #ifdef XML_XML_NAMESPACE |
|
3192 if (xmlStrEqual(prefix, (const xmlChar*)"xml")) |
|
3193 return(XML_XML_NAMESPACE); |
|
3194 #endif |
|
3195 |
|
3196 if (ctxt->namespaces) { |
|
3197 int i; |
|
3198 |
|
3199 for (i = 0;i < ctxt->nsNr;i++) { |
|
3200 if (ctxt->namespaces[i] && |
|
3201 xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)) |
|
3202 { |
|
3203 return(ctxt->namespaces[i]->href); |
|
3204 } |
|
3205 } |
|
3206 } |
|
3207 |
|
3208 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix)); |
|
3209 } |
|
3210 |
|
3211 /** |
|
3212 * xmlXPathRegisteredNsCleanup: |
|
3213 * @param ctxt the XPath context |
|
3214 * |
|
3215 * Cleanup the XPath context data associated to registered variables |
|
3216 */ |
|
3217 XMLPUBFUNEXPORT void |
|
3218 xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) { |
|
3219 if (!ctxt) |
|
3220 return; |
|
3221 |
|
3222 xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree); |
|
3223 ctxt->nsHash = NULL; |
|
3224 } |
|
3225 |
|
3226 /************************************************************************ |
|
3227 * * |
|
3228 * Routines to handle Values * |
|
3229 * * |
|
3230 ************************************************************************/ |
|
3231 |
|
3232 /* Allocations are terrible, one needs to optimize all this !!! */ |
|
3233 |
|
3234 /** |
|
3235 * xmlXPathNewFloat: |
|
3236 * @param val the double value |
|
3237 * |
|
3238 * Create a new xmlXPathObjectPtr of type double and of value val |
|
3239 * |
|
3240 * Returns the newly created object. |
|
3241 */ |
|
3242 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3243 xmlXPathNewFloat(double val) { |
|
3244 xmlXPathObjectPtr ret; |
|
3245 |
|
3246 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3247 if (ret == NULL) { |
|
3248 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating float object\n")); |
|
3249 return(NULL); |
|
3250 } |
|
3251 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3252 ret->type = XPATH_NUMBER; |
|
3253 ret->floatval = val; |
|
3254 return(ret); |
|
3255 } |
|
3256 |
|
3257 /** |
|
3258 * xmlXPathNewBoolean: |
|
3259 * @param val the boolean value |
|
3260 * |
|
3261 * Create a new xmlXPathObjectPtr of type boolean and of value val |
|
3262 * |
|
3263 * Returns the newly created object. |
|
3264 */ |
|
3265 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3266 xmlXPathNewBoolean(int val) { |
|
3267 xmlXPathObjectPtr ret; |
|
3268 |
|
3269 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3270 if (ret == NULL) { |
|
3271 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating boolean object\n")); |
|
3272 return(NULL); |
|
3273 } |
|
3274 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3275 ret->type = XPATH_BOOLEAN; |
|
3276 ret->boolval = (val != 0); |
|
3277 return(ret); |
|
3278 } |
|
3279 |
|
3280 /** |
|
3281 * xmlXPathNewString: |
|
3282 * @param val the xmlChar * value |
|
3283 * |
|
3284 * Create a new xmlXPathObjectPtr of type string and of value val |
|
3285 * |
|
3286 * Returns the newly created object or NULL if OOM |
|
3287 * |
|
3288 * OOM: possible --> sets OOM flag |
|
3289 */ |
|
3290 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3291 xmlXPathNewString(const xmlChar *val) { |
|
3292 LOAD_GS_DIRECT |
|
3293 xmlXPathObjectPtr ret; |
|
3294 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3295 if (ret == NULL) { |
|
3296 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n")); |
|
3297 return(NULL); |
|
3298 } |
|
3299 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3300 ret->type = XPATH_STRING; |
|
3301 ret->stringval = xmlStrdup(val ? val : (const xmlChar *)""); |
|
3302 if( OOM_FLAG ) |
|
3303 { |
|
3304 xmlXPathFreeObject(ret); |
|
3305 return(NULL); |
|
3306 } |
|
3307 return(ret); |
|
3308 } |
|
3309 |
|
3310 /** |
|
3311 * xmlXPathWrapString: |
|
3312 * @param val the xmlChar * value |
|
3313 * |
|
3314 * Wraps the val string into an XPath object. |
|
3315 * |
|
3316 * Returns the newly created object. |
|
3317 */ |
|
3318 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3319 xmlXPathWrapString (xmlChar *val) { |
|
3320 xmlXPathObjectPtr ret; |
|
3321 |
|
3322 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3323 if (ret == NULL) { |
|
3324 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n")); |
|
3325 return(NULL); |
|
3326 } |
|
3327 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3328 ret->type = XPATH_STRING; |
|
3329 ret->stringval = val; |
|
3330 return(ret); |
|
3331 } |
|
3332 |
|
3333 /** |
|
3334 * xmlXPathNewCString: |
|
3335 * @param val the char * value |
|
3336 * |
|
3337 * Create a new xmlXPathObjectPtr of type string and of value val |
|
3338 * |
|
3339 * Returns the newly created object. |
|
3340 * |
|
3341 * OOM: possible --> NULL is returned |
|
3342 */ |
|
3343 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3344 xmlXPathNewCString(const char *val) |
|
3345 { // Note: this function is used mostly for creating empty string objects! |
|
3346 |
|
3347 xmlXPathObjectPtr ret; |
|
3348 |
|
3349 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3350 if (!ret) |
|
3351 goto OOM_exit; |
|
3352 |
|
3353 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3354 ret->type = XPATH_STRING; |
|
3355 if(val){ |
|
3356 ret->stringval = xmlStrdup(BAD_CAST val); |
|
3357 if(!ret->stringval){ |
|
3358 xmlXPathFreeObject(ret); |
|
3359 OOM_exit: |
|
3360 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n")); |
|
3361 return(NULL); |
|
3362 } |
|
3363 } |
|
3364 return(ret); |
|
3365 } |
|
3366 |
|
3367 /** |
|
3368 * xmlXPathWrapCString: |
|
3369 * @param val the char * value |
|
3370 * |
|
3371 * Wraps a string into an XPath object. |
|
3372 * |
|
3373 * Returns the newly created object. |
|
3374 */ |
|
3375 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3376 xmlXPathWrapCString (char* val) |
|
3377 { |
|
3378 return(xmlXPathWrapString((xmlChar*)(val))); |
|
3379 } |
|
3380 |
|
3381 /** |
|
3382 * xmlXPathWrapExternal: |
|
3383 * @param val the user data |
|
3384 * |
|
3385 * Wraps the val data into an XPath object. |
|
3386 * |
|
3387 * Returns the newly created object. |
|
3388 */ |
|
3389 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3390 xmlXPathWrapExternal (void *val) { |
|
3391 xmlXPathObjectPtr ret; |
|
3392 |
|
3393 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3394 if (!ret) { |
|
3395 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating user object\n")); |
|
3396 return(NULL); |
|
3397 } |
|
3398 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3399 ret->type = XPATH_USERS; |
|
3400 ret->user = val; |
|
3401 return(ret); |
|
3402 } |
|
3403 |
|
3404 /** |
|
3405 * xmlXPathObjectCopy: |
|
3406 * @param val the original object |
|
3407 * |
|
3408 * allocate a new copy of a given object |
|
3409 * |
|
3410 * Returns the newly created object or NULL if OOM |
|
3411 */ |
|
3412 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3413 xmlXPathObjectCopy(xmlXPathObjectPtr val) { |
|
3414 LOAD_GS_DIRECT |
|
3415 xmlXPathObjectPtr ret; |
|
3416 |
|
3417 if (!val) |
|
3418 return(NULL); |
|
3419 |
|
3420 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3421 if (!ret) { |
|
3422 xmlXPathErrMemory(NULL, EMBED_ERRTXT("copying object\n")); |
|
3423 return(NULL); |
|
3424 } |
|
3425 memcpy(ret, val , (size_t) sizeof(xmlXPathObject)); |
|
3426 switch (val->type) { |
|
3427 case XPATH_STRING: |
|
3428 |
|
3429 ret->stringval = xmlStrdup(val->stringval); |
|
3430 if( OOM_FLAG ) |
|
3431 { |
|
3432 xmlXPathFreeObject(ret); |
|
3433 return(NULL); |
|
3434 } |
|
3435 break; |
|
3436 case XPATH_XSLT_TREE: |
|
3437 if (val->nodesetval && val->nodesetval->nodeTab) |
|
3438 { |
|
3439 xmlNodePtr cur, tmp; |
|
3440 xmlDocPtr top; |
|
3441 |
|
3442 ret->boolval = 1; /* Deallocate the copied tree value */ |
|
3443 top = xmlNewDoc(NULL); |
|
3444 top->name = (char *) |
|
3445 xmlStrdup(val->nodesetval->nodeTab[0]->name); |
|
3446 ret->user = top; |
|
3447 if (top) { |
|
3448 top->doc = top; |
|
3449 cur = val->nodesetval->nodeTab[0]->children; |
|
3450 while (cur) { |
|
3451 tmp = xmlDocCopyNode(cur, top, 1); |
|
3452 xmlAddChild((xmlNodePtr) top, tmp); |
|
3453 cur = cur->next; |
|
3454 } |
|
3455 } |
|
3456 ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top); |
|
3457 } else |
|
3458 ret->nodesetval = xmlXPathNodeSetCreate(NULL); |
|
3459 /* Deallocate the copied tree value */ |
|
3460 break; |
|
3461 case XPATH_NODESET: |
|
3462 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval); |
|
3463 /* Do not deallocate the copied tree value */ |
|
3464 ret->boolval = 0; |
|
3465 break; |
|
3466 |
|
3467 // DONE: OPTIMIZED: moved as less probable branches |
|
3468 case XPATH_BOOLEAN: |
|
3469 case XPATH_NUMBER: |
|
3470 break; |
|
3471 |
|
3472 case XPATH_LOCATIONSET: |
|
3473 #ifdef LIBXML_XPTR_ENABLED |
|
3474 { |
|
3475 xmlLocationSetPtr loc = val->user; |
|
3476 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc); |
|
3477 break; |
|
3478 } |
|
3479 #endif |
|
3480 case XPATH_USERS: |
|
3481 ret->user = val->user; |
|
3482 break; |
|
3483 case XPATH_UNDEFINED: |
|
3484 xmlGenericError(xmlGenericErrorContext, |
|
3485 EMBED_ERRTXT("xmlXPathObjectCopy: unsupported type %d\n"), |
|
3486 val->type); |
|
3487 break; |
|
3488 /* DONE: Optimized: defaulted (rarely used!) |
|
3489 case XPATH_POINT: |
|
3490 case XPATH_RANGE: |
|
3491 */ |
|
3492 default: |
|
3493 break; |
|
3494 } |
|
3495 return(ret); |
|
3496 } |
|
3497 |
|
3498 /** |
|
3499 * xmlXPathFreeObject: |
|
3500 * @param obj the object to free |
|
3501 * |
|
3502 * Free up an xmlXPathObjectPtr object. |
|
3503 */ |
|
3504 XMLPUBFUNEXPORT void |
|
3505 xmlXPathFreeObject(xmlXPathObjectPtr obj) |
|
3506 { |
|
3507 if (!obj) |
|
3508 return; |
|
3509 if (obj->type == XPATH_NODESET || |
|
3510 obj->type == XPATH_XSLT_TREE) |
|
3511 { |
|
3512 if (obj->boolval) { |
|
3513 if (obj->user) { |
|
3514 xmlXPathFreeNodeSet(obj->nodesetval); |
|
3515 xmlFreeNodeList((xmlNodePtr) obj->user); |
|
3516 } else |
|
3517 if (obj->nodesetval) |
|
3518 xmlXPathFreeValueTree(obj->nodesetval); |
|
3519 } else { |
|
3520 if (obj->nodesetval) |
|
3521 xmlXPathFreeNodeSet(obj->nodesetval); |
|
3522 } |
|
3523 #ifdef LIBXML_XPTR_ENABLED |
|
3524 } |
|
3525 else if (obj->type == XPATH_LOCATIONSET) |
|
3526 { |
|
3527 if (obj->user) |
|
3528 xmlXPtrFreeLocationSet(obj->user); |
|
3529 #endif |
|
3530 } |
|
3531 else if (obj->type == XPATH_STRING) |
|
3532 { |
|
3533 if (obj->stringval != NULL) |
|
3534 xmlFree(obj->stringval); |
|
3535 } |
|
3536 |
|
3537 xmlFree(obj); |
|
3538 } |
|
3539 |
|
3540 |
|
3541 /************************************************************************ |
|
3542 * * |
|
3543 * Type Casting Routines * |
|
3544 * * |
|
3545 ************************************************************************/ |
|
3546 |
|
3547 /** |
|
3548 * xmlXPathCastBooleanToString: |
|
3549 * @param val a boolean |
|
3550 * |
|
3551 * Converts a boolean to its string value. |
|
3552 * |
|
3553 * Returns a newly allocated string. |
|
3554 * OOM: possible --> returns NULL |
|
3555 */ |
|
3556 XMLPUBFUNEXPORT xmlChar * |
|
3557 xmlXPathCastBooleanToString (int val) |
|
3558 { |
|
3559 xmlChar *ret; |
|
3560 if (val) |
|
3561 ret = xmlStrdup((const xmlChar*) "true"); |
|
3562 else |
|
3563 ret = xmlStrdup((const xmlChar*) "false"); |
|
3564 return(ret); |
|
3565 } |
|
3566 |
|
3567 /** |
|
3568 * xmlXPathCastNumberToString: |
|
3569 * @param val a number |
|
3570 * |
|
3571 * Converts a number to its string value. |
|
3572 * |
|
3573 * Returns a newly allocated string. |
|
3574 * |
|
3575 * OOM: possible --> returns NULL |
|
3576 */ |
|
3577 XMLPUBFUNEXPORT xmlChar* |
|
3578 xmlXPathCastNumberToString (double val) |
|
3579 { |
|
3580 xmlChar *ret; |
|
3581 switch (xmlXPathIsInf(val)) |
|
3582 { |
|
3583 case 1: |
|
3584 ret = xmlStrdup((const xmlChar*) "Infinity"); |
|
3585 break; |
|
3586 case -1: |
|
3587 ret = xmlStrdup((const xmlChar*) "-Infinity"); |
|
3588 break; |
|
3589 default: |
|
3590 if (xmlXPathIsNaN(val)) { |
|
3591 ret = xmlStrdup((const xmlChar*) "NaN"); |
|
3592 } else if (val == 0 && xmlXPathGetSign(val) != 0) { |
|
3593 ret = xmlStrdup((const xmlChar*) "0"); |
|
3594 } else { |
|
3595 |
|
3596 char buf[100]; |
|
3597 xmlXPathFormatNumber(val, buf, 100); |
|
3598 ret = xmlStrdup((const xmlChar *) buf); |
|
3599 } |
|
3600 } |
|
3601 return(ret); |
|
3602 } |
|
3603 |
|
3604 /** |
|
3605 * xmlXPathCastNodeToString: |
|
3606 * @param node a node |
|
3607 * |
|
3608 * Converts a node to its string value. |
|
3609 * |
|
3610 * Returns a newly allocated string. |
|
3611 * |
|
3612 * OOM: possible --> returns NULL; OOM flag must be checked ALWAYS |
|
3613 */ |
|
3614 XMLPUBFUNEXPORT xmlChar* |
|
3615 xmlXPathCastNodeToString(xmlNodePtr node) |
|
3616 { |
|
3617 return xmlNodeGetContent(node); |
|
3618 } |
|
3619 |
|
3620 /** |
|
3621 * xmlXPathCastNodeSetToString: |
|
3622 * @param ns a node-set |
|
3623 * |
|
3624 * Converts a node-set to its string value. |
|
3625 * |
|
3626 * Returns a newly allocated string. |
|
3627 * |
|
3628 * OOM: possible --> OOM flag must be checked |
|
3629 */ |
|
3630 XMLPUBFUNEXPORT xmlChar* |
|
3631 xmlXPathCastNodeSetToString(xmlNodeSetPtr ns) |
|
3632 { // ISSUE: what are rules for string xmlXPathObject? can value be NULL or |
|
3633 // must be "" instead always? |
|
3634 if (!ns || (ns->nodeNr == 0) || !ns->nodeTab) |
|
3635 return(xmlStrdup((const xmlChar *) "")); |
|
3636 |
|
3637 xmlXPathNodeSetSort(ns); |
|
3638 return(xmlXPathCastNodeToString(ns->nodeTab[0])); |
|
3639 } |
|
3640 |
|
3641 /** |
|
3642 * xmlXPathCastToString: |
|
3643 * @param val an XPath object |
|
3644 * |
|
3645 * Converts an existing object to its string() equivalent |
|
3646 * |
|
3647 * Returns the string value of the object, NULL in case of error. |
|
3648 * A new string is allocated only if needed (val isn't a |
|
3649 * string object). |
|
3650 */ |
|
3651 XMLPUBFUNEXPORT xmlChar * |
|
3652 xmlXPathCastToString(xmlXPathObjectPtr val) |
|
3653 { |
|
3654 |
|
3655 |
|
3656 xmlChar *ret = NULL; |
|
3657 |
|
3658 if (val == NULL) |
|
3659 return(xmlStrdup((const xmlChar *) "")); |
|
3660 |
|
3661 switch (val->type) { |
|
3662 case XPATH_UNDEFINED: |
|
3663 #ifdef DEBUG_EXPR |
|
3664 xmlGenericError(xmlGenericErrorContext, "String: undefined\n"); |
|
3665 #endif |
|
3666 ret = xmlStrdup((const xmlChar *) ""); |
|
3667 break; |
|
3668 case XPATH_NUMBER: |
|
3669 ret = xmlXPathCastNumberToString(val->floatval); |
|
3670 break; |
|
3671 |
|
3672 case XPATH_NODESET: |
|
3673 case XPATH_XSLT_TREE: |
|
3674 ret = xmlXPathCastNodeSetToString(val->nodesetval); |
|
3675 break; |
|
3676 |
|
3677 case XPATH_BOOLEAN: |
|
3678 ret = xmlXPathCastBooleanToString(val->boolval); |
|
3679 break; |
|
3680 |
|
3681 case XPATH_STRING: |
|
3682 return(xmlStrdup(val->stringval)); |
|
3683 |
|
3684 case XPATH_USERS: |
|
3685 case XPATH_POINT: |
|
3686 case XPATH_RANGE: |
|
3687 case XPATH_LOCATIONSET: |
|
3688 |
|
3689 ret = xmlStrdup((const xmlChar *) ""); |
|
3690 break; |
|
3691 } |
|
3692 return(ret); |
|
3693 } |
|
3694 |
|
3695 /** |
|
3696 * xmlXPathConvertString: |
|
3697 * @param val an XPath object |
|
3698 * |
|
3699 * Converts an existing object to its string() equivalent |
|
3700 * |
|
3701 * Returns the new object, the old one is freed (or the operation |
|
3702 * is done directly on val) |
|
3703 * OOM: returns NULL if OOM; |
|
3704 */ |
|
3705 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3706 xmlXPathConvertString(xmlXPathObjectPtr val) |
|
3707 { |
|
3708 LOAD_GS_DIRECT |
|
3709 xmlChar* res; |
|
3710 xmlXPathObjectPtr ret; |
|
3711 |
|
3712 if (!val) |
|
3713 return(xmlXPathNewCString("")); |
|
3714 |
|
3715 switch (val->type) |
|
3716 { |
|
3717 case XPATH_NODESET: |
|
3718 case XPATH_XSLT_TREE: |
|
3719 res = xmlXPathCastNodeSetToString(val->nodesetval); |
|
3720 if(OOM_FLAG && res){ |
|
3721 xmlFree(res); |
|
3722 res = NULL; |
|
3723 } |
|
3724 break; |
|
3725 |
|
3726 case XPATH_STRING: |
|
3727 return(val); |
|
3728 |
|
3729 case XPATH_BOOLEAN: |
|
3730 res = xmlXPathCastBooleanToString(val->boolval); |
|
3731 break; |
|
3732 |
|
3733 case XPATH_NUMBER: |
|
3734 res = xmlXPathCastNumberToString(val->floatval); |
|
3735 break; |
|
3736 |
|
3737 case XPATH_UNDEFINED: |
|
3738 #ifdef DEBUG_EXPR |
|
3739 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n"); |
|
3740 #endif |
|
3741 //break; |
|
3742 //-- FALLTHROUGH into the default branch --// |
|
3743 /* defaulted: |
|
3744 case XPATH_USERS: |
|
3745 case XPATH_POINT: |
|
3746 case XPATH_RANGE: |
|
3747 case XPATH_LOCATIONSET: |
|
3748 */ |
|
3749 default: |
|
3750 // Nothing to convert or unknown object, so return "" |
|
3751 xmlXPathFreeObject(val); |
|
3752 return xmlXPathNewCString(""); |
|
3753 } // end switch |
|
3754 |
|
3755 |
|
3756 xmlXPathFreeObject(val); |
|
3757 // Note: by now 'res' is NULL only if OOM happend -- |
|
3758 // functions above never return NULL normally or 'res' is set to NULL in OOM |
|
3759 if(!res) |
|
3760 return(NULL); |
|
3761 |
|
3762 ret = xmlXPathWrapString(res); |
|
3763 if(!ret) // OOM! |
|
3764 { |
|
3765 xmlFree(res); |
|
3766 return(NULL); |
|
3767 } |
|
3768 return(ret); |
|
3769 } |
|
3770 |
|
3771 /** |
|
3772 * xmlXPathCastBooleanToNumber: |
|
3773 * @param val a boolean |
|
3774 * |
|
3775 * Converts a boolean to its number value |
|
3776 * |
|
3777 * Returns the number value |
|
3778 */ |
|
3779 XMLPUBFUNEXPORT double |
|
3780 xmlXPathCastBooleanToNumber(int val) { |
|
3781 return val ? (1.0) :(0.0); |
|
3782 } |
|
3783 |
|
3784 /** |
|
3785 * xmlXPathCastStringToNumber: |
|
3786 * @param val a string |
|
3787 * |
|
3788 * Converts a string to its number value |
|
3789 * |
|
3790 * Returns the number value |
|
3791 * |
|
3792 * OOM: Never |
|
3793 */ |
|
3794 XMLPUBFUNEXPORT double |
|
3795 xmlXPathCastStringToNumber(const xmlChar * val) { |
|
3796 return(xmlXPathStringEvalNumber(val)); |
|
3797 } |
|
3798 |
|
3799 /** |
|
3800 * xmlXPathCastNodeToNumber: |
|
3801 * @param node a node |
|
3802 * |
|
3803 * Converts a node to its number value |
|
3804 * |
|
3805 * Returns the number value |
|
3806 */ |
|
3807 XMLPUBFUNEXPORT double |
|
3808 xmlXPathCastNodeToNumber (xmlNodePtr node) { |
|
3809 xmlChar *strval; |
|
3810 double ret; |
|
3811 LOAD_GS_DIRECT |
|
3812 |
|
3813 if (node == NULL) |
|
3814 return(xmlXPathNAN); |
|
3815 strval = xmlXPathCastNodeToString(node); |
|
3816 if (!strval) |
|
3817 return(xmlXPathNAN); |
|
3818 |
|
3819 ret = xmlXPathCastStringToNumber(strval); |
|
3820 xmlFree(strval); |
|
3821 |
|
3822 return(ret); |
|
3823 } |
|
3824 |
|
3825 /** |
|
3826 * xmlXPathCastNodeSetToNumber: |
|
3827 * @param ns a node-set |
|
3828 * |
|
3829 * Converts a node-set to its number value |
|
3830 * |
|
3831 * Returns the number value |
|
3832 * |
|
3833 * OOM: possible --> OOM flag must be checked |
|
3834 */ |
|
3835 XMLPUBFUNEXPORT double |
|
3836 xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) { |
|
3837 LOAD_GS_DIRECT |
|
3838 xmlChar *str; |
|
3839 double ret; |
|
3840 |
|
3841 if (ns == NULL) |
|
3842 return(xmlXPathNAN); |
|
3843 str = xmlXPathCastNodeSetToString(ns); // OOM is possible |
|
3844 if(str){ |
|
3845 ret = xmlXPathCastStringToNumber(str); |
|
3846 xmlFree(str); |
|
3847 return(ret); |
|
3848 } |
|
3849 return 0; |
|
3850 } |
|
3851 |
|
3852 /** |
|
3853 * xmlXPathCastToNumber: |
|
3854 * @param val an XPath object |
|
3855 * |
|
3856 * Converts an XPath object to its number value |
|
3857 * |
|
3858 * Returns the number value |
|
3859 */ |
|
3860 XMLPUBFUNEXPORT double |
|
3861 xmlXPathCastToNumber(xmlXPathObjectPtr val) { |
|
3862 LOAD_GS_DIRECT |
|
3863 double ret = 0.0; |
|
3864 |
|
3865 if (val == NULL) |
|
3866 return(xmlXPathNAN); |
|
3867 switch (val->type) { |
|
3868 case XPATH_UNDEFINED: |
|
3869 #ifdef DEGUB_EXPR |
|
3870 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n"); |
|
3871 #endif |
|
3872 ret = xmlXPathNAN; |
|
3873 break; |
|
3874 case XPATH_NODESET: |
|
3875 case XPATH_XSLT_TREE: |
|
3876 ret = xmlXPathCastNodeSetToNumber(val->nodesetval); |
|
3877 break; |
|
3878 case XPATH_STRING: |
|
3879 ret = xmlXPathCastStringToNumber(val->stringval); |
|
3880 break; |
|
3881 case XPATH_NUMBER: |
|
3882 ret = val->floatval; |
|
3883 break; |
|
3884 case XPATH_BOOLEAN: |
|
3885 ret = xmlXPathCastBooleanToNumber(val->boolval); |
|
3886 break; |
|
3887 case XPATH_USERS: |
|
3888 case XPATH_POINT: |
|
3889 case XPATH_RANGE: |
|
3890 case XPATH_LOCATIONSET: |
|
3891 TODO; |
|
3892 ret = xmlXPathNAN; |
|
3893 break; |
|
3894 } |
|
3895 return(ret); |
|
3896 } |
|
3897 |
|
3898 /** |
|
3899 * xmlXPathConvertNumber: |
|
3900 * @param val an XPath object |
|
3901 * |
|
3902 * Converts an existing object to its number() equivalent |
|
3903 * |
|
3904 * Returns the new object, the old one is freed (or the operation |
|
3905 * is done directly on val) |
|
3906 */ |
|
3907 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
3908 xmlXPathConvertNumber(xmlXPathObjectPtr val) { |
|
3909 xmlXPathObjectPtr ret; |
|
3910 |
|
3911 if (val == NULL) |
|
3912 return(xmlXPathNewFloat(0.0)); |
|
3913 if (val->type == XPATH_NUMBER) |
|
3914 return(val); |
|
3915 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val)); |
|
3916 xmlXPathFreeObject(val); |
|
3917 return(ret); |
|
3918 } |
|
3919 |
|
3920 /** |
|
3921 * xmlXPathCastNumberToBoolean: |
|
3922 * @param val a number |
|
3923 * |
|
3924 * Converts a number to its boolean value |
|
3925 * |
|
3926 * Returns the boolean value |
|
3927 */ |
|
3928 XMLPUBFUNEXPORT int |
|
3929 xmlXPathCastNumberToBoolean (double val) { |
|
3930 if (xmlXPathIsNaN(val) || (val == 0.0)) |
|
3931 return(0); |
|
3932 return(1); |
|
3933 } |
|
3934 |
|
3935 /** |
|
3936 * xmlXPathCastStringToBoolean: |
|
3937 * @param val a string |
|
3938 * |
|
3939 * Converts a string to its boolean value |
|
3940 * |
|
3941 * Returns the boolean value |
|
3942 * |
|
3943 * OOM: never |
|
3944 */ |
|
3945 XMLPUBFUNEXPORT int |
|
3946 xmlXPathCastStringToBoolean (const xmlChar *val) { |
|
3947 if ((val == NULL) || (xmlStrlen(val) == 0)) |
|
3948 return(0); |
|
3949 return(1); |
|
3950 } |
|
3951 |
|
3952 /** |
|
3953 * xmlXPathCastNodeSetToBoolean: |
|
3954 * @param ns a node-set |
|
3955 * |
|
3956 * Converts a node-set to its boolean value |
|
3957 * |
|
3958 * Returns the boolean value |
|
3959 * |
|
3960 * OOM: never |
|
3961 */ |
|
3962 XMLPUBFUNEXPORT int |
|
3963 xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) { |
|
3964 if ((ns == NULL) || (ns->nodeNr == 0)) |
|
3965 return(0); |
|
3966 return(1); |
|
3967 } |
|
3968 |
|
3969 /** |
|
3970 * xmlXPathCastToBoolean: |
|
3971 * @param val an XPath object |
|
3972 * |
|
3973 * Converts an XPath object to its boolean value |
|
3974 * |
|
3975 * Returns the boolean value |
|
3976 */ |
|
3977 XMLPUBFUNEXPORT int |
|
3978 xmlXPathCastToBoolean (xmlXPathObjectPtr val) { |
|
3979 int ret = 0; |
|
3980 |
|
3981 if (val == NULL) |
|
3982 return(0); |
|
3983 switch (val->type) { |
|
3984 case XPATH_UNDEFINED: |
|
3985 #ifdef DEBUG_EXPR |
|
3986 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n"); |
|
3987 #endif |
|
3988 ret = 0; |
|
3989 break; |
|
3990 case XPATH_NODESET: |
|
3991 case XPATH_XSLT_TREE: |
|
3992 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval); |
|
3993 break; |
|
3994 case XPATH_STRING: |
|
3995 ret = xmlXPathCastStringToBoolean(val->stringval); |
|
3996 break; |
|
3997 case XPATH_NUMBER: |
|
3998 ret = xmlXPathCastNumberToBoolean(val->floatval); |
|
3999 break; |
|
4000 case XPATH_BOOLEAN: |
|
4001 ret = val->boolval; |
|
4002 break; |
|
4003 case XPATH_USERS: |
|
4004 case XPATH_POINT: |
|
4005 case XPATH_RANGE: |
|
4006 case XPATH_LOCATIONSET: |
|
4007 TODO; |
|
4008 ret = 0; |
|
4009 break; |
|
4010 } |
|
4011 return(ret); |
|
4012 } |
|
4013 |
|
4014 |
|
4015 /** |
|
4016 * xmlXPathConvertBoolean: |
|
4017 * @param val an XPath object |
|
4018 * |
|
4019 * Converts an existing object to its boolean() equivalent |
|
4020 * |
|
4021 * Returns the new object, the old one is freed (or the operation |
|
4022 * is done directly on val) |
|
4023 */ |
|
4024 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
4025 xmlXPathConvertBoolean(xmlXPathObjectPtr val) { |
|
4026 xmlXPathObjectPtr ret; |
|
4027 |
|
4028 if (val == NULL) |
|
4029 return(xmlXPathNewBoolean(0)); |
|
4030 if (val->type == XPATH_BOOLEAN) |
|
4031 return(val); |
|
4032 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val)); |
|
4033 xmlXPathFreeObject(val); |
|
4034 return(ret); |
|
4035 } |
|
4036 |
|
4037 /************************************************************************ |
|
4038 * * |
|
4039 * Routines to handle XPath contexts * |
|
4040 * * |
|
4041 ************************************************************************/ |
|
4042 |
|
4043 /** |
|
4044 * xmlHashCopier: |
|
4045 * |
|
4046 *Prototyped by: |
|
4047 * typedef void *(*xmlHashCopier)(void *payload, xmlChar *name); |
|
4048 */ |
|
4049 void* xeSimpleHashEntryCopier(void *payload, xmlChar* name) |
|
4050 { |
|
4051 return payload; // just return the same value |
|
4052 } |
|
4053 |
|
4054 /** |
|
4055 * xmlXPathNewContext: |
|
4056 * @param doc the XML document |
|
4057 * |
|
4058 * Create a new xmlXPathContext |
|
4059 * |
|
4060 * Returns the xmlXPathContext just allocated. The caller will need to free it. |
|
4061 */ |
|
4062 XMLPUBFUNEXPORT xmlXPathContextPtr |
|
4063 xmlXPathNewContext(xmlDocPtr doc) |
|
4064 { |
|
4065 xmlXPathContextPtr ret; |
|
4066 xmlHashTablePtr gFuncHash; |
|
4067 LOAD_GS_DIRECT |
|
4068 |
|
4069 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext)); |
|
4070 if (!ret) |
|
4071 goto OOM; |
|
4072 |
|
4073 memset(ret, 0 , (size_t) sizeof(xmlXPathContext)); |
|
4074 |
|
4075 ret->doc = doc; // this is a context node in general, not always a document node |
|
4076 |
|
4077 // XMLENGINE: these are nullified already |
|
4078 //ret->node = NULL; |
|
4079 //ret->varHash = NULL; |
|
4080 //ret->nb_types = 0; |
|
4081 //ret->max_types = 0; |
|
4082 //ret->types = NULL; |
|
4083 |
|
4084 // XMLENGINE: BEGIN REPLACE CODE |
|
4085 /* REPLACED original code |
|
4086 ret->funcHash = xmlHashCreate(0); |
|
4087 xmlXPathRegisterAllFunctions(ret); |
|
4088 */ |
|
4089 |
|
4090 // xmlXPathDefaultFunctionsHash is the new field in xmlGlobalState structure |
|
4091 gFuncHash = xmlXPathDefaultFunctionsHash; |
|
4092 if(!gFuncHash) |
|
4093 { // global reusable hash table was not initialized yet; it will be done here: |
|
4094 xmlXPathRegisterAllFunctions(ret); |
|
4095 if(OOM_FLAG) |
|
4096 goto OOM_ret; |
|
4097 |
|
4098 if(xmlXPathDefineExtensionFunctionsGlobally) |
|
4099 { |
|
4100 // Now the hash table is owned by XML Engine and will be reused: |
|
4101 xmlXPathDefaultFunctionsHash = ret->funcHash; |
|
4102 } |
|
4103 else |
|
4104 { |
|
4105 // Cache initialized function hash table; it will be copied later into each XPath context |
|
4106 xmlXPathDefaultFunctionsHash = xmlHashCopy(ret->funcHash, xeSimpleHashEntryCopier); |
|
4107 } |
|
4108 } |
|
4109 else |
|
4110 { // copy previously initialized hash table OR |
|
4111 // reuse the global hash, then, no cleanup is needed when |
|
4112 // XPath context is freed |
|
4113 if(xmlXPathDefineExtensionFunctionsGlobally) |
|
4114 { |
|
4115 // reuse global function hash |
|
4116 ret->funcHash = gFuncHash; |
|
4117 } |
|
4118 else |
|
4119 { |
|
4120 // use cached hash table for creating hash table for the given context |
|
4121 ret->funcHash = xmlHashCopy( gFuncHash, xeSimpleHashEntryCopier ); |
|
4122 if(OOM_FLAG) |
|
4123 goto OOM_ret; |
|
4124 } |
|
4125 } |
|
4126 // XMLENGINE: END REPLACE CODE |
|
4127 |
|
4128 // XMLENGINE: these are nullified already |
|
4129 //ret->nb_axis = 0; |
|
4130 //ret->max_axis = 0; |
|
4131 //ret->axis = NULL; |
|
4132 //ret->nsHash = NULL; |
|
4133 //ret->user = NULL; |
|
4134 |
|
4135 ret->contextSize = -1; |
|
4136 ret->proximityPosition = -1; |
|
4137 |
|
4138 return(ret); |
|
4139 |
|
4140 //---- OOM |
|
4141 OOM_ret: |
|
4142 xmlFree(ret); |
|
4143 OOM: |
|
4144 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating context\n")); |
|
4145 return(NULL); |
|
4146 } |
|
4147 |
|
4148 /** |
|
4149 * xmlXPathFreeContext: |
|
4150 * @param ctxt the context to free |
|
4151 * |
|
4152 * Free up an xmlXPathContext |
|
4153 */ |
|
4154 XMLPUBFUNEXPORT void |
|
4155 xmlXPathFreeContext(xmlXPathContextPtr ctxt) { |
|
4156 LOAD_GS_DIRECT |
|
4157 |
|
4158 |
|
4159 if (&ctxt->lastError) |
|
4160 xmlResetError(&ctxt->lastError); |
|
4161 |
|
4162 xmlXPathRegisteredNsCleanup(ctxt); |
|
4163 |
|
4164 // XMLENGINE: BEGIN REPLACE CODE |
|
4165 /* ORIGINAL CODE |
|
4166 xmlXPathRegisteredFuncsCleanup(ctxt); |
|
4167 */ |
|
4168 |
|
4169 // DO NOTHING HERE if global hash table is reused. |
|
4170 // The function hash is destroyed only if it is not reused, |
|
4171 // otherwise the reference to it is stored in global: |
|
4172 // xmlXPathDefaultFunctionsHash variable |
|
4173 // and the hash table is freed during XML Engine cleanup with |
|
4174 // xeXPathCleanup() |
|
4175 if(!xmlXPathDefineExtensionFunctionsGlobally) |
|
4176 { |
|
4177 xmlXPathRegisteredFuncsCleanup(ctxt); |
|
4178 } |
|
4179 // XMLENGINE: END REPLACE CODE |
|
4180 |
|
4181 xmlXPathRegisteredVariablesCleanup(ctxt); |
|
4182 xmlFree(ctxt); |
|
4183 } |
|
4184 |
|
4185 /************************************************************************ |
|
4186 * * |
|
4187 * Routines to handle XPath parser contexts * |
|
4188 * * |
|
4189 ************************************************************************/ |
|
4190 |
|
4191 #define CHECK_CTXT(ctxt) \ |
|
4192 if (ctxt == NULL) { \ |
|
4193 xmlGenericError(xmlGenericErrorContext, \ |
|
4194 EMBED_ERRTXT("%s:%d Internal error: ctxt == NULL\n"), \ |
|
4195 __FILE__, __LINE__); \ |
|
4196 } \ |
|
4197 |
|
4198 |
|
4199 |
|
4200 #define CHECK_CONTEXT(ctxt) \ |
|
4201 if (ctxt == NULL) { \ |
|
4202 xmlGenericError(xmlGenericErrorContext, \ |
|
4203 EMBED_ERRTXT("%s:%d Internal error: no context\n"), \ |
|
4204 __FILE__, __LINE__); \ |
|
4205 } \ |
|
4206 else if (ctxt->doc == NULL) { \ |
|
4207 xmlGenericError(xmlGenericErrorContext, \ |
|
4208 EMBED_ERRTXT("%s:%d Internal error: no document\n"), \ |
|
4209 __FILE__, __LINE__); \ |
|
4210 } \ |
|
4211 else if (ctxt->doc->children == NULL) { \ |
|
4212 xmlGenericError(xmlGenericErrorContext, \ |
|
4213 EMBED_ERRTXT("%s:%d Internal error: document without root\n"), \ |
|
4214 __FILE__, __LINE__); \ |
|
4215 } \ |
|
4216 |
|
4217 |
|
4218 /** |
|
4219 * xmlXPathNewParserContext: |
|
4220 * @param str the XPath expression |
|
4221 * @param ctxt the XPath context |
|
4222 * |
|
4223 * Create a new xmlXPathParserContext |
|
4224 * |
|
4225 * Returns the xmlXPathParserContext just allocated. |
|
4226 */ |
|
4227 XMLPUBFUNEXPORT xmlXPathParserContextPtr |
|
4228 xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) { |
|
4229 xmlXPathParserContextPtr ret; |
|
4230 |
|
4231 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); |
|
4232 if (ret == NULL) { |
|
4233 xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating parser context\n")); |
|
4234 return(NULL); |
|
4235 } |
|
4236 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); |
|
4237 ret->cur = ret->base = str; |
|
4238 ret->context = ctxt; |
|
4239 |
|
4240 ret->comp = xmlXPathNewCompExpr(); |
|
4241 if (ret->comp == NULL) { |
|
4242 xmlFree(ret->valueTab); |
|
4243 xmlFree(ret); |
|
4244 return(NULL); |
|
4245 } |
|
4246 if ((ctxt != NULL) && (ctxt->dict != NULL)) { |
|
4247 ret->comp->dict = ctxt->dict; |
|
4248 xmlDictReference(ret->comp->dict); |
|
4249 } |
|
4250 |
|
4251 return(ret); |
|
4252 } |
|
4253 |
|
4254 /** |
|
4255 * xmlXPathCompParserContext: |
|
4256 * @param comp the XPath compiled expression |
|
4257 * @param ctxt the XPath context |
|
4258 * |
|
4259 * Create a new xmlXPathParserContext when processing a compiled expression |
|
4260 * |
|
4261 * Returns the xmlXPathParserContext just allocated. |
|
4262 */ |
|
4263 static xmlXPathParserContextPtr |
|
4264 xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) { |
|
4265 xmlXPathParserContextPtr ret; |
|
4266 |
|
4267 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); |
|
4268 if (!ret) { |
|
4269 xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n")); |
|
4270 return(NULL); |
|
4271 } |
|
4272 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); |
|
4273 |
|
4274 /* Allocate the value stack */ |
|
4275 ret->valueTab = (xmlXPathObjectPtr *) |
|
4276 xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); |
|
4277 if (!ret->valueTab) { |
|
4278 xmlFree(ret); |
|
4279 xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n")); |
|
4280 return(NULL); |
|
4281 } |
|
4282 // all other fields were nullified already |
|
4283 //ret->valueNr = 0; |
|
4284 ret->valueMax = 10; |
|
4285 //ret->value = NULL; |
|
4286 |
|
4287 ret->context = ctxt; |
|
4288 ret->comp = comp; |
|
4289 |
|
4290 return(ret); |
|
4291 } |
|
4292 |
|
4293 /** |
|
4294 * xmlXPathFreeParserContext: |
|
4295 * @param ctxt the context to free |
|
4296 * |
|
4297 * Free up an xmlXPathParserContext |
|
4298 */ |
|
4299 XMLPUBFUNEXPORT void |
|
4300 xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) { |
|
4301 if (ctxt->valueTab != NULL) { |
|
4302 xmlFree(ctxt->valueTab); |
|
4303 } |
|
4304 if (ctxt->comp) |
|
4305 xmlXPathFreeCompExpr(ctxt->comp); |
|
4306 xmlFree(ctxt); |
|
4307 } |
|
4308 |
|
4309 /************************************************************************ |
|
4310 * * |
|
4311 * The implicit core function library * |
|
4312 * * |
|
4313 ************************************************************************/ |
|
4314 |
|
4315 /** |
|
4316 * xmlXPathNodeValHash: |
|
4317 * @param node a node pointer |
|
4318 * |
|
4319 * Function computing the beginning of the string value of the node, |
|
4320 * used to speed up comparisons |
|
4321 * |
|
4322 * Returns an int usable as a hash |
|
4323 */ |
|
4324 static unsigned int |
|
4325 xmlXPathNodeValHash(xmlNodePtr node) { |
|
4326 int len = 2; |
|
4327 const xmlChar * string = NULL; |
|
4328 xmlNodePtr tmp = NULL; |
|
4329 unsigned int ret = 0; |
|
4330 |
|
4331 if (node == NULL) |
|
4332 return(0); |
|
4333 |
|
4334 if (node->type == XML_DOCUMENT_NODE) { |
|
4335 tmp = xmlDocGetRootElement((xmlDocPtr) node); |
|
4336 if (tmp == NULL) |
|
4337 node = node->children; |
|
4338 else |
|
4339 node = tmp; |
|
4340 |
|
4341 if (node == NULL) |
|
4342 return(0); |
|
4343 } |
|
4344 |
|
4345 switch (node->type) { |
|
4346 case XML_COMMENT_NODE: |
|
4347 case XML_PI_NODE: |
|
4348 case XML_CDATA_SECTION_NODE: |
|
4349 case XML_TEXT_NODE: |
|
4350 string = node->content; |
|
4351 if (string == NULL) |
|
4352 return(0); |
|
4353 if (string[0] == 0) |
|
4354 return(0); |
|
4355 return(((unsigned int) string[0]) + |
|
4356 (((unsigned int) string[1]) << 8)); |
|
4357 case XML_NAMESPACE_DECL: |
|
4358 string = ((xmlNsPtr)node)->href; |
|
4359 if (string == NULL) |
|
4360 return(0); |
|
4361 if (string[0] == 0) |
|
4362 return(0); |
|
4363 return(((unsigned int) string[0]) + |
|
4364 (((unsigned int) string[1]) << 8)); |
|
4365 case XML_ATTRIBUTE_NODE: |
|
4366 tmp = ((xmlAttrPtr) node)->children; |
|
4367 break; |
|
4368 case XML_ELEMENT_NODE: |
|
4369 tmp = node->children; |
|
4370 break; |
|
4371 default: |
|
4372 return(0); |
|
4373 } |
|
4374 while (tmp != NULL) { |
|
4375 switch (tmp->type) { |
|
4376 case XML_COMMENT_NODE: |
|
4377 case XML_PI_NODE: |
|
4378 case XML_CDATA_SECTION_NODE: |
|
4379 case XML_TEXT_NODE: |
|
4380 string = tmp->content; |
|
4381 break; |
|
4382 case XML_NAMESPACE_DECL: |
|
4383 string = ((xmlNsPtr)tmp)->href; |
|
4384 break; |
|
4385 default: |
|
4386 break; |
|
4387 } |
|
4388 if ((string != NULL) && (string[0] != 0)) { |
|
4389 if (len == 1) { |
|
4390 return(ret + (((unsigned int) string[0]) << 8)); |
|
4391 } |
|
4392 if (string[1] == 0) { |
|
4393 len = 1; |
|
4394 ret = (unsigned int) string[0]; |
|
4395 } else { |
|
4396 return(((unsigned int) string[0]) + |
|
4397 (((unsigned int) string[1]) << 8)); |
|
4398 } |
|
4399 } |
|
4400 /* |
|
4401 * Skip to next node |
|
4402 */ |
|
4403 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) { |
|
4404 if (tmp->children->type != XML_ENTITY_DECL) { |
|
4405 tmp = tmp->children; |
|
4406 continue; |
|
4407 } |
|
4408 } |
|
4409 if (tmp == node) |
|
4410 break; |
|
4411 |
|
4412 if (tmp->next != NULL) { |
|
4413 tmp = tmp->next; |
|
4414 continue; |
|
4415 } |
|
4416 |
|
4417 do { |
|
4418 tmp = tmp->parent; |
|
4419 if (tmp == NULL) |
|
4420 break; |
|
4421 if (tmp == node) { |
|
4422 tmp = NULL; |
|
4423 break; |
|
4424 } |
|
4425 if (tmp->next != NULL) { |
|
4426 tmp = tmp->next; |
|
4427 break; |
|
4428 } |
|
4429 } while (tmp != NULL); |
|
4430 } |
|
4431 return(ret); |
|
4432 } |
|
4433 |
|
4434 /** |
|
4435 * xmlXPathStringHash: |
|
4436 * @param string a string |
|
4437 * |
|
4438 * Function computing the beginning of the string value of the node, |
|
4439 * used to speed up comparisons |
|
4440 * |
|
4441 * Returns an int usable as a hash |
|
4442 */ |
|
4443 static unsigned int |
|
4444 xmlXPathStringHash(const xmlChar * string) { |
|
4445 if (string == NULL) |
|
4446 return((unsigned int) 0); |
|
4447 if (string[0] == 0) |
|
4448 return(0); |
|
4449 return(((unsigned int) string[0]) + |
|
4450 (((unsigned int) string[1]) << 8)); |
|
4451 } |
|
4452 |
|
4453 /** |
|
4454 * xmlXPathCompareNodeSetFloat: |
|
4455 * @param ctxt the XPath Parser context |
|
4456 * @param inf less than (1) or greater than (0) |
|
4457 * @param strict is the comparison strict |
|
4458 * @param arg the node set |
|
4459 * @param f the value |
|
4460 * |
|
4461 * Implement the compare operation between a nodeset and a number |
|
4462 * ns < val (1, 1, ... |
|
4463 * ns <= val (1, 0, ... |
|
4464 * ns > val (0, 1, ... |
|
4465 * ns >= val (0, 0, ... |
|
4466 * |
|
4467 * If one object to be compared is a node-set and the other is a number, |
|
4468 * then the comparison will be true if and only if there is a node in the |
|
4469 * node-set such that the result of performing the comparison on the number |
|
4470 * to be compared and on the result of converting the string-value of that |
|
4471 * node to a number using the number function is true. |
|
4472 * |
|
4473 * Returns 0 or 1 depending on the results of the test. |
|
4474 */ |
|
4475 static int |
|
4476 xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict, |
|
4477 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) { |
|
4478 int i, ret = 0; |
|
4479 xmlNodeSetPtr ns; |
|
4480 xmlChar *str2; |
|
4481 |
|
4482 if ((f == NULL) || (arg == NULL) || |
|
4483 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { |
|
4484 xmlXPathFreeObject(arg); |
|
4485 xmlXPathFreeObject(f); |
|
4486 return(0); |
|
4487 } |
|
4488 ns = arg->nodesetval; |
|
4489 if (ns != NULL) { |
|
4490 for (i = 0;i < ns->nodeNr;i++) { |
|
4491 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); |
|
4492 if (str2 != NULL) { |
|
4493 valuePush(ctxt, |
|
4494 xmlXPathNewString(str2)); |
|
4495 xmlFree(str2); |
|
4496 xmlXPathNumberFunction(ctxt, 1); |
|
4497 valuePush(ctxt, xmlXPathObjectCopy(f)); |
|
4498 ret = xmlXPathCompareValues(ctxt, inf, strict); |
|
4499 if (ret) |
|
4500 break; |
|
4501 } |
|
4502 } |
|
4503 } |
|
4504 xmlXPathFreeObject(arg); |
|
4505 xmlXPathFreeObject(f); |
|
4506 return(ret); |
|
4507 } |
|
4508 |
|
4509 /** |
|
4510 * xmlXPathCompareNodeSetString: |
|
4511 * @param ctxt the XPath Parser context |
|
4512 * @param inf less than (1) or greater than (0) |
|
4513 * @param strict is the comparison strict |
|
4514 * @param arg the node set |
|
4515 * @param s the value |
|
4516 * |
|
4517 * Implement the compare operation between a nodeset and a string |
|
4518 * ns < val (1, 1, ... |
|
4519 * ns <= val (1, 0, ... |
|
4520 * ns > val (0, 1, ... |
|
4521 * ns >= val (0, 0, ... |
|
4522 * |
|
4523 * If one object to be compared is a node-set and the other is a string, |
|
4524 * then the comparison will be true if and only if there is a node in |
|
4525 * the node-set such that the result of performing the comparison on the |
|
4526 * string-value of the node and the other string is true. |
|
4527 * |
|
4528 * Returns 0 or 1 depending on the results of the test. |
|
4529 */ |
|
4530 static int |
|
4531 xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict, |
|
4532 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) { |
|
4533 int i, ret = 0; |
|
4534 xmlNodeSetPtr ns; |
|
4535 xmlChar *str2; |
|
4536 |
|
4537 if ((s == NULL) || (arg == NULL) || |
|
4538 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { |
|
4539 xmlXPathFreeObject(arg); |
|
4540 xmlXPathFreeObject(s); |
|
4541 return(0); |
|
4542 } |
|
4543 ns = arg->nodesetval; |
|
4544 if (ns != NULL) { |
|
4545 for (i = 0;i < ns->nodeNr;i++) { |
|
4546 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); |
|
4547 if (str2 != NULL) { |
|
4548 valuePush(ctxt, |
|
4549 xmlXPathNewString(str2)); |
|
4550 xmlFree(str2); |
|
4551 valuePush(ctxt, xmlXPathObjectCopy(s)); |
|
4552 ret = xmlXPathCompareValues(ctxt, inf, strict); |
|
4553 if (ret) |
|
4554 break; |
|
4555 } |
|
4556 } |
|
4557 } |
|
4558 xmlXPathFreeObject(arg); |
|
4559 xmlXPathFreeObject(s); |
|
4560 return(ret); |
|
4561 } |
|
4562 |
|
4563 /** |
|
4564 * xmlXPathCompareNodeSets: |
|
4565 * @param inf less than (1) or greater than (0) |
|
4566 * @param strict is the comparison strict |
|
4567 * @param arg1 the first node set object |
|
4568 * @param arg2 the second node set object |
|
4569 * |
|
4570 * Implement the compare operation on nodesets: |
|
4571 * |
|
4572 * If both objects to be compared are node-sets, then the comparison |
|
4573 * will be true if and only if there is a node in the first node-set |
|
4574 * and a node in the second node-set such that the result of performing |
|
4575 * the comparison on the string-values of the two nodes is true. |
|
4576 * .... |
|
4577 * When neither object to be compared is a node-set and the operator |
|
4578 * is <=, <, >= or >, then the objects are compared by converting both |
|
4579 * objects to numbers and comparing the numbers according to IEEE 754. |
|
4580 * .... |
|
4581 * The number function converts its argument to a number as follows: |
|
4582 * - a string that consists of optional whitespace followed by an |
|
4583 * optional minus sign followed by a Number followed by whitespace |
|
4584 * is converted to the IEEE 754 number that is nearest (according |
|
4585 * to the IEEE 754 round-to-nearest rule) to the mathematical value |
|
4586 * represented by the string; any other string is converted to NaN |
|
4587 * |
|
4588 * Conclusion all nodes need to be converted first to their string value |
|
4589 * and then the comparison must be done when possible |
|
4590 */ |
|
4591 static int |
|
4592 xmlXPathCompareNodeSets(int inf, int strict, |
|
4593 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { |
|
4594 int i, j, init = 0; |
|
4595 double val1; |
|
4596 double *values2; |
|
4597 int ret = 0; |
|
4598 xmlNodeSetPtr ns1; |
|
4599 xmlNodeSetPtr ns2; |
|
4600 |
|
4601 if ((arg1 == NULL) || |
|
4602 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) { |
|
4603 xmlXPathFreeObject(arg2); |
|
4604 return(0); |
|
4605 } |
|
4606 if ((arg2 == NULL) || |
|
4607 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) { |
|
4608 xmlXPathFreeObject(arg1); |
|
4609 xmlXPathFreeObject(arg2); |
|
4610 return(0); |
|
4611 } |
|
4612 |
|
4613 ns1 = arg1->nodesetval; |
|
4614 ns2 = arg2->nodesetval; |
|
4615 |
|
4616 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) { |
|
4617 xmlXPathFreeObject(arg1); |
|
4618 xmlXPathFreeObject(arg2); |
|
4619 return(0); |
|
4620 } |
|
4621 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) { |
|
4622 xmlXPathFreeObject(arg1); |
|
4623 xmlXPathFreeObject(arg2); |
|
4624 return(0); |
|
4625 } |
|
4626 |
|
4627 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double)); |
|
4628 if (values2 == NULL) { |
|
4629 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4630 xmlXPathFreeObject(arg1); |
|
4631 xmlXPathFreeObject(arg2); |
|
4632 return(0); |
|
4633 } |
|
4634 for (i = 0;i < ns1->nodeNr;i++) { |
|
4635 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]); |
|
4636 if (xmlXPathIsNaN(val1)) |
|
4637 continue; |
|
4638 for (j = 0;j < ns2->nodeNr;j++) { |
|
4639 if (init == 0) { |
|
4640 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]); |
|
4641 } |
|
4642 if (xmlXPathIsNaN(values2[j])) |
|
4643 continue; |
|
4644 if (inf && strict) |
|
4645 ret = (val1 < values2[j]); |
|
4646 else if (inf && !strict) |
|
4647 ret = (val1 <= values2[j]); |
|
4648 else if (!inf && strict) |
|
4649 ret = (val1 > values2[j]); |
|
4650 else if (!inf && !strict) |
|
4651 ret = (val1 >= values2[j]); |
|
4652 if (ret) |
|
4653 break; |
|
4654 } |
|
4655 if (ret) |
|
4656 break; |
|
4657 init = 1; |
|
4658 } |
|
4659 xmlFree(values2); |
|
4660 xmlXPathFreeObject(arg1); |
|
4661 xmlXPathFreeObject(arg2); |
|
4662 return(ret); |
|
4663 } |
|
4664 |
|
4665 /** |
|
4666 * xmlXPathCompareNodeSetValue: |
|
4667 * @param ctxt the XPath Parser context |
|
4668 * @param inf less than (1) or greater than (0) |
|
4669 * @param strict is the comparison strict |
|
4670 * @param arg the node set |
|
4671 * @param val the value |
|
4672 * |
|
4673 * Implement the compare operation between a nodeset and a value |
|
4674 * ns < val (1, 1, ... |
|
4675 * ns <= val (1, 0, ... |
|
4676 * ns > val (0, 1, ... |
|
4677 * ns >= val (0, 0, ... |
|
4678 * |
|
4679 * If one object to be compared is a node-set and the other is a boolean, |
|
4680 * then the comparison will be true if and only if the result of performing |
|
4681 * the comparison on the boolean and on the result of converting |
|
4682 * the node-set to a boolean using the boolean function is true. |
|
4683 * |
|
4684 * Returns 0 or 1 depending on the results of the test. |
|
4685 */ |
|
4686 static int |
|
4687 xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict, |
|
4688 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) { |
|
4689 if ((val == NULL) || (arg == NULL) || |
|
4690 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) |
|
4691 return(0); |
|
4692 |
|
4693 switch(val->type) { |
|
4694 case XPATH_NUMBER: |
|
4695 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val)); |
|
4696 case XPATH_NODESET: |
|
4697 case XPATH_XSLT_TREE: |
|
4698 return(xmlXPathCompareNodeSets(inf, strict, arg, val)); |
|
4699 case XPATH_STRING: |
|
4700 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val)); |
|
4701 case XPATH_BOOLEAN: |
|
4702 valuePush(ctxt, arg); |
|
4703 xmlXPathBooleanFunction(ctxt, 1); |
|
4704 valuePush(ctxt, val); |
|
4705 return(xmlXPathCompareValues(ctxt, inf, strict)); |
|
4706 default: |
|
4707 TODO |
|
4708 } |
|
4709 return(0); |
|
4710 } |
|
4711 |
|
4712 /** |
|
4713 * xmlXPathEqualNodeSetString: |
|
4714 * @param arg the nodeset object argument |
|
4715 * @param str the string to compare to. |
|
4716 * @param neq flag to show whether for '=' (0) or '!=' (1) |
|
4717 * |
|
4718 * Implement the equal operation on XPath objects content: arg1 == arg2 |
|
4719 * If one object to be compared is a node-set and the other is a string, |
|
4720 * then the comparison will be true if and only if there is a node in |
|
4721 * the node-set such that the result of performing the comparison on the |
|
4722 * string-value of the node and the other string is true. |
|
4723 * |
|
4724 * Returns 0 or 1 depending on the results of the test. |
|
4725 */ |
|
4726 static int |
|
4727 xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq) |
|
4728 { |
|
4729 int i; |
|
4730 xmlNodeSetPtr ns; |
|
4731 xmlChar *str2; |
|
4732 unsigned int hash; |
|
4733 |
|
4734 if ((str == NULL) || (arg == NULL) || |
|
4735 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) |
|
4736 return (0); |
|
4737 ns = arg->nodesetval; |
|
4738 /* |
|
4739 * A NULL nodeset compared with a string is always false |
|
4740 * (since there is no node equal, and no node not equal) |
|
4741 */ |
|
4742 if ((ns == NULL) || (ns->nodeNr <= 0) ) |
|
4743 return (0); |
|
4744 hash = xmlXPathStringHash(str); |
|
4745 for (i = 0; i < ns->nodeNr; i++) { |
|
4746 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) { |
|
4747 str2 = xmlNodeGetContent(ns->nodeTab[i]); |
|
4748 if ((str2 != NULL) && (xmlStrEqual(str, str2))) { |
|
4749 xmlFree(str2); |
|
4750 if (neq) |
|
4751 continue; |
|
4752 return (1); |
|
4753 } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) { |
|
4754 if (neq) |
|
4755 continue; |
|
4756 return (1); |
|
4757 } else if (neq) { |
|
4758 if (str2 != NULL) |
|
4759 xmlFree(str2); |
|
4760 return (1); |
|
4761 } |
|
4762 if (str2 != NULL) |
|
4763 xmlFree(str2); |
|
4764 } else if (neq) |
|
4765 return (1); |
|
4766 } |
|
4767 return (0); |
|
4768 } |
|
4769 |
|
4770 /** |
|
4771 * xmlXPathEqualNodeSetFloat: |
|
4772 * @param arg the nodeset object argument |
|
4773 * @param f the float to compare to |
|
4774 * @param neq flag to show whether to compare '=' (0) or '!=' (1) |
|
4775 * |
|
4776 * Implement the equal operation on XPath objects content: arg1 == arg2 |
|
4777 * If one object to be compared is a node-set and the other is a number, |
|
4778 * then the comparison will be true if and only if there is a node in |
|
4779 * the node-set such that the result of performing the comparison on the |
|
4780 * number to be compared and on the result of converting the string-value |
|
4781 * of that node to a number using the number function is true. |
|
4782 * |
|
4783 * Returns 0 or 1 depending on the results of the test. |
|
4784 */ |
|
4785 static int |
|
4786 xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt, |
|
4787 xmlXPathObjectPtr arg, double f, int neq) { |
|
4788 int i, ret=0; |
|
4789 xmlNodeSetPtr ns; |
|
4790 xmlChar *str2; |
|
4791 xmlXPathObjectPtr val; |
|
4792 double v; |
|
4793 |
|
4794 if ((arg == NULL) || |
|
4795 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) |
|
4796 return(0); |
|
4797 |
|
4798 ns = arg->nodesetval; |
|
4799 if (ns != NULL) { |
|
4800 for (i=0;i<ns->nodeNr;i++) { |
|
4801 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); |
|
4802 if (str2 != NULL) { |
|
4803 valuePush(ctxt, xmlXPathNewString(str2)); |
|
4804 xmlFree(str2); |
|
4805 xmlXPathNumberFunction(ctxt, 1); |
|
4806 val = valuePop(ctxt); |
|
4807 v = val->floatval; |
|
4808 xmlXPathFreeObject(val); |
|
4809 if (!xmlXPathIsNaN(v)) { |
|
4810 if ((!neq) && (v==f)) { |
|
4811 ret = 1; |
|
4812 break; |
|
4813 } else if ((neq) && (v!=f)) { |
|
4814 ret = 1; |
|
4815 break; |
|
4816 } |
|
4817 } |
|
4818 } |
|
4819 } |
|
4820 } |
|
4821 |
|
4822 return(ret); |
|
4823 } |
|
4824 |
|
4825 |
|
4826 /** |
|
4827 * xmlXPathEqualNodeSets: |
|
4828 * @param arg1 first nodeset object argument |
|
4829 * @param arg2 second nodeset object argument |
|
4830 * @param neq flag to show whether to test '=' (0) or '!=' (1) |
|
4831 * |
|
4832 * Implement the equal / not equal operation on XPath nodesets: |
|
4833 * arg1 == arg2 or arg1 != arg2 |
|
4834 * If both objects to be compared are node-sets, then the comparison |
|
4835 * will be true if and only if there is a node in the first node-set and |
|
4836 * a node in the second node-set such that the result of performing the |
|
4837 * comparison on the string-values of the two nodes is true. |
|
4838 * |
|
4839 * (needless to say, this is a costly operation) |
|
4840 * |
|
4841 * Returns 0 or 1 depending on the results of the test. |
|
4842 */ |
|
4843 static int |
|
4844 xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) { |
|
4845 int i, j; |
|
4846 unsigned int *hashs1; |
|
4847 unsigned int *hashs2; |
|
4848 xmlChar **values1; |
|
4849 xmlChar **values2; |
|
4850 int ret = 0; |
|
4851 xmlNodeSetPtr ns1; |
|
4852 xmlNodeSetPtr ns2; |
|
4853 |
|
4854 if ((arg1 == NULL) || |
|
4855 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) |
|
4856 return(0); |
|
4857 if ((arg2 == NULL) || |
|
4858 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) |
|
4859 return(0); |
|
4860 |
|
4861 ns1 = arg1->nodesetval; |
|
4862 ns2 = arg2->nodesetval; |
|
4863 |
|
4864 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) |
|
4865 return(0); |
|
4866 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) |
|
4867 return(0); |
|
4868 |
|
4869 /* |
|
4870 * for equal, check if there is a node pertaining to both sets |
|
4871 */ |
|
4872 if (neq == 0) |
|
4873 for (i = 0;i < ns1->nodeNr;i++) |
|
4874 for (j = 0;j < ns2->nodeNr;j++) |
|
4875 if (ns1->nodeTab[i] == ns2->nodeTab[j]) |
|
4876 return(1); |
|
4877 |
|
4878 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *)); |
|
4879 if (values1 == NULL) { |
|
4880 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4881 return(0); |
|
4882 } |
|
4883 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int)); |
|
4884 if (hashs1 == NULL) { |
|
4885 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4886 xmlFree(values1); |
|
4887 return(0); |
|
4888 } |
|
4889 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *)); |
|
4890 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *)); |
|
4891 if (values2 == NULL) { |
|
4892 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4893 xmlFree(hashs1); |
|
4894 xmlFree(values1); |
|
4895 return(0); |
|
4896 } |
|
4897 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int)); |
|
4898 if (hashs2 == NULL) { |
|
4899 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4900 xmlFree(hashs1); |
|
4901 xmlFree(values1); |
|
4902 xmlFree(values2); |
|
4903 return(0); |
|
4904 } |
|
4905 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *)); |
|
4906 for (i = 0;i < ns1->nodeNr;i++) { |
|
4907 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]); |
|
4908 for (j = 0;j < ns2->nodeNr;j++) { |
|
4909 if (i == 0) |
|
4910 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]); |
|
4911 if (hashs1[i] != hashs2[j]) { |
|
4912 if (neq) { |
|
4913 ret = 1; |
|
4914 break; |
|
4915 } |
|
4916 } |
|
4917 else { |
|
4918 if (values1[i] == NULL) |
|
4919 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]); |
|
4920 if (values2[j] == NULL) |
|
4921 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]); |
|
4922 ret = xmlStrEqual(values1[i], values2[j]) ^ neq; |
|
4923 if (ret) |
|
4924 break; |
|
4925 } |
|
4926 } |
|
4927 if (ret) |
|
4928 break; |
|
4929 } |
|
4930 for (i = 0;i < ns1->nodeNr;i++) |
|
4931 if (values1[i] != NULL) |
|
4932 xmlFree(values1[i]); |
|
4933 for (j = 0;j < ns2->nodeNr;j++) |
|
4934 if (values2[j] != NULL) |
|
4935 xmlFree(values2[j]); |
|
4936 xmlFree(values1); |
|
4937 xmlFree(values2); |
|
4938 xmlFree(hashs1); |
|
4939 xmlFree(hashs2); |
|
4940 return(ret); |
|
4941 } |
|
4942 |
|
4943 static int |
|
4944 xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt, |
|
4945 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { |
|
4946 int ret = 0; |
|
4947 /* |
|
4948 *At this point we are assured neither arg1 nor arg2 |
|
4949 *is a nodeset, so we can just pick the appropriate routine. |
|
4950 */ |
|
4951 switch (arg1->type) { |
|
4952 case XPATH_UNDEFINED: |
|
4953 #ifdef DEBUG_EXPR |
|
4954 xmlGenericError(xmlGenericErrorContext, |
|
4955 "Equal: undefined\n"); |
|
4956 #endif |
|
4957 break; |
|
4958 case XPATH_BOOLEAN: |
|
4959 switch (arg2->type) { |
|
4960 case XPATH_UNDEFINED: |
|
4961 #ifdef DEBUG_EXPR |
|
4962 xmlGenericError(xmlGenericErrorContext, |
|
4963 "Equal: undefined\n"); |
|
4964 #endif |
|
4965 break; |
|
4966 case XPATH_BOOLEAN: |
|
4967 #ifdef DEBUG_EXPR |
|
4968 xmlGenericError(xmlGenericErrorContext, |
|
4969 "Equal: %d boolean %d \n", |
|
4970 arg1->boolval, arg2->boolval); |
|
4971 #endif |
|
4972 ret = (arg1->boolval == arg2->boolval); |
|
4973 break; |
|
4974 case XPATH_NUMBER: |
|
4975 ret = (arg1->boolval == |
|
4976 xmlXPathCastNumberToBoolean(arg2->floatval)); |
|
4977 break; |
|
4978 case XPATH_STRING: |
|
4979 if ((arg2->stringval == NULL) || |
|
4980 (arg2->stringval[0] == 0)) ret = 0; |
|
4981 else |
|
4982 ret = 1; |
|
4983 ret = (arg1->boolval == ret); |
|
4984 break; |
|
4985 case XPATH_USERS: |
|
4986 case XPATH_POINT: |
|
4987 case XPATH_RANGE: |
|
4988 case XPATH_LOCATIONSET: |
|
4989 TODO |
|
4990 break; |
|
4991 case XPATH_NODESET: |
|
4992 case XPATH_XSLT_TREE: |
|
4993 break; |
|
4994 } |
|
4995 break; |
|
4996 case XPATH_NUMBER: |
|
4997 switch (arg2->type) { |
|
4998 case XPATH_UNDEFINED: |
|
4999 #ifdef DEBUG_EXPR |
|
5000 xmlGenericError(xmlGenericErrorContext, |
|
5001 "Equal: undefined\n"); |
|
5002 #endif |
|
5003 break; |
|
5004 case XPATH_BOOLEAN: |
|
5005 ret = (arg2->boolval== |
|
5006 xmlXPathCastNumberToBoolean(arg1->floatval)); |
|
5007 break; |
|
5008 case XPATH_STRING: |
|
5009 valuePush(ctxt, arg2); |
|
5010 xmlXPathNumberFunction(ctxt, 1); |
|
5011 arg2 = valuePop(ctxt); |
|
5012 /* no break on purpose */ |
|
5013 case XPATH_NUMBER: |
|
5014 /* Hand check NaN and Infinity equalities */ |
|
5015 if (xmlXPathIsNaN(arg1->floatval) || |
|
5016 xmlXPathIsNaN(arg2->floatval)) { |
|
5017 ret = 0; |
|
5018 } else if (xmlXPathIsInf(arg1->floatval) == 1) { |
|
5019 if (xmlXPathIsInf(arg2->floatval) == 1) |
|
5020 ret = 1; |
|
5021 else |
|
5022 ret = 0; |
|
5023 } else if (xmlXPathIsInf(arg1->floatval) == -1) { |
|
5024 if (xmlXPathIsInf(arg2->floatval) == -1) |
|
5025 ret = 1; |
|
5026 else |
|
5027 ret = 0; |
|
5028 } else if (xmlXPathIsInf(arg2->floatval) == 1) { |
|
5029 if (xmlXPathIsInf(arg1->floatval) == 1) |
|
5030 ret = 1; |
|
5031 else |
|
5032 ret = 0; |
|
5033 } else if (xmlXPathIsInf(arg2->floatval) == -1) { |
|
5034 if (xmlXPathIsInf(arg1->floatval) == -1) |
|
5035 ret = 1; |
|
5036 else |
|
5037 ret = 0; |
|
5038 } else { |
|
5039 ret = (arg1->floatval == arg2->floatval); |
|
5040 } |
|
5041 break; |
|
5042 case XPATH_USERS: |
|
5043 case XPATH_POINT: |
|
5044 case XPATH_RANGE: |
|
5045 case XPATH_LOCATIONSET: |
|
5046 TODO |
|
5047 break; |
|
5048 case XPATH_NODESET: |
|
5049 case XPATH_XSLT_TREE: |
|
5050 break; |
|
5051 } |
|
5052 break; |
|
5053 case XPATH_STRING: |
|
5054 switch (arg2->type) { |
|
5055 case XPATH_UNDEFINED: |
|
5056 #ifdef DEBUG_EXPR |
|
5057 xmlGenericError(xmlGenericErrorContext, |
|
5058 "Equal: undefined\n"); |
|
5059 #endif |
|
5060 break; |
|
5061 case XPATH_BOOLEAN: |
|
5062 if ((arg1->stringval == NULL) || |
|
5063 (arg1->stringval[0] == 0)) ret = 0; |
|
5064 else |
|
5065 ret = 1; |
|
5066 ret = (arg2->boolval == ret); |
|
5067 break; |
|
5068 case XPATH_STRING: |
|
5069 ret = xmlStrEqual(arg1->stringval, arg2->stringval); |
|
5070 break; |
|
5071 case XPATH_NUMBER: |
|
5072 valuePush(ctxt, arg1); |
|
5073 xmlXPathNumberFunction(ctxt, 1); |
|
5074 arg1 = valuePop(ctxt); |
|
5075 /* Hand check NaN and Infinity equalities */ |
|
5076 if (xmlXPathIsNaN(arg1->floatval) || |
|
5077 xmlXPathIsNaN(arg2->floatval)) { |
|
5078 ret = 0; |
|
5079 } else if (xmlXPathIsInf(arg1->floatval) == 1) { |
|
5080 if (xmlXPathIsInf(arg2->floatval) == 1) |
|
5081 ret = 1; |
|
5082 else |
|
5083 ret = 0; |
|
5084 } else if (xmlXPathIsInf(arg1->floatval) == -1) { |
|
5085 if (xmlXPathIsInf(arg2->floatval) == -1) |
|
5086 ret = 1; |
|
5087 else |
|
5088 ret = 0; |
|
5089 } else if (xmlXPathIsInf(arg2->floatval) == 1) { |
|
5090 if (xmlXPathIsInf(arg1->floatval) == 1) |
|
5091 ret = 1; |
|
5092 else |
|
5093 ret = 0; |
|
5094 } else if (xmlXPathIsInf(arg2->floatval) == -1) { |
|
5095 if (xmlXPathIsInf(arg1->floatval) == -1) |
|
5096 ret = 1; |
|
5097 else |
|
5098 ret = 0; |
|
5099 } else { |
|
5100 ret = (arg1->floatval == arg2->floatval); |
|
5101 } |
|
5102 break; |
|
5103 case XPATH_USERS: |
|
5104 case XPATH_POINT: |
|
5105 case XPATH_RANGE: |
|
5106 case XPATH_LOCATIONSET: |
|
5107 TODO |
|
5108 break; |
|
5109 case XPATH_NODESET: |
|
5110 case XPATH_XSLT_TREE: |
|
5111 break; |
|
5112 } |
|
5113 break; |
|
5114 case XPATH_USERS: |
|
5115 case XPATH_POINT: |
|
5116 case XPATH_RANGE: |
|
5117 case XPATH_LOCATIONSET: |
|
5118 TODO |
|
5119 break; |
|
5120 case XPATH_NODESET: |
|
5121 case XPATH_XSLT_TREE: |
|
5122 break; |
|
5123 } |
|
5124 xmlXPathFreeObject(arg1); |
|
5125 xmlXPathFreeObject(arg2); |
|
5126 return(ret); |
|
5127 } |
|
5128 |
|
5129 /** |
|
5130 * xmlXPathEqualValues: |
|
5131 * @param ctxt the XPath Parser context |
|
5132 * |
|
5133 * Implement the equal operation on XPath objects content: arg1 == arg2 |
|
5134 * |
|
5135 * Returns 0 or 1 depending on the results of the test. |
|
5136 */ |
|
5137 XMLPUBFUNEXPORT int |
|
5138 xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { |
|
5139 xmlXPathObjectPtr arg1, arg2, argtmp; |
|
5140 int ret = 0; |
|
5141 |
|
5142 arg2 = valuePop(ctxt); |
|
5143 arg1 = valuePop(ctxt); |
|
5144 if ((arg1 == NULL) || (arg2 == NULL)) { |
|
5145 if (arg1 != NULL) |
|
5146 xmlXPathFreeObject(arg1); |
|
5147 else |
|
5148 xmlXPathFreeObject(arg2); |
|
5149 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5150 } |
|
5151 |
|
5152 if (arg1 == arg2) { |
|
5153 #ifdef DEBUG_EXPR |
|
5154 xmlGenericError(xmlGenericErrorContext, |
|
5155 "Equal: by pointer\n"); |
|
5156 #endif |
|
5157 return(1); |
|
5158 } |
|
5159 |
|
5160 /* |
|
5161 *If either argument is a nodeset, it's a 'special case' |
|
5162 */ |
|
5163 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || |
|
5164 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5165 /* |
|
5166 * |
|
5167 */ |
|
5168 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) { |
|
5169 argtmp = arg2; |
|
5170 arg2 = arg1; |
|
5171 arg1 = argtmp; |
|
5172 } |
|
5173 switch (arg2->type) { |
|
5174 case XPATH_UNDEFINED: |
|
5175 #ifdef DEBUG_EXPR |
|
5176 xmlGenericError(xmlGenericErrorContext, |
|
5177 "Equal: undefined\n"); |
|
5178 #endif |
|
5179 break; |
|
5180 case XPATH_NODESET: |
|
5181 case XPATH_XSLT_TREE: |
|
5182 ret = xmlXPathEqualNodeSets(arg1, arg2, 0); |
|
5183 break; |
|
5184 case XPATH_BOOLEAN: |
|
5185 if ((arg1->nodesetval == NULL) || |
|
5186 (arg1->nodesetval->nodeNr == 0)) ret = 0; |
|
5187 else |
|
5188 ret = 1; |
|
5189 ret = (ret == arg2->boolval); |
|
5190 break; |
|
5191 case XPATH_NUMBER: |
|
5192 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0); |
|
5193 break; |
|
5194 case XPATH_STRING: |
|
5195 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0); |
|
5196 break; |
|
5197 case XPATH_USERS: |
|
5198 case XPATH_POINT: |
|
5199 case XPATH_RANGE: |
|
5200 case XPATH_LOCATIONSET: |
|
5201 TODO |
|
5202 break; |
|
5203 } |
|
5204 xmlXPathFreeObject(arg1); |
|
5205 xmlXPathFreeObject(arg2); |
|
5206 return(ret); |
|
5207 } |
|
5208 |
|
5209 return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2)); |
|
5210 } |
|
5211 |
|
5212 /** |
|
5213 * xmlXPathNotEqualValues: |
|
5214 * @param ctxt the XPath Parser context |
|
5215 * |
|
5216 * Implement the equal operation on XPath objects content: arg1 == arg2 |
|
5217 * |
|
5218 * Returns 0 or 1 depending on the results of the test. |
|
5219 */ |
|
5220 XMLPUBFUNEXPORT int |
|
5221 xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) { |
|
5222 xmlXPathObjectPtr arg1, arg2, argtmp; |
|
5223 int ret = 0; |
|
5224 |
|
5225 arg2 = valuePop(ctxt); |
|
5226 arg1 = valuePop(ctxt); |
|
5227 if ((arg1 == NULL) || (arg2 == NULL)) { |
|
5228 if (arg1 != NULL) |
|
5229 xmlXPathFreeObject(arg1); |
|
5230 else |
|
5231 xmlXPathFreeObject(arg2); |
|
5232 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5233 } |
|
5234 |
|
5235 if (arg1 == arg2) { |
|
5236 #ifdef DEBUG_EXPR |
|
5237 xmlGenericError(xmlGenericErrorContext, |
|
5238 "NotEqual: by pointer\n"); |
|
5239 #endif |
|
5240 return(0); |
|
5241 } |
|
5242 |
|
5243 /* |
|
5244 *If either argument is a nodeset, it's a 'special case' |
|
5245 */ |
|
5246 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || |
|
5247 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5248 /* |
|
5249 * |
|
5250 */ |
|
5251 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) { |
|
5252 argtmp = arg2; |
|
5253 arg2 = arg1; |
|
5254 arg1 = argtmp; |
|
5255 } |
|
5256 switch (arg2->type) { |
|
5257 case XPATH_UNDEFINED: |
|
5258 #ifdef DEBUG_EXPR |
|
5259 xmlGenericError(xmlGenericErrorContext, |
|
5260 "NotEqual: undefined\n"); |
|
5261 #endif |
|
5262 break; |
|
5263 case XPATH_NODESET: |
|
5264 case XPATH_XSLT_TREE: |
|
5265 ret = xmlXPathEqualNodeSets(arg1, arg2, 1); |
|
5266 break; |
|
5267 case XPATH_BOOLEAN: |
|
5268 if ((arg1->nodesetval == NULL) || |
|
5269 (arg1->nodesetval->nodeNr == 0)) ret = 0; |
|
5270 else |
|
5271 ret = 1; |
|
5272 ret = (ret != arg2->boolval); |
|
5273 break; |
|
5274 case XPATH_NUMBER: |
|
5275 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1); |
|
5276 break; |
|
5277 case XPATH_STRING: |
|
5278 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1); |
|
5279 break; |
|
5280 case XPATH_USERS: |
|
5281 case XPATH_POINT: |
|
5282 case XPATH_RANGE: |
|
5283 case XPATH_LOCATIONSET: |
|
5284 TODO |
|
5285 break; |
|
5286 } |
|
5287 xmlXPathFreeObject(arg1); |
|
5288 xmlXPathFreeObject(arg2); |
|
5289 return(ret); |
|
5290 } |
|
5291 |
|
5292 return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2)); |
|
5293 } |
|
5294 |
|
5295 /** |
|
5296 * xmlXPathCompareValues: |
|
5297 * @param ctxt the XPath Parser context |
|
5298 * @param inf less than (1) or greater than (0) |
|
5299 * @param strict is the comparison strict |
|
5300 * |
|
5301 * Implement the compare operation on XPath objects: |
|
5302 * arg1 < arg2 (1, 1, ... |
|
5303 * arg1 <= arg2 (1, 0, ... |
|
5304 * arg1 > arg2 (0, 1, ... |
|
5305 * arg1 >= arg2 (0, 0, ... |
|
5306 * |
|
5307 * When neither object to be compared is a node-set and the operator is |
|
5308 * <=, <, >=, >, then the objects are compared by converted both objects |
|
5309 * to numbers and comparing the numbers according to IEEE 754. The < |
|
5310 * comparison will be true if and only if the first number is less than the |
|
5311 * second number. The <= comparison will be true if and only if the first |
|
5312 * number is less than or equal to the second number. The > comparison |
|
5313 * will be true if and only if the first number is greater than the second |
|
5314 * number. The >= comparison will be true if and only if the first number |
|
5315 * is greater than or equal to the second number. |
|
5316 * |
|
5317 * Returns 1 if the comparison succeeded, 0 if it failed |
|
5318 */ |
|
5319 XMLPUBFUNEXPORT int |
|
5320 xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { |
|
5321 int ret = 0, arg1i = 0, arg2i = 0; |
|
5322 xmlXPathObjectPtr arg1, arg2; |
|
5323 |
|
5324 arg2 = valuePop(ctxt); |
|
5325 arg1 = valuePop(ctxt); |
|
5326 if ((arg1 == NULL) || (arg2 == NULL)) { |
|
5327 if (arg1 != NULL) |
|
5328 xmlXPathFreeObject(arg1); |
|
5329 else |
|
5330 xmlXPathFreeObject(arg2); |
|
5331 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5332 } |
|
5333 |
|
5334 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || |
|
5335 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5336 if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) && |
|
5337 ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){ |
|
5338 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2); |
|
5339 } else { |
|
5340 if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5341 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict, |
|
5342 arg1, arg2); |
|
5343 } else { |
|
5344 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict, |
|
5345 arg2, arg1); |
|
5346 } |
|
5347 } |
|
5348 return(ret); |
|
5349 } |
|
5350 |
|
5351 if (arg1->type != XPATH_NUMBER) { |
|
5352 valuePush(ctxt, arg1); |
|
5353 xmlXPathNumberFunction(ctxt, 1); |
|
5354 arg1 = valuePop(ctxt); |
|
5355 } |
|
5356 if (arg1->type != XPATH_NUMBER) { |
|
5357 xmlXPathFreeObject(arg1); |
|
5358 xmlXPathFreeObject(arg2); |
|
5359 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5360 } |
|
5361 if (arg2->type != XPATH_NUMBER) { |
|
5362 valuePush(ctxt, arg2); |
|
5363 xmlXPathNumberFunction(ctxt, 1); |
|
5364 arg2 = valuePop(ctxt); |
|
5365 } |
|
5366 if (arg2->type != XPATH_NUMBER) { |
|
5367 xmlXPathFreeObject(arg1); |
|
5368 xmlXPathFreeObject(arg2); |
|
5369 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5370 } |
|
5371 /* |
|
5372 * Add tests for infinity and nan |
|
5373 * => feedback on 3.4 for Inf and NaN |
|
5374 */ |
|
5375 /* Hand check NaN and Infinity comparisons */ |
|
5376 if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) { |
|
5377 ret=0; |
|
5378 } else { |
|
5379 arg1i=xmlXPathIsInf(arg1->floatval); |
|
5380 arg2i=xmlXPathIsInf(arg2->floatval); |
|
5381 if (inf && strict) { |
|
5382 if ((arg1i == -1 && arg2i != -1) || |
|
5383 (arg2i == 1 && arg1i != 1)) { |
|
5384 ret = 1; |
|
5385 } else if (arg1i == 0 && arg2i == 0) { |
|
5386 ret = (arg1->floatval < arg2->floatval); |
|
5387 } else { |
|
5388 ret = 0; |
|
5389 } |
|
5390 } |
|
5391 else if (inf && !strict) { |
|
5392 if (arg1i == -1 || arg2i == 1) { |
|
5393 ret = 1; |
|
5394 } else if (arg1i == 0 && arg2i == 0) { |
|
5395 ret = (arg1->floatval <= arg2->floatval); |
|
5396 } else { |
|
5397 ret = 0; |
|
5398 } |
|
5399 } |
|
5400 else if (!inf && strict) { |
|
5401 if ((arg1i == 1 && arg2i != 1) || |
|
5402 (arg2i == -1 && arg1i != -1)) { |
|
5403 ret = 1; |
|
5404 } else if (arg1i == 0 && arg2i == 0) { |
|
5405 ret = (arg1->floatval > arg2->floatval); |
|
5406 } else { |
|
5407 ret = 0; |
|
5408 } |
|
5409 } |
|
5410 else if (!inf && !strict) { |
|
5411 if (arg1i == 1 || arg2i == -1) { |
|
5412 ret = 1; |
|
5413 } else if (arg1i == 0 && arg2i == 0) { |
|
5414 ret = (arg1->floatval >= arg2->floatval); |
|
5415 } else { |
|
5416 ret = 0; |
|
5417 } |
|
5418 } |
|
5419 } |
|
5420 xmlXPathFreeObject(arg1); |
|
5421 xmlXPathFreeObject(arg2); |
|
5422 return(ret); |
|
5423 } |
|
5424 |
|
5425 /** |
|
5426 * xmlXPathValueFlipSign: |
|
5427 * @param ctxt the XPath Parser context |
|
5428 * |
|
5429 * Implement the unary - operation on an XPath object |
|
5430 * The numeric operators convert their operands to numbers as if |
|
5431 * by calling the number function. |
|
5432 */ |
|
5433 XMLPUBFUNEXPORT void |
|
5434 xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) { |
|
5435 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
5436 CAST_TO_NUMBER; |
|
5437 CHECK_TYPE(XPATH_NUMBER); |
|
5438 if (xmlXPathIsNaN(ctxt->value->floatval)) |
|
5439 ctxt->value->floatval=xmlXPathNAN; |
|
5440 else if (xmlXPathIsInf(ctxt->value->floatval) == 1) |
|
5441 ctxt->value->floatval=xmlXPathNINF; |
|
5442 else if (xmlXPathIsInf(ctxt->value->floatval) == -1) |
|
5443 ctxt->value->floatval=xmlXPathPINF; |
|
5444 else if (ctxt->value->floatval == 0) { |
|
5445 if (xmlXPathGetSign(ctxt->value->floatval) == 0) |
|
5446 ctxt->value->floatval = xmlXPathNZERO; |
|
5447 else |
|
5448 ctxt->value->floatval = 0; |
|
5449 } |
|
5450 else |
|
5451 ctxt->value->floatval = - ctxt->value->floatval; |
|
5452 } |
|
5453 |
|
5454 /** |
|
5455 * xmlXPathAddValues: |
|
5456 * @param ctxt the XPath Parser context |
|
5457 * |
|
5458 * Implement the add operation on XPath objects: |
|
5459 * The numeric operators convert their operands to numbers as if |
|
5460 * by calling the number function. |
|
5461 */ |
|
5462 XMLPUBFUNEXPORT void |
|
5463 xmlXPathAddValues(xmlXPathParserContextPtr ctxt) { |
|
5464 xmlXPathObjectPtr arg; |
|
5465 double val; |
|
5466 |
|
5467 arg = valuePop(ctxt); |
|
5468 if (arg == NULL) |
|
5469 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5470 val = xmlXPathCastToNumber(arg); |
|
5471 xmlXPathFreeObject(arg); |
|
5472 |
|
5473 CAST_TO_NUMBER; |
|
5474 CHECK_TYPE(XPATH_NUMBER); |
|
5475 ctxt->value->floatval += val; |
|
5476 } |
|
5477 |
|
5478 /** |
|
5479 * xmlXPathSubValues: |
|
5480 * @param ctxt the XPath Parser context |
|
5481 * |
|
5482 * Implement the subtraction operation on XPath objects: |
|
5483 * The numeric operators convert their operands to numbers as if |
|
5484 * by calling the number function. |
|
5485 */ |
|
5486 XMLPUBFUNEXPORT void |
|
5487 xmlXPathSubValues(xmlXPathParserContextPtr ctxt) { |
|
5488 xmlXPathObjectPtr arg; |
|
5489 double val; |
|
5490 |
|
5491 arg = valuePop(ctxt); |
|
5492 if (arg == NULL) |
|
5493 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5494 val = xmlXPathCastToNumber(arg); |
|
5495 xmlXPathFreeObject(arg); |
|
5496 |
|
5497 CAST_TO_NUMBER; |
|
5498 CHECK_TYPE(XPATH_NUMBER); |
|
5499 ctxt->value->floatval -= val; |
|
5500 } |
|
5501 |
|
5502 /** |
|
5503 * xmlXPathMultValues: |
|
5504 * @param ctxt the XPath Parser context |
|
5505 * |
|
5506 * Implement the multiply operation on XPath objects: |
|
5507 * The numeric operators convert their operands to numbers as if |
|
5508 * by calling the number function. |
|
5509 */ |
|
5510 XMLPUBFUNEXPORT void |
|
5511 xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { |
|
5512 xmlXPathObjectPtr arg; |
|
5513 double val; |
|
5514 |
|
5515 arg = valuePop(ctxt); |
|
5516 if (arg == NULL) |
|
5517 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5518 val = xmlXPathCastToNumber(arg); |
|
5519 xmlXPathFreeObject(arg); |
|
5520 |
|
5521 CAST_TO_NUMBER; |
|
5522 CHECK_TYPE(XPATH_NUMBER); |
|
5523 ctxt->value->floatval *= val; |
|
5524 } |
|
5525 |
|
5526 /** |
|
5527 * xmlXPathDivValues: |
|
5528 * @param ctxt the XPath Parser context |
|
5529 * |
|
5530 * Implement the div operation on XPath objects arg1 / arg2 |
|
5531 * The numeric operators convert their operands to numbers as if |
|
5532 * by calling the number function. |
|
5533 */ |
|
5534 XMLPUBFUNEXPORT void |
|
5535 xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { |
|
5536 xmlXPathObjectPtr arg; |
|
5537 double val; |
|
5538 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
5539 |
|
5540 arg = valuePop(ctxt); |
|
5541 if (arg == NULL) |
|
5542 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5543 val = xmlXPathCastToNumber(arg); |
|
5544 xmlXPathFreeObject(arg); |
|
5545 |
|
5546 CAST_TO_NUMBER; |
|
5547 CHECK_TYPE(XPATH_NUMBER); |
|
5548 if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval)) |
|
5549 ctxt->value->floatval = xmlXPathNAN; |
|
5550 else if (val == 0 && xmlXPathGetSign(val) != 0) { |
|
5551 if (ctxt->value->floatval == 0) |
|
5552 ctxt->value->floatval = xmlXPathNAN; |
|
5553 else if (ctxt->value->floatval > 0) |
|
5554 ctxt->value->floatval = xmlXPathNINF; |
|
5555 else if (ctxt->value->floatval < 0) |
|
5556 ctxt->value->floatval = xmlXPathPINF; |
|
5557 } |
|
5558 else if (val == 0) { |
|
5559 if (ctxt->value->floatval == 0) |
|
5560 ctxt->value->floatval = xmlXPathNAN; |
|
5561 else if (ctxt->value->floatval > 0) |
|
5562 ctxt->value->floatval = xmlXPathPINF; |
|
5563 else if (ctxt->value->floatval < 0) |
|
5564 ctxt->value->floatval = xmlXPathNINF; |
|
5565 } else |
|
5566 ctxt->value->floatval /= val; |
|
5567 } |
|
5568 |
|
5569 /** |
|
5570 * xmlXPathModValues: |
|
5571 * @param ctxt the XPath Parser context |
|
5572 * |
|
5573 * Implement the mod operation on XPath objects: arg1 / arg2 |
|
5574 * The numeric operators convert their operands to numbers as if |
|
5575 * by calling the number function. |
|
5576 */ |
|
5577 XMLPUBFUNEXPORT void |
|
5578 xmlXPathModValues(xmlXPathParserContextPtr ctxt) { |
|
5579 xmlXPathObjectPtr arg; |
|
5580 double arg1, arg2; |
|
5581 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
5582 |
|
5583 arg = valuePop(ctxt); |
|
5584 if (arg == NULL) |
|
5585 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5586 arg2 = xmlXPathCastToNumber(arg); |
|
5587 xmlXPathFreeObject(arg); |
|
5588 |
|
5589 CAST_TO_NUMBER; |
|
5590 CHECK_TYPE(XPATH_NUMBER); |
|
5591 arg1 = ctxt->value->floatval; |
|
5592 if (arg2 == 0) |
|
5593 ctxt->value->floatval = xmlXPathNAN; |
|
5594 else { |
|
5595 ctxt->value->floatval = fmod(arg1, arg2); |
|
5596 } |
|
5597 } |
|
5598 |
|
5599 /************************************************************************ |
|
5600 * * |
|
5601 * The traversal functions * |
|
5602 * * |
|
5603 ************************************************************************/ |
|
5604 |
|
5605 /* |
|
5606 * A traversal function enumerates nodes along an axis. |
|
5607 * Initially it must be called with NULL, and it indicates |
|
5608 * termination on the axis by returning NULL. |
|
5609 */ |
|
5610 typedef xmlNodePtr (*xmlXPathTraversalFunction) |
|
5611 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur); |
|
5612 |
|
5613 /** |
|
5614 * xmlXPathNextSelf: |
|
5615 * @param ctxt the XPath Parser context |
|
5616 * @param cur the current node in the traversal |
|
5617 * |
|
5618 * Traversal function for the "self" direction |
|
5619 * The self axis contains just the context node itself |
|
5620 * |
|
5621 * Returns the next element following that axis |
|
5622 */ |
|
5623 XMLPUBFUNEXPORT xmlNodePtr |
|
5624 xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) |
|
5625 { |
|
5626 if (cur == NULL) |
|
5627 return(ctxt->context->node); |
|
5628 return(NULL); |
|
5629 } |
|
5630 |
|
5631 /** |
|
5632 * xmlXPathNextChild: |
|
5633 * @param ctxt the XPath Parser context |
|
5634 * @param cur the current node in the traversal |
|
5635 * |
|
5636 * Traversal function for the "child" direction |
|
5637 * The child axis contains the children of the context node in document order. |
|
5638 * |
|
5639 * Returns the next element following that axis |
|
5640 */ |
|
5641 XMLPUBFUNEXPORT xmlNodePtr |
|
5642 xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5643 if (cur == NULL) { |
|
5644 if (ctxt->context->node == NULL) return(NULL); |
|
5645 switch (ctxt->context->node->type) { |
|
5646 case XML_ELEMENT_NODE: |
|
5647 case XML_TEXT_NODE: |
|
5648 case XML_CDATA_SECTION_NODE: |
|
5649 case XML_ENTITY_REF_NODE: |
|
5650 case XML_ENTITY_NODE: |
|
5651 case XML_PI_NODE: |
|
5652 case XML_COMMENT_NODE: |
|
5653 case XML_NOTATION_NODE: |
|
5654 case XML_DTD_NODE: |
|
5655 return(ctxt->context->node->children); |
|
5656 case XML_DOCUMENT_NODE: |
|
5657 case XML_DOCUMENT_TYPE_NODE: |
|
5658 case XML_DOCUMENT_FRAG_NODE: |
|
5659 case XML_HTML_DOCUMENT_NODE: |
|
5660 #ifdef LIBXML_DOCB_ENABLED |
|
5661 case XML_DOCB_DOCUMENT_NODE: |
|
5662 #endif |
|
5663 return(((xmlDocPtr) ctxt->context->node)->children); |
|
5664 case XML_ELEMENT_DECL: |
|
5665 case XML_ATTRIBUTE_DECL: |
|
5666 case XML_ENTITY_DECL: |
|
5667 case XML_ATTRIBUTE_NODE: |
|
5668 case XML_NAMESPACE_DECL: |
|
5669 case XML_XINCLUDE_START: |
|
5670 case XML_XINCLUDE_END: |
|
5671 return(NULL); |
|
5672 } |
|
5673 return(NULL); |
|
5674 } |
|
5675 if ((cur->type == XML_DOCUMENT_NODE) || |
|
5676 (cur->type == XML_HTML_DOCUMENT_NODE)) |
|
5677 return(NULL); |
|
5678 return(cur->next); |
|
5679 } |
|
5680 |
|
5681 /** |
|
5682 * xmlXPathNextDescendant: |
|
5683 * @param ctxt the XPath Parser context |
|
5684 * @param cur the current node in the traversal |
|
5685 * |
|
5686 * Traversal function for the "descendant" direction |
|
5687 * the descendant axis contains the descendants of the context node in document |
|
5688 * order; a descendant is a child or a child of a child and so on. |
|
5689 * |
|
5690 * Returns the next element following that axis |
|
5691 */ |
|
5692 XMLPUBFUNEXPORT xmlNodePtr |
|
5693 xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5694 if (cur == NULL) { |
|
5695 if (ctxt->context->node == NULL) |
|
5696 return(NULL); |
|
5697 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5698 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5699 return(NULL); |
|
5700 |
|
5701 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) |
|
5702 return(ctxt->context->doc->children); |
|
5703 return(ctxt->context->node->children); |
|
5704 } |
|
5705 |
|
5706 if (cur->children != NULL) { |
|
5707 /* |
|
5708 * Do not descend on entities declarations |
|
5709 */ |
|
5710 if (cur->children->type != XML_ENTITY_DECL) { |
|
5711 cur = cur->children; |
|
5712 /* |
|
5713 * Skip DTDs |
|
5714 */ |
|
5715 if (cur->type != XML_DTD_NODE) |
|
5716 return(cur); |
|
5717 } |
|
5718 } |
|
5719 |
|
5720 if (cur == ctxt->context->node) return(NULL); |
|
5721 |
|
5722 while (cur->next != NULL) { |
|
5723 cur = cur->next; |
|
5724 if ((cur->type != XML_ENTITY_DECL) && |
|
5725 (cur->type != XML_DTD_NODE)) |
|
5726 return(cur); |
|
5727 } |
|
5728 |
|
5729 do { |
|
5730 cur = cur->parent; |
|
5731 if (cur == NULL) return(NULL); |
|
5732 if (cur == ctxt->context->node) return(NULL); |
|
5733 if (cur->next != NULL) { |
|
5734 cur = cur->next; |
|
5735 return(cur); |
|
5736 } |
|
5737 } while (cur != NULL); |
|
5738 return(cur); |
|
5739 } |
|
5740 |
|
5741 /** |
|
5742 * xmlXPathNextDescendantOrSelf: |
|
5743 * @param ctxt the XPath Parser context |
|
5744 * @param cur the current node in the traversal |
|
5745 * |
|
5746 * Traversal function for the "descendant-or-self" direction |
|
5747 * the descendant-or-self axis contains the context node and the descendants |
|
5748 * of the context node in document order; thus the context node is the first |
|
5749 * node on the axis, and the first child of the context node is the second node |
|
5750 * on the axis |
|
5751 * |
|
5752 * Returns the next element following that axis |
|
5753 */ |
|
5754 XMLPUBFUNEXPORT xmlNodePtr |
|
5755 xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5756 if (cur == NULL) { |
|
5757 if (ctxt->context->node == NULL) |
|
5758 return(NULL); |
|
5759 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5760 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5761 return(NULL); |
|
5762 return(ctxt->context->node); |
|
5763 } |
|
5764 |
|
5765 return(xmlXPathNextDescendant(ctxt, cur)); |
|
5766 } |
|
5767 |
|
5768 /** |
|
5769 * xmlXPathNextParent: |
|
5770 * @param ctxt the XPath Parser context |
|
5771 * @param cur the current node in the traversal |
|
5772 * |
|
5773 * Traversal function for the "parent" direction |
|
5774 * The parent axis contains the parent of the context node, if there is one. |
|
5775 * |
|
5776 * Returns the next element following that axis |
|
5777 */ |
|
5778 XMLPUBFUNEXPORT xmlNodePtr |
|
5779 xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5780 /* |
|
5781 * the parent of an attribute or namespace node is the element |
|
5782 * to which the attribute or namespace node is attached |
|
5783 * Namespace handling !!! |
|
5784 */ |
|
5785 if (cur == NULL) { |
|
5786 if (ctxt->context->node == NULL) return(NULL); |
|
5787 switch (ctxt->context->node->type) { |
|
5788 case XML_ELEMENT_NODE: |
|
5789 case XML_TEXT_NODE: |
|
5790 case XML_CDATA_SECTION_NODE: |
|
5791 case XML_ENTITY_REF_NODE: |
|
5792 case XML_ENTITY_NODE: |
|
5793 case XML_PI_NODE: |
|
5794 case XML_COMMENT_NODE: |
|
5795 case XML_NOTATION_NODE: |
|
5796 case XML_DTD_NODE: |
|
5797 case XML_ELEMENT_DECL: |
|
5798 case XML_ATTRIBUTE_DECL: |
|
5799 case XML_XINCLUDE_START: |
|
5800 case XML_XINCLUDE_END: |
|
5801 case XML_ENTITY_DECL: |
|
5802 if (ctxt->context->node->parent == NULL) |
|
5803 return((xmlNodePtr) ctxt->context->doc); |
|
5804 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) && |
|
5805 ((ctxt->context->node->parent->name[0] == ' ') || |
|
5806 (xmlStrEqual(ctxt->context->node->parent->name, |
|
5807 BAD_CAST "fake node libxslt")))) |
|
5808 return(NULL); |
|
5809 return(ctxt->context->node->parent); |
|
5810 case XML_ATTRIBUTE_NODE: { |
|
5811 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; |
|
5812 |
|
5813 return(att->parent); |
|
5814 } |
|
5815 case XML_DOCUMENT_NODE: |
|
5816 case XML_DOCUMENT_TYPE_NODE: |
|
5817 case XML_DOCUMENT_FRAG_NODE: |
|
5818 case XML_HTML_DOCUMENT_NODE: |
|
5819 #ifdef LIBXML_DOCB_ENABLED |
|
5820 case XML_DOCB_DOCUMENT_NODE: |
|
5821 #endif |
|
5822 return(NULL); |
|
5823 case XML_NAMESPACE_DECL: { |
|
5824 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; |
|
5825 |
|
5826 if ((ns->next != NULL) && |
|
5827 (ns->next->type != XML_NAMESPACE_DECL)) |
|
5828 return((xmlNodePtr) ns->next); |
|
5829 return(NULL); |
|
5830 } |
|
5831 } |
|
5832 } |
|
5833 return(NULL); |
|
5834 } |
|
5835 |
|
5836 /** |
|
5837 * xmlXPathNextAncestor: |
|
5838 * @param ctxt the XPath Parser context |
|
5839 * @param cur the current node in the traversal |
|
5840 * |
|
5841 * Traversal function for the "ancestor" direction |
|
5842 * the ancestor axis contains the ancestors of the context node; the ancestors |
|
5843 * of the context node consist of the parent of context node and the parent's |
|
5844 * parent and so on; the nodes are ordered in reverse document order; thus the |
|
5845 * parent is the first node on the axis, and the parent's parent is the second |
|
5846 * node on the axis |
|
5847 * |
|
5848 * Returns the next element following that axis |
|
5849 */ |
|
5850 XMLPUBFUNEXPORT xmlNodePtr |
|
5851 xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5852 /* |
|
5853 * the parent of an attribute or namespace node is the element |
|
5854 * to which the attribute or namespace node is attached |
|
5855 * !!!!!!!!!!!!! |
|
5856 */ |
|
5857 if (cur == NULL) { |
|
5858 if (ctxt->context->node == NULL) return(NULL); |
|
5859 switch (ctxt->context->node->type) { |
|
5860 case XML_ELEMENT_NODE: |
|
5861 case XML_TEXT_NODE: |
|
5862 case XML_CDATA_SECTION_NODE: |
|
5863 case XML_ENTITY_REF_NODE: |
|
5864 case XML_ENTITY_NODE: |
|
5865 case XML_PI_NODE: |
|
5866 case XML_COMMENT_NODE: |
|
5867 case XML_DTD_NODE: |
|
5868 case XML_ELEMENT_DECL: |
|
5869 case XML_ATTRIBUTE_DECL: |
|
5870 case XML_ENTITY_DECL: |
|
5871 case XML_NOTATION_NODE: |
|
5872 case XML_XINCLUDE_START: |
|
5873 case XML_XINCLUDE_END: |
|
5874 if (ctxt->context->node->parent == NULL) |
|
5875 return((xmlNodePtr) ctxt->context->doc); |
|
5876 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) && |
|
5877 ((ctxt->context->node->parent->name[0] == ' ') || |
|
5878 (xmlStrEqual(ctxt->context->node->parent->name, |
|
5879 BAD_CAST "fake node libxslt")))) |
|
5880 return(NULL); |
|
5881 return(ctxt->context->node->parent); |
|
5882 case XML_ATTRIBUTE_NODE: { |
|
5883 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node; |
|
5884 |
|
5885 return(tmp->parent); |
|
5886 } |
|
5887 case XML_DOCUMENT_NODE: |
|
5888 case XML_DOCUMENT_TYPE_NODE: |
|
5889 case XML_DOCUMENT_FRAG_NODE: |
|
5890 case XML_HTML_DOCUMENT_NODE: |
|
5891 #ifdef LIBXML_DOCB_ENABLED |
|
5892 case XML_DOCB_DOCUMENT_NODE: |
|
5893 #endif |
|
5894 return(NULL); |
|
5895 case XML_NAMESPACE_DECL: { |
|
5896 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; |
|
5897 |
|
5898 if ((ns->next != NULL) && |
|
5899 (ns->next->type != XML_NAMESPACE_DECL)) |
|
5900 return((xmlNodePtr) ns->next); |
|
5901 /* Bad, how did that namespace end up here ? */ |
|
5902 return(NULL); |
|
5903 } |
|
5904 } |
|
5905 return(NULL); |
|
5906 } |
|
5907 if (cur == ctxt->context->doc->children) |
|
5908 return((xmlNodePtr) ctxt->context->doc); |
|
5909 if (cur == (xmlNodePtr) ctxt->context->doc) |
|
5910 return(NULL); |
|
5911 switch (cur->type) { |
|
5912 case XML_ELEMENT_NODE: |
|
5913 case XML_TEXT_NODE: |
|
5914 case XML_CDATA_SECTION_NODE: |
|
5915 case XML_ENTITY_REF_NODE: |
|
5916 case XML_ENTITY_NODE: |
|
5917 case XML_PI_NODE: |
|
5918 case XML_COMMENT_NODE: |
|
5919 case XML_NOTATION_NODE: |
|
5920 case XML_DTD_NODE: |
|
5921 case XML_ELEMENT_DECL: |
|
5922 case XML_ATTRIBUTE_DECL: |
|
5923 case XML_ENTITY_DECL: |
|
5924 case XML_XINCLUDE_START: |
|
5925 case XML_XINCLUDE_END: |
|
5926 if (cur->parent == NULL) |
|
5927 return(NULL); |
|
5928 if ((cur->parent->type == XML_ELEMENT_NODE) && |
|
5929 ((cur->parent->name[0] == ' ') || |
|
5930 (xmlStrEqual(cur->parent->name, |
|
5931 BAD_CAST "fake node libxslt")))) |
|
5932 return(NULL); |
|
5933 return(cur->parent); |
|
5934 case XML_ATTRIBUTE_NODE: { |
|
5935 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; |
|
5936 |
|
5937 return(att->parent); |
|
5938 } |
|
5939 case XML_NAMESPACE_DECL: { |
|
5940 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; |
|
5941 |
|
5942 if ((ns->next != NULL) && |
|
5943 (ns->next->type != XML_NAMESPACE_DECL)) |
|
5944 return((xmlNodePtr) ns->next); |
|
5945 /* Bad, how did that namespace end up here ? */ |
|
5946 return(NULL); |
|
5947 } |
|
5948 case XML_DOCUMENT_NODE: |
|
5949 case XML_DOCUMENT_TYPE_NODE: |
|
5950 case XML_DOCUMENT_FRAG_NODE: |
|
5951 case XML_HTML_DOCUMENT_NODE: |
|
5952 #ifdef LIBXML_DOCB_ENABLED |
|
5953 case XML_DOCB_DOCUMENT_NODE: |
|
5954 #endif |
|
5955 return(NULL); |
|
5956 } |
|
5957 return(NULL); |
|
5958 } |
|
5959 |
|
5960 /** |
|
5961 * xmlXPathNextAncestorOrSelf: |
|
5962 * @param ctxt the XPath Parser context |
|
5963 * @param cur the current node in the traversal |
|
5964 * |
|
5965 * Traversal function for the "ancestor-or-self" direction |
|
5966 * he ancestor-or-self axis contains the context node and ancestors of |
|
5967 * the context node in reverse document order; thus the context node is |
|
5968 * the first node on the axis, and the context node's parent the second; |
|
5969 * parent here is defined the same as with the parent axis. |
|
5970 * |
|
5971 * Returns the next element following that axis |
|
5972 */ |
|
5973 XMLPUBFUNEXPORT xmlNodePtr |
|
5974 xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5975 if (cur == NULL) |
|
5976 return(ctxt->context->node); |
|
5977 return(xmlXPathNextAncestor(ctxt, cur)); |
|
5978 } |
|
5979 |
|
5980 /** |
|
5981 * xmlXPathNextFollowingSibling: |
|
5982 * @param ctxt the XPath Parser context |
|
5983 * @param cur the current node in the traversal |
|
5984 * |
|
5985 * Traversal function for the "following-sibling" direction |
|
5986 * The following-sibling axis contains the following siblings of the context |
|
5987 * node in document order. |
|
5988 * |
|
5989 * Returns the next element following that axis |
|
5990 */ |
|
5991 XMLPUBFUNEXPORT xmlNodePtr |
|
5992 xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5993 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5994 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5995 return(NULL); |
|
5996 if (cur == (xmlNodePtr) ctxt->context->doc) |
|
5997 return(NULL); |
|
5998 if (cur == NULL) |
|
5999 return(ctxt->context->node->next); |
|
6000 return(cur->next); |
|
6001 } |
|
6002 |
|
6003 /** |
|
6004 * xmlXPathNextPrecedingSibling: |
|
6005 * @param ctxt the XPath Parser context |
|
6006 * @param cur the current node in the traversal |
|
6007 * |
|
6008 * Traversal function for the "preceding-sibling" direction |
|
6009 * The preceding-sibling axis contains the preceding siblings of the context |
|
6010 * node in reverse document order; the first preceding sibling is first on the |
|
6011 * axis; the sibling preceding that node is the second on the axis and so on. |
|
6012 * |
|
6013 * Returns the next element following that axis |
|
6014 */ |
|
6015 XMLPUBFUNEXPORT xmlNodePtr |
|
6016 xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
6017 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
6018 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
6019 return(NULL); |
|
6020 if (cur == (xmlNodePtr) ctxt->context->doc) |
|
6021 return(NULL); |
|
6022 if (cur == NULL) |
|
6023 return(ctxt->context->node->prev); |
|
6024 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) { |
|
6025 cur = cur->prev; |
|
6026 if (cur == NULL) |
|
6027 return(ctxt->context->node->prev); |
|
6028 } |
|
6029 return(cur->prev); |
|
6030 } |
|
6031 |
|
6032 /** |
|
6033 * xmlXPathNextFollowing: |
|
6034 * @param ctxt the XPath Parser context |
|
6035 * @param cur the current node in the traversal |
|
6036 * |
|
6037 * Traversal function for the "following" direction |
|
6038 * The following axis contains all nodes in the same document as the context |
|
6039 * node that are after the context node in document order, excluding any |
|
6040 * descendants and excluding attribute nodes and namespace nodes; the nodes |
|
6041 * are ordered in document order |
|
6042 * |
|
6043 * Returns the next element following that axis |
|
6044 */ |
|
6045 XMLPUBFUNEXPORT xmlNodePtr |
|
6046 xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
6047 if (cur != NULL && cur->children != NULL) |
|
6048 return cur->children ; |
|
6049 if (cur == NULL) cur = ctxt->context->node; |
|
6050 if (cur == NULL) return(NULL) ; /* ERROR */ |
|
6051 if (cur->next != NULL) return(cur->next) ; |
|
6052 do { |
|
6053 cur = cur->parent; |
|
6054 if (cur == NULL) return(NULL); |
|
6055 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); |
|
6056 if (cur->next != NULL) return(cur->next); |
|
6057 } while (cur != NULL); |
|
6058 return(cur); |
|
6059 } |
|
6060 |
|
6061 /* |
|
6062 * xmlXPathIsAncestor: |
|
6063 * @param ancestor the ancestor node |
|
6064 * @param node the current node |
|
6065 * |
|
6066 * Check that ancestor is a node's ancestor |
|
6067 * |
|
6068 * returns 1 if ancestor is a node's ancestor, 0 otherwise. |
|
6069 */ |
|
6070 static int |
|
6071 xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) { |
|
6072 if ((ancestor == NULL) || (node == NULL)) return(0); |
|
6073 /* nodes need to be in the same document */ |
|
6074 if (ancestor->doc != node->doc) return(0); |
|
6075 /* avoid searching if ancestor or node is the root node */ |
|
6076 if (ancestor == (xmlNodePtr) node->doc) return(1); |
|
6077 if (node == (xmlNodePtr) ancestor->doc) return(0); |
|
6078 while (node->parent != NULL) { |
|
6079 if (node->parent == ancestor) |
|
6080 return(1); |
|
6081 node = node->parent; |
|
6082 } |
|
6083 return(0); |
|
6084 } |
|
6085 |
|
6086 /** |
|
6087 * xmlXPathNextPreceding: |
|
6088 * @param ctxt the XPath Parser context |
|
6089 * @param cur the current node in the traversal |
|
6090 * |
|
6091 * Traversal function for the "preceding" direction |
|
6092 * the preceding axis contains all nodes in the same document as the context |
|
6093 * node that are before the context node in document order, excluding any |
|
6094 * ancestors and excluding attribute nodes and namespace nodes; the nodes are |
|
6095 * ordered in reverse document order |
|
6096 * |
|
6097 * Returns the next element following that axis |
|
6098 */ |
|
6099 XMLPUBFUNEXPORT xmlNodePtr |
|
6100 xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) |
|
6101 { |
|
6102 if (cur == NULL) |
|
6103 cur = ctxt->context->node; |
|
6104 if (cur == NULL) |
|
6105 return (NULL); |
|
6106 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) |
|
6107 cur = cur->prev; |
|
6108 do { |
|
6109 if (cur->prev != NULL) { |
|
6110 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ; |
|
6111 return (cur); |
|
6112 } |
|
6113 |
|
6114 cur = cur->parent; |
|
6115 if (cur == NULL) |
|
6116 return (NULL); |
|
6117 if (cur == ctxt->context->doc->children) |
|
6118 return (NULL); |
|
6119 } while (xmlXPathIsAncestor(cur, ctxt->context->node)); |
|
6120 return (cur); |
|
6121 } |
|
6122 |
|
6123 /** |
|
6124 * xmlXPathNextPrecedingInternal: |
|
6125 * @param ctxt the XPath Parser context |
|
6126 * @param cur the current node in the traversal |
|
6127 * |
|
6128 * Traversal function for the "preceding" direction |
|
6129 * the preceding axis contains all nodes in the same document as the context |
|
6130 * node that are before the context node in document order, excluding any |
|
6131 * ancestors and excluding attribute nodes and namespace nodes; the nodes are |
|
6132 * ordered in reverse document order |
|
6133 * This is a faster implementation but internal only since it requires a |
|
6134 * state kept in the parser context: ctxt->ancestor. |
|
6135 * |
|
6136 * Returns the next element following that axis |
|
6137 */ |
|
6138 static xmlNodePtr |
|
6139 xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt, |
|
6140 xmlNodePtr cur) |
|
6141 { |
|
6142 if (cur == NULL) { |
|
6143 cur = ctxt->context->node; |
|
6144 if (cur == NULL) |
|
6145 return (NULL); |
|
6146 if (cur->type == XML_NAMESPACE_DECL) |
|
6147 cur = (xmlNodePtr)((xmlNsPtr)cur)->next; |
|
6148 ctxt->ancestor = cur->parent; |
|
6149 } |
|
6150 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) |
|
6151 cur = cur->prev; |
|
6152 while (cur->prev == NULL) { |
|
6153 cur = cur->parent; |
|
6154 if (cur == NULL) |
|
6155 return (NULL); |
|
6156 if (cur == ctxt->context->doc->children) |
|
6157 return (NULL); |
|
6158 if (cur != ctxt->ancestor) |
|
6159 return (cur); |
|
6160 ctxt->ancestor = cur->parent; |
|
6161 } |
|
6162 cur = cur->prev; |
|
6163 while (cur->last != NULL) |
|
6164 cur = cur->last; |
|
6165 return (cur); |
|
6166 } |
|
6167 |
|
6168 /** |
|
6169 * xmlXPathNextNamespace: |
|
6170 * @param ctxt the XPath Parser context |
|
6171 * @param cur the current attribute in the traversal |
|
6172 * |
|
6173 * Traversal function for the "namespace" direction |
|
6174 * the namespace axis contains the namespace nodes of the context node; |
|
6175 * the order of nodes on this axis is implementation-defined; the axis will |
|
6176 * be empty unless the context node is an element |
|
6177 * |
|
6178 * We keep the XML namespace node at the end of the list. |
|
6179 * |
|
6180 * Returns the next element following that axis |
|
6181 */ |
|
6182 XMLPUBFUNEXPORT xmlNodePtr |
|
6183 xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
6184 if (ctxt->context->node->type != XML_ELEMENT_NODE) |
|
6185 return(NULL); |
|
6186 if (ctxt->context->tmpNsList == NULL && |
|
6187 cur != (xmlNodePtr) xmlXPathXMLNamespace) |
|
6188 { |
|
6189 if (ctxt->context->tmpNsList != NULL) |
|
6190 xmlFree(ctxt->context->tmpNsList); |
|
6191 ctxt->context->tmpNsList = |
|
6192 xmlGetNsList(ctxt->context->doc, ctxt->context->node); |
|
6193 ctxt->context->tmpNsNr = 0; |
|
6194 if (ctxt->context->tmpNsList != NULL) { |
|
6195 while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) { |
|
6196 ctxt->context->tmpNsNr++; |
|
6197 } |
|
6198 } |
|
6199 return((xmlNodePtr) xmlXPathXMLNamespace); |
|
6200 } |
|
6201 if (ctxt->context->tmpNsNr > 0) { |
|
6202 return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr]; |
|
6203 } else { |
|
6204 if (ctxt->context->tmpNsList != NULL) |
|
6205 xmlFree(ctxt->context->tmpNsList); |
|
6206 ctxt->context->tmpNsList = NULL; |
|
6207 return(NULL); |
|
6208 } |
|
6209 } |
|
6210 |
|
6211 /** |
|
6212 * xmlXPathNextAttribute: |
|
6213 * @param ctxt the XPath Parser context |
|
6214 * @param cur the current attribute in the traversal |
|
6215 * |
|
6216 * Traversal function for the "attribute" direction |
|
6217 * |
|
6218 * |
|
6219 * Returns the next element following that axis |
|
6220 */ |
|
6221 XMLPUBFUNEXPORT xmlNodePtr |
|
6222 xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
6223 if (ctxt->context->node == NULL) |
|
6224 return(NULL); |
|
6225 if (ctxt->context->node->type != XML_ELEMENT_NODE) |
|
6226 return(NULL); |
|
6227 if (cur == NULL) { |
|
6228 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) |
|
6229 return(NULL); |
|
6230 return((xmlNodePtr)ctxt->context->node->properties); |
|
6231 } |
|
6232 return((xmlNodePtr)cur->next); |
|
6233 } |
|
6234 |
|
6235 /************************************************************************ |
|
6236 * * |
|
6237 * NodeTest Functions * |
|
6238 * * |
|
6239 ************************************************************************/ |
|
6240 |
|
6241 #define IS_FUNCTION 200 |
|
6242 |
|
6243 |
|
6244 /************************************************************************ |
|
6245 * * |
|
6246 * Implicit tree core function library * |
|
6247 * * |
|
6248 ************************************************************************/ |
|
6249 |
|
6250 /** |
|
6251 * xmlXPathRoot: |
|
6252 * @param ctxt the XPath Parser context |
|
6253 * |
|
6254 * Initialize the context to the root of the document |
|
6255 */ |
|
6256 XMLPUBFUNEXPORT void |
|
6257 xmlXPathRoot(xmlXPathParserContextPtr ctxt) { |
|
6258 ctxt->context->node = (xmlNodePtr) ctxt->context->doc; |
|
6259 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6260 } |
|
6261 |
|
6262 /************************************************************************ |
|
6263 * * |
|
6264 * The explicit core function library * |
|
6265 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib * |
|
6266 * * |
|
6267 ************************************************************************/ |
|
6268 |
|
6269 |
|
6270 /** |
|
6271 * xmlXPathLastFunction: |
|
6272 * @param ctxt the XPath Parser context |
|
6273 * @param nargs the number of arguments |
|
6274 * |
|
6275 * Implement the last() XPath function |
|
6276 * number last() |
|
6277 * The last function returns the number of nodes in the context node list. |
|
6278 */ |
|
6279 XMLPUBFUNEXPORT void |
|
6280 xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6281 CHECK_ARITY(0); |
|
6282 if (ctxt->context->contextSize >= 0) { |
|
6283 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize)); |
|
6284 #ifdef DEBUG_EXPR |
|
6285 xmlGenericError(xmlGenericErrorContext, |
|
6286 "last() : %d\n", ctxt->context->contextSize); |
|
6287 #endif |
|
6288 } else { |
|
6289 XP_ERROR(XPATH_INVALID_CTXT_SIZE); |
|
6290 } |
|
6291 } |
|
6292 |
|
6293 /** |
|
6294 * xmlXPathPositionFunction: |
|
6295 * @param ctxt the XPath Parser context |
|
6296 * @param nargs the number of arguments |
|
6297 * |
|
6298 * Implement the position() XPath function |
|
6299 * number position() |
|
6300 * The position function returns the position of the context node in the |
|
6301 * context node list. The first position is 1, and so the last position |
|
6302 * will be equal to last(). |
|
6303 */ |
|
6304 XMLPUBFUNEXPORT void |
|
6305 xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6306 CHECK_ARITY(0); |
|
6307 if (ctxt->context->proximityPosition >= 0) { |
|
6308 valuePush(ctxt, |
|
6309 xmlXPathNewFloat((double) ctxt->context->proximityPosition)); |
|
6310 #ifdef DEBUG_EXPR |
|
6311 xmlGenericError(xmlGenericErrorContext, "position() : %d\n", |
|
6312 ctxt->context->proximityPosition); |
|
6313 #endif |
|
6314 } else { |
|
6315 XP_ERROR(XPATH_INVALID_CTXT_POSITION); |
|
6316 } |
|
6317 } |
|
6318 |
|
6319 /** |
|
6320 * xmlXPathCountFunction: |
|
6321 * @param ctxt the XPath Parser context |
|
6322 * @param nargs the number of arguments |
|
6323 * |
|
6324 * Implement the count() XPath function |
|
6325 * number count(node-set) |
|
6326 */ |
|
6327 XMLPUBFUNEXPORT void |
|
6328 xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6329 xmlXPathObjectPtr cur; |
|
6330 |
|
6331 CHECK_ARITY(1); |
|
6332 if ((ctxt->value == NULL) || |
|
6333 ((ctxt->value->type != XPATH_NODESET) && |
|
6334 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6335 XP_ERROR(XPATH_INVALID_TYPE); |
|
6336 cur = valuePop(ctxt); |
|
6337 |
|
6338 if ((cur == NULL) || (cur->nodesetval == NULL)) |
|
6339 valuePush(ctxt, xmlXPathNewFloat((double) 0)); |
|
6340 else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) { |
|
6341 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr)); |
|
6342 } else { |
|
6343 if ((cur->nodesetval->nodeNr != 1) || |
|
6344 (cur->nodesetval->nodeTab == NULL)) { |
|
6345 valuePush(ctxt, xmlXPathNewFloat((double) 0)); |
|
6346 } else { |
|
6347 xmlNodePtr tmp; |
|
6348 int i = 0; |
|
6349 |
|
6350 tmp = cur->nodesetval->nodeTab[0]; |
|
6351 if (tmp != NULL) { |
|
6352 tmp = tmp->children; |
|
6353 while (tmp != NULL) { |
|
6354 tmp = tmp->next; |
|
6355 i++; |
|
6356 } |
|
6357 } |
|
6358 valuePush(ctxt, xmlXPathNewFloat((double) i)); |
|
6359 } |
|
6360 } |
|
6361 xmlXPathFreeObject(cur); |
|
6362 } |
|
6363 |
|
6364 /** |
|
6365 * xmlXPathGetElementsByIds: |
|
6366 * @param doc the document |
|
6367 * @param ids a whitespace separated list of IDs |
|
6368 * |
|
6369 * Selects elements by their unique ID. |
|
6370 * |
|
6371 * Returns a node-set of selected elements. |
|
6372 */ |
|
6373 static xmlNodeSetPtr |
|
6374 xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar* ids) |
|
6375 { |
|
6376 xmlNodeSetPtr ret; |
|
6377 const xmlChar* cur = ids; |
|
6378 xmlChar *ID; |
|
6379 xmlAttrPtr attr; |
|
6380 xmlNodePtr elem;// Unneeded initialization: // = NULL; |
|
6381 |
|
6382 if (!ids) |
|
6383 return(NULL); |
|
6384 |
|
6385 ret = xmlXPathNodeSetCreate(NULL); |
|
6386 |
|
6387 if ( !ret ) |
|
6388 return NULL; |
|
6389 |
|
6390 while (IS_BLANK_CH(*cur)) |
|
6391 cur++; |
|
6392 |
|
6393 while (*cur != 0) |
|
6394 { |
|
6395 while ((!IS_BLANK_CH(*cur)) && (*cur != 0)) |
|
6396 cur++; |
|
6397 |
|
6398 ID = xmlStrndup(ids, cur - ids); |
|
6399 if (ID != NULL) { |
|
6400 /* |
|
6401 * We used to check the fact that the value passed |
|
6402 * was an NCName, but this generated much troubles for |
|
6403 * me and Aleksey Sanin, people blatantly violated that |
|
6404 * constaint, like Visa3D spec. |
|
6405 * if (xmlValidateNCName(ID, 1) == 0) |
|
6406 */ |
|
6407 attr = xmlGetID(doc, ID); |
|
6408 if (attr != NULL) |
|
6409 { |
|
6410 // Note: xmlGetID CAN return xmlDocPtr instead of xmlAttrPtr!!! |
|
6411 if (attr->type == XML_ATTRIBUTE_NODE) |
|
6412 elem = attr->parent; |
|
6413 else if (attr->type == XML_ELEMENT_NODE) |
|
6414 elem = (xmlNodePtr) attr; |
|
6415 else |
|
6416 elem = NULL; |
|
6417 if (elem) |
|
6418 xmlXPathNodeSetAdd(ret, elem); |
|
6419 } |
|
6420 xmlFree(ID); |
|
6421 } |
|
6422 |
|
6423 while (IS_BLANK_CH(*cur)) |
|
6424 cur++; |
|
6425 ids = cur; |
|
6426 } |
|
6427 return(ret); |
|
6428 } |
|
6429 |
|
6430 /** |
|
6431 * xmlXPathIdFunction: |
|
6432 * @param ctxt the XPath Parser context |
|
6433 * @param nargs the number of arguments |
|
6434 * |
|
6435 * Implement the id() XPath function |
|
6436 * node-set id(object) |
|
6437 * The id function selects elements by their unique ID |
|
6438 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set, |
|
6439 * then the result is the union of the result of applying id to the |
|
6440 * string value of each of the nodes in the argument node-set. When the |
|
6441 * argument to id is of any other type, the argument is converted to a |
|
6442 * string as if by a call to the string function; the string is split |
|
6443 * into a whitespace-separated list of tokens (whitespace is any sequence |
|
6444 * of characters matching the production S); the result is a node-set |
|
6445 * containing the elements in the same document as the context node that |
|
6446 * have a unique ID equal to any of the tokens in the list. |
|
6447 */ |
|
6448 XMLPUBFUNEXPORT void |
|
6449 xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6450 xmlChar *tokens; |
|
6451 xmlNodeSetPtr ret; |
|
6452 xmlXPathObjectPtr obj; |
|
6453 xmlXPathObjectPtr wrappedNodeSet; |
|
6454 |
|
6455 CHECK_ARITY(1); |
|
6456 obj = valuePop(ctxt); |
|
6457 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND); |
|
6458 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) { |
|
6459 xmlNodeSetPtr ns; |
|
6460 int i; |
|
6461 |
|
6462 ret = xmlXPathNodeSetCreate(NULL); |
|
6463 |
|
6464 if (obj->nodesetval != NULL) { |
|
6465 for (i = 0; i < obj->nodesetval->nodeNr; i++) { |
|
6466 tokens = |
|
6467 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); |
|
6468 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens); |
|
6469 ret = xmlXPathNodeSetMerge(ret, ns); |
|
6470 xmlXPathFreeNodeSet(ns); |
|
6471 if (tokens != NULL) |
|
6472 xmlFree(tokens); |
|
6473 } |
|
6474 } |
|
6475 |
|
6476 xmlXPathFreeObject(obj); |
|
6477 valuePush(ctxt, xmlXPathWrapNodeSet(ret)); |
|
6478 return; |
|
6479 } |
|
6480 obj = xmlXPathConvertString(obj); |
|
6481 |
|
6482 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval); |
|
6483 wrappedNodeSet = xmlXPathWrapNodeSet(ret); |
|
6484 if ( !wrappedNodeSet ) |
|
6485 { |
|
6486 xmlXPathFreeNodeSet( ret ); |
|
6487 } |
|
6488 valuePush(ctxt, wrappedNodeSet); |
|
6489 |
|
6490 xmlXPathFreeObject(obj); |
|
6491 return; |
|
6492 } |
|
6493 |
|
6494 /** |
|
6495 * xmlXPathLocalNameFunction: |
|
6496 * @param ctxt the XPath Parser context |
|
6497 * @param nargs the number of arguments |
|
6498 * |
|
6499 * Implement the local-name() XPath function |
|
6500 * string local-name(node-set?) |
|
6501 * The local-name function returns a string containing the local part |
|
6502 * of the name of the node in the argument node-set that is first in |
|
6503 * document order. If the node-set is empty or the first node has no |
|
6504 * name, an empty string is returned. If the argument is omitted it |
|
6505 * defaults to the context node. |
|
6506 */ |
|
6507 XMLPUBFUNEXPORT void |
|
6508 xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6509 xmlXPathObjectPtr cur; |
|
6510 |
|
6511 if (nargs == 0) { |
|
6512 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6513 nargs = 1; |
|
6514 } |
|
6515 |
|
6516 CHECK_ARITY(1); |
|
6517 if ((ctxt->value == NULL) || |
|
6518 ((ctxt->value->type != XPATH_NODESET) && |
|
6519 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6520 XP_ERROR(XPATH_INVALID_TYPE); |
|
6521 cur = valuePop(ctxt); |
|
6522 |
|
6523 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { |
|
6524 valuePush(ctxt, xmlXPathNewCString("")); |
|
6525 } else { |
|
6526 int i = 0; /* Should be first in document order !!!!! */ |
|
6527 switch (cur->nodesetval->nodeTab[i]->type) { |
|
6528 case XML_ELEMENT_NODE: |
|
6529 case XML_ATTRIBUTE_NODE: |
|
6530 case XML_PI_NODE: |
|
6531 if (cur->nodesetval->nodeTab[i]->name[0] == ' ') |
|
6532 valuePush(ctxt, xmlXPathNewCString("")); |
|
6533 else |
|
6534 valuePush(ctxt, |
|
6535 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); |
|
6536 break; |
|
6537 case XML_NAMESPACE_DECL: |
|
6538 valuePush(ctxt, xmlXPathNewString( |
|
6539 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix)); |
|
6540 break; |
|
6541 default: |
|
6542 valuePush(ctxt, xmlXPathNewCString("")); |
|
6543 } |
|
6544 } |
|
6545 xmlXPathFreeObject(cur); |
|
6546 } |
|
6547 |
|
6548 /** |
|
6549 * xmlXPathNamespaceURIFunction: |
|
6550 * @param ctxt the XPath Parser context |
|
6551 * @param nargs the number of arguments |
|
6552 * |
|
6553 * Implement the namespace-uri() XPath function |
|
6554 * string namespace-uri(node-set?) |
|
6555 * The namespace-uri function returns a string containing the |
|
6556 * namespace URI of the expanded name of the node in the argument |
|
6557 * node-set that is first in document order. If the node-set is empty, |
|
6558 * the first node has no name, or the expanded name has no namespace |
|
6559 * URI, an empty string is returned. If the argument is omitted it |
|
6560 * defaults to the context node. |
|
6561 */ |
|
6562 XMLPUBFUNEXPORT void |
|
6563 xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6564 xmlXPathObjectPtr cur; |
|
6565 |
|
6566 if (nargs == 0) { |
|
6567 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6568 nargs = 1; |
|
6569 } |
|
6570 CHECK_ARITY(1); |
|
6571 if ((ctxt->value == NULL) || |
|
6572 ((ctxt->value->type != XPATH_NODESET) && |
|
6573 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6574 XP_ERROR(XPATH_INVALID_TYPE); |
|
6575 cur = valuePop(ctxt); |
|
6576 |
|
6577 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { |
|
6578 valuePush(ctxt, xmlXPathNewCString("")); |
|
6579 } else { |
|
6580 int i = 0; /* Should be first in document order !!!!! */ |
|
6581 switch (cur->nodesetval->nodeTab[i]->type) { |
|
6582 case XML_ELEMENT_NODE: |
|
6583 case XML_ATTRIBUTE_NODE: |
|
6584 if (cur->nodesetval->nodeTab[i]->ns == NULL) |
|
6585 valuePush(ctxt, xmlXPathNewCString("")); |
|
6586 else |
|
6587 valuePush(ctxt, xmlXPathNewString( |
|
6588 cur->nodesetval->nodeTab[i]->ns->href)); |
|
6589 break; |
|
6590 default: |
|
6591 valuePush(ctxt, xmlXPathNewCString("")); |
|
6592 } |
|
6593 } |
|
6594 xmlXPathFreeObject(cur); |
|
6595 } |
|
6596 |
|
6597 /** |
|
6598 * xmlXPathNameFunction: |
|
6599 * @param ctxt the XPath Parser context |
|
6600 * @param nargs the number of arguments |
|
6601 * |
|
6602 * Implement the name() XPath function |
|
6603 * string name(node-set?) |
|
6604 * The name function returns a string containing a QName representing |
|
6605 * the name of the node in the argument node-set that is first in document |
|
6606 * order. The QName must represent the name with respect to the namespace |
|
6607 * declarations in effect on the node whose name is being represented. |
|
6608 * Typically, this will be the form in which the name occurred in the XML |
|
6609 * source. This need not be the case if there are namespace declarations |
|
6610 * in effect on the node that associate multiple prefixes with the same |
|
6611 * namespace. However, an implementation may include information about |
|
6612 * the original prefix in its representation of nodes; in this case, an |
|
6613 * implementation can ensure that the returned string is always the same |
|
6614 * as the QName used in the XML source. If the argument it omitted it |
|
6615 * defaults to the context node. |
|
6616 * Libxml keep the original prefix so the "real qualified name" used is |
|
6617 * returned. |
|
6618 */ |
|
6619 static void |
|
6620 xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) |
|
6621 { |
|
6622 xmlXPathObjectPtr cur; |
|
6623 |
|
6624 if (nargs == 0) { |
|
6625 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6626 nargs = 1; |
|
6627 } |
|
6628 |
|
6629 CHECK_ARITY(1); |
|
6630 if ((ctxt->value == NULL) || |
|
6631 ((ctxt->value->type != XPATH_NODESET) && |
|
6632 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6633 XP_ERROR(XPATH_INVALID_TYPE); |
|
6634 cur = valuePop(ctxt); |
|
6635 |
|
6636 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { |
|
6637 valuePush(ctxt, xmlXPathNewCString("")); |
|
6638 } else { |
|
6639 int i = 0; /* Should be first in document order !!!!! */ |
|
6640 |
|
6641 switch (cur->nodesetval->nodeTab[i]->type) { |
|
6642 case XML_ELEMENT_NODE: |
|
6643 case XML_ATTRIBUTE_NODE: |
|
6644 if (cur->nodesetval->nodeTab[i]->name[0] == ' ') |
|
6645 valuePush(ctxt, xmlXPathNewCString("")); |
|
6646 else if ((cur->nodesetval->nodeTab[i]->ns == NULL) || |
|
6647 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) { |
|
6648 valuePush(ctxt, |
|
6649 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); |
|
6650 |
|
6651 } else { |
|
6652 xmlChar *fullname; |
|
6653 |
|
6654 fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name, |
|
6655 cur->nodesetval->nodeTab[i]->ns->prefix, |
|
6656 NULL, 0); |
|
6657 if (fullname == cur->nodesetval->nodeTab[i]->name) |
|
6658 fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name); |
|
6659 if (fullname == NULL) { |
|
6660 XP_ERROR(XPATH_MEMORY_ERROR); |
|
6661 } |
|
6662 valuePush(ctxt, xmlXPathWrapString(fullname)); |
|
6663 } |
|
6664 break; |
|
6665 default: |
|
6666 valuePush(ctxt, |
|
6667 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i])); |
|
6668 xmlXPathLocalNameFunction(ctxt, 1); |
|
6669 } |
|
6670 } |
|
6671 xmlXPathFreeObject(cur); |
|
6672 } |
|
6673 |
|
6674 |
|
6675 /** |
|
6676 * xmlXPathStringFunction: |
|
6677 * @param ctxt the XPath Parser context |
|
6678 * @param nargs the number of arguments |
|
6679 * |
|
6680 * Implement the string() XPath function |
|
6681 * string string(object?) |
|
6682 * The string function converts an object to a string as follows: |
|
6683 * - A node-set is converted to a string by returning the value of |
|
6684 * the node in the node-set that is first in document order. |
|
6685 * If the node-set is empty, an empty string is returned. |
|
6686 * - A number is converted to a string as follows |
|
6687 * + NaN is converted to the string NaN |
|
6688 * + positive zero is converted to the string 0 |
|
6689 * + negative zero is converted to the string 0 |
|
6690 * + positive infinity is converted to the string Infinity |
|
6691 * + negative infinity is converted to the string -Infinity |
|
6692 * + if the number is an integer, the number is represented in |
|
6693 * decimal form as a Number with no decimal point and no leading |
|
6694 * zeros, preceded by a minus sign (-) if the number is negative |
|
6695 * + otherwise, the number is represented in decimal form as a |
|
6696 * Number including a decimal point with at least one digit |
|
6697 * before the decimal point and at least one digit after the |
|
6698 * decimal point, preceded by a minus sign (-) if the number |
|
6699 * is negative; there must be no leading zeros before the decimal |
|
6700 * point apart possibly from the one required digit immediately |
|
6701 * before the decimal point; beyond the one required digit |
|
6702 * after the decimal point there must be as many, but only as |
|
6703 * many, more digits as are needed to uniquely distinguish the |
|
6704 * number from all other IEEE 754 numeric values. |
|
6705 * - The boolean false value is converted to the string false. |
|
6706 * The boolean true value is converted to the string true. |
|
6707 * |
|
6708 * If the argument is omitted, it defaults to a node-set with the |
|
6709 * context node as its only member. |
|
6710 * |
|
6711 * OOM: possible |
|
6712 */ |
|
6713 XMLPUBFUNEXPORT void |
|
6714 xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) |
|
6715 { |
|
6716 xmlXPathObjectPtr cur; |
|
6717 |
|
6718 if (nargs == 0) { |
|
6719 valuePush(ctxt, |
|
6720 xmlXPathWrapString( |
|
6721 xmlXPathCastNodeToString(ctxt->context->node))); |
|
6722 return; |
|
6723 } |
|
6724 |
|
6725 CHECK_ARITY(1); |
|
6726 cur = valuePop(ctxt); |
|
6727 if (!cur) |
|
6728 XP_ERROR(XPATH_INVALID_OPERAND); |
|
6729 cur = xmlXPathConvertString(cur); // returns NULL if OOM |
|
6730 |
|
6731 if(!cur) |
|
6732 XP_ERROR(XPATH_MEMORY_ERROR); // OOM |
|
6733 |
|
6734 valuePush(ctxt, cur); |
|
6735 } |
|
6736 |
|
6737 /** |
|
6738 * xmlXPathStringLengthFunction: |
|
6739 * @param ctxt the XPath Parser context |
|
6740 * @param nargs the number of arguments |
|
6741 * |
|
6742 * Implement the string-length() XPath function |
|
6743 * number string-length(string?) |
|
6744 * The string-length returns the number of characters in the string |
|
6745 * (see [3.6 Strings]). If the argument is omitted, it defaults to |
|
6746 * the context node converted to a string, in other words the value |
|
6747 * of the context node. |
|
6748 */ |
|
6749 XMLPUBFUNEXPORT void |
|
6750 xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6751 xmlXPathObjectPtr cur; |
|
6752 |
|
6753 if (nargs == 0) { |
|
6754 if (ctxt->context->node == NULL) { |
|
6755 valuePush(ctxt, xmlXPathNewFloat(0)); |
|
6756 } else { |
|
6757 xmlChar *content; |
|
6758 |
|
6759 content = xmlXPathCastNodeToString(ctxt->context->node); |
|
6760 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content))); |
|
6761 xmlFree(content); |
|
6762 } |
|
6763 return; |
|
6764 } |
|
6765 CHECK_ARITY(1); |
|
6766 CAST_TO_STRING; |
|
6767 CHECK_TYPE(XPATH_STRING); |
|
6768 cur = valuePop(ctxt); |
|
6769 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval))); |
|
6770 xmlXPathFreeObject(cur); |
|
6771 } |
|
6772 |
|
6773 /** |
|
6774 * xmlXPathConcatFunction: |
|
6775 * @param ctxt the XPath Parser context |
|
6776 * @param nargs the number of arguments |
|
6777 * |
|
6778 * Implement the concat() XPath function |
|
6779 * string concat(string, string, string*) |
|
6780 * The concat function returns the concatenation of its arguments. |
|
6781 */ |
|
6782 XMLPUBFUNEXPORT void |
|
6783 xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6784 xmlXPathObjectPtr cur, newobj; |
|
6785 xmlChar *tmp; |
|
6786 |
|
6787 if (nargs < 2) { |
|
6788 CHECK_ARITY(2); |
|
6789 } |
|
6790 |
|
6791 CAST_TO_STRING; |
|
6792 cur = valuePop(ctxt); |
|
6793 if ((cur == NULL) || (cur->type != XPATH_STRING)) { |
|
6794 xmlXPathFreeObject(cur); |
|
6795 return; |
|
6796 } |
|
6797 nargs--; |
|
6798 |
|
6799 while (nargs > 0) { |
|
6800 CAST_TO_STRING; |
|
6801 newobj = valuePop(ctxt); |
|
6802 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) { |
|
6803 xmlXPathFreeObject(newobj); |
|
6804 xmlXPathFreeObject(cur); |
|
6805 XP_ERROR(XPATH_INVALID_TYPE); |
|
6806 } |
|
6807 tmp = xmlStrcat(newobj->stringval, cur->stringval); |
|
6808 newobj->stringval = cur->stringval; |
|
6809 cur->stringval = tmp; |
|
6810 |
|
6811 xmlXPathFreeObject(newobj); |
|
6812 nargs--; |
|
6813 } |
|
6814 valuePush(ctxt, cur); |
|
6815 } |
|
6816 |
|
6817 /** |
|
6818 * xmlXPathContainsFunction: |
|
6819 * @param ctxt the XPath Parser context |
|
6820 * @param nargs the number of arguments |
|
6821 * |
|
6822 * Implement the contains() XPath function |
|
6823 * boolean contains(string, string) |
|
6824 * The contains function returns true if the first argument string |
|
6825 * contains the second argument string, and otherwise returns false. |
|
6826 */ |
|
6827 XMLPUBFUNEXPORT void |
|
6828 xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6829 xmlXPathObjectPtr hay, needle; |
|
6830 |
|
6831 CHECK_ARITY(2); |
|
6832 CAST_TO_STRING; |
|
6833 CHECK_TYPE(XPATH_STRING); |
|
6834 needle = valuePop(ctxt); |
|
6835 CAST_TO_STRING; |
|
6836 hay = valuePop(ctxt); |
|
6837 if ((hay == NULL) || (hay->type != XPATH_STRING)) { |
|
6838 xmlXPathFreeObject(hay); |
|
6839 xmlXPathFreeObject(needle); |
|
6840 XP_ERROR(XPATH_INVALID_TYPE); |
|
6841 } |
|
6842 if (xmlStrstr(hay->stringval, needle->stringval)) |
|
6843 valuePush(ctxt, xmlXPathNewBoolean(1)); |
|
6844 else |
|
6845 valuePush(ctxt, xmlXPathNewBoolean(0)); |
|
6846 xmlXPathFreeObject(hay); |
|
6847 xmlXPathFreeObject(needle); |
|
6848 } |
|
6849 |
|
6850 /** |
|
6851 * xmlXPathStartsWithFunction: |
|
6852 * @param ctxt the XPath Parser context |
|
6853 * @param nargs the number of arguments |
|
6854 * |
|
6855 * Implement the starts-with() XPath function |
|
6856 * boolean starts-with(string, string) |
|
6857 * The starts-with function returns true if the first argument string |
|
6858 * starts with the second argument string, and otherwise returns false. |
|
6859 */ |
|
6860 XMLPUBFUNEXPORT void |
|
6861 xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6862 xmlXPathObjectPtr hay, needle; |
|
6863 int n; |
|
6864 |
|
6865 CHECK_ARITY(2); |
|
6866 CAST_TO_STRING; |
|
6867 CHECK_TYPE(XPATH_STRING); |
|
6868 needle = valuePop(ctxt); |
|
6869 CAST_TO_STRING; |
|
6870 hay = valuePop(ctxt); |
|
6871 if ((hay == NULL) || (hay->type != XPATH_STRING)) { |
|
6872 xmlXPathFreeObject(hay); |
|
6873 xmlXPathFreeObject(needle); |
|
6874 XP_ERROR(XPATH_INVALID_TYPE); |
|
6875 } |
|
6876 n = xmlStrlen(needle->stringval); |
|
6877 if (xmlStrncmp(hay->stringval, needle->stringval, n)) |
|
6878 valuePush(ctxt, xmlXPathNewBoolean(0)); |
|
6879 else |
|
6880 valuePush(ctxt, xmlXPathNewBoolean(1)); |
|
6881 xmlXPathFreeObject(hay); |
|
6882 xmlXPathFreeObject(needle); |
|
6883 } |
|
6884 |
|
6885 /** |
|
6886 * xmlXPathSubstringFunction: |
|
6887 * @param ctxt the XPath Parser context |
|
6888 * @param nargs the number of arguments |
|
6889 * |
|
6890 * Implement the substring() XPath function |
|
6891 * string substring(string, number, number?) |
|
6892 * The substring function returns the substring of the first argument |
|
6893 * starting at the position specified in the second argument with |
|
6894 * length specified in the third argument. For example, |
|
6895 * substring("12345",2,3) returns "234". If the third argument is not |
|
6896 * specified, it returns the substring starting at the position specified |
|
6897 * in the second argument and continuing to the end of the string. For |
|
6898 * example, substring("12345",2) returns "2345". More precisely, each |
|
6899 * character in the string (see [3.6 Strings]) is considered to have a |
|
6900 * numeric position: the position of the first character is 1, the position |
|
6901 * of the second character is 2 and so on. The returned substring contains |
|
6902 * those characters for which the position of the character is greater than |
|
6903 * or equal to the second argument and, if the third argument is specified, |
|
6904 * less than the sum of the second and third arguments; the comparisons |
|
6905 * and addition used for the above follow the standard IEEE 754 rules. Thus: |
|
6906 * - substring("12345", 1.5, 2.6) returns "234" |
|
6907 * - substring("12345", 0, 3) returns "12" |
|
6908 * - substring("12345", 0 div 0, 3) returns "" |
|
6909 * - substring("12345", 1, 0 div 0) returns "" |
|
6910 * - substring("12345", -42, 1 div 0) returns "12345" |
|
6911 * - substring("12345", -1 div 0, 1 div 0) returns "" |
|
6912 */ |
|
6913 XMLPUBFUNEXPORT void |
|
6914 xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6915 xmlXPathObjectPtr str, start, len; |
|
6916 double le=0, in; |
|
6917 |
|
6918 int i, l, m; |
|
6919 xmlChar *ret; |
|
6920 |
|
6921 if (nargs < 2) { |
|
6922 |
|
6923 // -- make check manually OR define normal macro for that |
|
6924 CHECK_ARITY(2); |
|
6925 } |
|
6926 if (nargs > 3) { |
|
6927 CHECK_ARITY(3); |
|
6928 } |
|
6929 /* |
|
6930 * take care of possible last (position) argument |
|
6931 */ |
|
6932 if (nargs == 3) { |
|
6933 CAST_TO_NUMBER; |
|
6934 CHECK_TYPE(XPATH_NUMBER); |
|
6935 len = valuePop(ctxt); |
|
6936 le = len->floatval; |
|
6937 xmlXPathFreeObject(len); |
|
6938 } |
|
6939 |
|
6940 CAST_TO_NUMBER; |
|
6941 CHECK_TYPE(XPATH_NUMBER); |
|
6942 start = valuePop(ctxt); |
|
6943 in = start->floatval; |
|
6944 xmlXPathFreeObject(start); |
|
6945 CAST_TO_STRING; |
|
6946 CHECK_TYPE(XPATH_STRING); |
|
6947 str = valuePop(ctxt); |
|
6948 m = xmlUTF8Strlen((const unsigned char *)str->stringval); |
|
6949 |
|
6950 /* |
|
6951 * If last pos not present, calculate last position |
|
6952 */ |
|
6953 if (nargs != 3) { |
|
6954 le = (double)m; |
|
6955 if (in < 1.0) |
|
6956 in = 1.0; |
|
6957 } |
|
6958 |
|
6959 /* Need to check for the special cases where either |
|
6960 * the index is NaN, the length is NaN, or both |
|
6961 * arguments are infinity (relying on Inf + -Inf = NaN) |
|
6962 */ |
|
6963 if (!xmlXPathIsNaN(in + le) && !xmlXPathIsInf(in)) { |
|
6964 /* |
|
6965 * To meet the requirements of the spec, the arguments |
|
6966 * must be converted to integer format before |
|
6967 * initial index calculations are done |
|
6968 * |
|
6969 * First we go to integer form, rounding up |
|
6970 * and checking for special cases |
|
6971 */ |
|
6972 i = (int) in; |
|
6973 if (((double)i)+0.5 <= in) i++; |
|
6974 |
|
6975 if (xmlXPathIsInf(le) == 1) { |
|
6976 l = m; |
|
6977 if (i < 1) |
|
6978 i = 1; |
|
6979 } |
|
6980 else if (xmlXPathIsInf(le) == -1 || le < 0.0) |
|
6981 l = 0; |
|
6982 else { |
|
6983 l = (int) le; |
|
6984 if (((double)l)+0.5 <= le) l++; |
|
6985 } |
|
6986 |
|
6987 /* Now we normalize inidices */ |
|
6988 i -= 1; |
|
6989 l += i; |
|
6990 if (i < 0) |
|
6991 i = 0; |
|
6992 if (l > m) |
|
6993 l = m; |
|
6994 |
|
6995 /* number of chars to copy */ |
|
6996 l -= i; |
|
6997 |
|
6998 ret = xmlUTF8Strsub(str->stringval, i, l); |
|
6999 } |
|
7000 else { |
|
7001 ret = NULL; |
|
7002 } |
|
7003 |
|
7004 if (ret == NULL) |
|
7005 valuePush(ctxt, xmlXPathNewCString("")); |
|
7006 else { |
|
7007 valuePush(ctxt, xmlXPathNewString(ret)); |
|
7008 xmlFree(ret); |
|
7009 } |
|
7010 |
|
7011 xmlXPathFreeObject(str); |
|
7012 } |
|
7013 |
|
7014 /** |
|
7015 * xmlXPathSubstringBeforeFunction: |
|
7016 * @param ctxt the XPath Parser context |
|
7017 * @param nargs the number of arguments |
|
7018 * |
|
7019 * Implement the substring-before() XPath function |
|
7020 * string substring-before(string, string) |
|
7021 * The substring-before function returns the substring of the first |
|
7022 * argument string that precedes the first occurrence of the second |
|
7023 * argument string in the first argument string, or the empty string |
|
7024 * if the first argument string does not contain the second argument |
|
7025 * string. For example, substring-before("1999/04/01","/") returns 1999. |
|
7026 */ |
|
7027 XMLPUBFUNEXPORT void |
|
7028 xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7029 xmlXPathObjectPtr str; |
|
7030 xmlXPathObjectPtr find; |
|
7031 xmlBufferPtr target; |
|
7032 const xmlChar *point; |
|
7033 int offset; |
|
7034 |
|
7035 CHECK_ARITY(2); |
|
7036 CAST_TO_STRING; |
|
7037 find = valuePop(ctxt); |
|
7038 CAST_TO_STRING; |
|
7039 str = valuePop(ctxt); |
|
7040 |
|
7041 target = xmlBufferCreate(); |
|
7042 if (target) { |
|
7043 point = xmlStrstr(str->stringval, find->stringval); |
|
7044 if (point) { |
|
7045 offset = (int)(point - str->stringval); |
|
7046 xmlBufferAdd(target, str->stringval, offset); |
|
7047 } |
|
7048 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
7049 xmlBufferFree(target); |
|
7050 } |
|
7051 |
|
7052 xmlXPathFreeObject(str); |
|
7053 xmlXPathFreeObject(find); |
|
7054 } |
|
7055 |
|
7056 /** |
|
7057 * xmlXPathSubstringAfterFunction: |
|
7058 * @param ctxt the XPath Parser context |
|
7059 * @param nargs the number of arguments |
|
7060 * |
|
7061 * Implement the substring-after() XPath function |
|
7062 * string substring-after(string, string) |
|
7063 * The substring-after function returns the substring of the first |
|
7064 * argument string that follows the first occurrence of the second |
|
7065 * argument string in the first argument string, or the empty stringi |
|
7066 * if the first argument string does not contain the second argument |
|
7067 * string. For example, substring-after("1999/04/01","/") returns 04/01, |
|
7068 * and substring-after("1999/04/01","19") returns 99/04/01. |
|
7069 */ |
|
7070 XMLPUBFUNEXPORT void |
|
7071 xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7072 xmlXPathObjectPtr str; |
|
7073 xmlXPathObjectPtr find; |
|
7074 xmlBufferPtr target; |
|
7075 const xmlChar *point; |
|
7076 int offset; |
|
7077 |
|
7078 CHECK_ARITY(2); |
|
7079 CAST_TO_STRING; |
|
7080 find = valuePop(ctxt); |
|
7081 CAST_TO_STRING; |
|
7082 str = valuePop(ctxt); |
|
7083 |
|
7084 target = xmlBufferCreate(); |
|
7085 if (target) { |
|
7086 point = xmlStrstr(str->stringval, find->stringval); |
|
7087 if (point) { |
|
7088 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval); |
|
7089 xmlBufferAdd(target, &str->stringval[offset], |
|
7090 xmlStrlen(str->stringval) - offset); |
|
7091 } |
|
7092 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
7093 xmlBufferFree(target); |
|
7094 } |
|
7095 |
|
7096 xmlXPathFreeObject(str); |
|
7097 xmlXPathFreeObject(find); |
|
7098 } |
|
7099 |
|
7100 /** |
|
7101 * xmlXPathNormalizeFunction: |
|
7102 * @param ctxt the XPath Parser context |
|
7103 * @param nargs the number of arguments |
|
7104 * |
|
7105 * Implement the normalize-space() XPath function |
|
7106 * string normalize-space(string?) |
|
7107 * The normalize-space function returns the argument string with white |
|
7108 * space normalized by stripping leading and trailing whitespace |
|
7109 * and replacing sequences of whitespace characters by a single |
|
7110 * space. Whitespace characters are the same allowed by the S production |
|
7111 * in XML. If the argument is omitted, it defaults to the context |
|
7112 * node converted to a string, in other words the value of the context node. |
|
7113 */ |
|
7114 XMLPUBFUNEXPORT void |
|
7115 xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7116 xmlXPathObjectPtr obj = NULL; |
|
7117 xmlChar *source = NULL; |
|
7118 xmlBufferPtr target; |
|
7119 xmlChar blank; |
|
7120 |
|
7121 if (nargs == 0) { |
|
7122 /* Use current context node */ |
|
7123 valuePush(ctxt, |
|
7124 xmlXPathWrapString( |
|
7125 xmlXPathCastNodeToString(ctxt->context->node))); |
|
7126 nargs = 1; |
|
7127 } |
|
7128 |
|
7129 CHECK_ARITY(1); |
|
7130 CAST_TO_STRING; |
|
7131 CHECK_TYPE(XPATH_STRING); |
|
7132 obj = valuePop(ctxt); |
|
7133 source = obj->stringval; |
|
7134 |
|
7135 target = xmlBufferCreate(); |
|
7136 if (target && source) { |
|
7137 |
|
7138 /* Skip leading whitespaces */ |
|
7139 while (IS_BLANK_CH(*source)) |
|
7140 source++; |
|
7141 |
|
7142 /* Collapse intermediate whitespaces, and skip trailing whitespaces */ |
|
7143 blank = 0; |
|
7144 while (*source) { |
|
7145 if (IS_BLANK_CH(*source)) { |
|
7146 blank = 0x20; |
|
7147 } else { |
|
7148 if (blank) { |
|
7149 xmlBufferAdd(target, &blank, 1); |
|
7150 blank = 0; |
|
7151 } |
|
7152 xmlBufferAdd(target, source, 1); |
|
7153 } |
|
7154 source++; |
|
7155 } |
|
7156 |
|
7157 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
7158 xmlBufferFree(target); |
|
7159 } |
|
7160 xmlXPathFreeObject(obj); |
|
7161 } |
|
7162 |
|
7163 /** |
|
7164 * xmlXPathTranslateFunction: |
|
7165 * @param ctxt the XPath Parser context |
|
7166 * @param nargs the number of arguments |
|
7167 * |
|
7168 * Implement the translate() XPath function |
|
7169 * string translate(string, string, string) |
|
7170 * The translate function returns the first argument string with |
|
7171 * occurrences of characters in the second argument string replaced |
|
7172 * by the character at the corresponding position in the third argument |
|
7173 * string. For example, translate("bar","abc","ABC") returns the string |
|
7174 * BAr. If there is a character in the second argument string with no |
|
7175 * character at a corresponding position in the third argument string |
|
7176 * (because the second argument string is longer than the third argument |
|
7177 * string), then occurrences of that character in the first argument |
|
7178 * string are removed. For example, translate("--aaa--","abc-","ABC") |
|
7179 * returns "AAA". If a character occurs more than once in second |
|
7180 * argument string, then the first occurrence determines the replacement |
|
7181 * character. If the third argument string is longer than the second |
|
7182 * argument string, then excess characters are ignored. |
|
7183 */ |
|
7184 XMLPUBFUNEXPORT void |
|
7185 xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7186 xmlXPathObjectPtr str; |
|
7187 xmlXPathObjectPtr from; |
|
7188 xmlXPathObjectPtr to; |
|
7189 xmlBufferPtr target; |
|
7190 int offset, max; |
|
7191 xmlChar ch; |
|
7192 xmlChar *point; |
|
7193 xmlChar *cptr; |
|
7194 |
|
7195 CHECK_ARITY(3); |
|
7196 |
|
7197 CAST_TO_STRING; |
|
7198 to = valuePop(ctxt); |
|
7199 CAST_TO_STRING; |
|
7200 from = valuePop(ctxt); |
|
7201 CAST_TO_STRING; |
|
7202 str = valuePop(ctxt); |
|
7203 |
|
7204 target = xmlBufferCreate(); |
|
7205 if (target) { |
|
7206 max = xmlUTF8Strlen(to->stringval); |
|
7207 for (cptr = str->stringval; (ch=*cptr); ) { |
|
7208 offset = xmlUTF8Strloc(from->stringval, cptr); |
|
7209 if (offset >= 0) { |
|
7210 if (offset < max) { |
|
7211 point = xmlUTF8Strpos(to->stringval, offset); |
|
7212 if (point) |
|
7213 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1)); |
|
7214 } |
|
7215 } else |
|
7216 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1)); |
|
7217 |
|
7218 /* Step to next character in input */ |
|
7219 cptr++; |
|
7220 if ( ch & 0x80 ) { |
|
7221 /* if not simple ascii, verify proper format */ |
|
7222 if ( (ch & 0xc0) != 0xc0 ) { |
|
7223 xmlGenericError(xmlGenericErrorContext, |
|
7224 EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n")); |
|
7225 break; |
|
7226 } |
|
7227 /* then skip over remaining bytes for this char */ |
|
7228 while ( (ch <<= 1) & 0x80 ) |
|
7229 if ( (*cptr++ & 0xc0) != 0x80 ) { |
|
7230 xmlGenericError(xmlGenericErrorContext, |
|
7231 EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n")); |
|
7232 break; |
|
7233 } |
|
7234 if (ch & 0x80) /* must have had error encountered */ |
|
7235 break; |
|
7236 } |
|
7237 } |
|
7238 } |
|
7239 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
7240 xmlBufferFree(target); |
|
7241 xmlXPathFreeObject(str); |
|
7242 xmlXPathFreeObject(from); |
|
7243 xmlXPathFreeObject(to); |
|
7244 } |
|
7245 |
|
7246 /** |
|
7247 * xmlXPathBooleanFunction: |
|
7248 * @param ctxt the XPath Parser context |
|
7249 * @param nargs the number of arguments |
|
7250 * |
|
7251 * Implement the boolean() XPath function |
|
7252 * boolean boolean(object) |
|
7253 * The boolean function converts its argument to a boolean as follows: |
|
7254 * - a number is true if and only if it is neither positive or |
|
7255 * negative zero nor NaN |
|
7256 * - a node-set is true if and only if it is non-empty |
|
7257 * - a string is true if and only if its length is non-zero |
|
7258 */ |
|
7259 XMLPUBFUNEXPORT void |
|
7260 xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7261 xmlXPathObjectPtr cur; |
|
7262 |
|
7263 CHECK_ARITY(1); |
|
7264 cur = valuePop(ctxt); |
|
7265 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); |
|
7266 cur = xmlXPathConvertBoolean(cur); |
|
7267 valuePush(ctxt, cur); |
|
7268 } |
|
7269 |
|
7270 /** |
|
7271 * xmlXPathNotFunction: |
|
7272 * @param ctxt the XPath Parser context |
|
7273 * @param nargs the number of arguments |
|
7274 * |
|
7275 * Implement the not() XPath function |
|
7276 * boolean not(boolean) |
|
7277 * The not function returns true if its argument is false, |
|
7278 * and false otherwise. |
|
7279 */ |
|
7280 XMLPUBFUNEXPORT void |
|
7281 xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7282 CHECK_ARITY(1); |
|
7283 CAST_TO_BOOLEAN; |
|
7284 CHECK_TYPE(XPATH_BOOLEAN); |
|
7285 ctxt->value->boolval = ! ctxt->value->boolval; |
|
7286 } |
|
7287 |
|
7288 /** |
|
7289 * xmlXPathTrueFunction: |
|
7290 * @param ctxt the XPath Parser context |
|
7291 * @param nargs the number of arguments |
|
7292 * |
|
7293 * Implement the true() XPath function |
|
7294 * boolean true() |
|
7295 */ |
|
7296 XMLPUBFUNEXPORT void |
|
7297 xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7298 CHECK_ARITY(0); |
|
7299 valuePush(ctxt, xmlXPathNewBoolean(1)); |
|
7300 } |
|
7301 |
|
7302 /** |
|
7303 * xmlXPathFalseFunction: |
|
7304 * @param ctxt the XPath Parser context |
|
7305 * @param nargs the number of arguments |
|
7306 * |
|
7307 * Implement the false() XPath function |
|
7308 * boolean false() |
|
7309 */ |
|
7310 XMLPUBFUNEXPORT void |
|
7311 xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7312 CHECK_ARITY(0); |
|
7313 valuePush(ctxt, xmlXPathNewBoolean(0)); |
|
7314 } |
|
7315 |
|
7316 /** |
|
7317 * xmlXPathLangFunction: |
|
7318 * @param ctxt the XPath Parser context |
|
7319 * @param nargs the number of arguments |
|
7320 * |
|
7321 * Implement the lang() XPath function |
|
7322 * boolean lang(string) |
|
7323 * The lang function returns true or false depending on whether the |
|
7324 * language of the context node as specified by xml:lang attributes |
|
7325 * is the same as or is a sublanguage of the language specified by |
|
7326 * the argument string. The language of the context node is determined |
|
7327 * by the value of the xml:lang attribute on the context node, or, if |
|
7328 * the context node has no xml:lang attribute, by the value of the |
|
7329 * xml:lang attribute on the nearest ancestor of the context node that |
|
7330 * has an xml:lang attribute. If there is no such attribute, then lang |
|
7331 * returns false. If there is such an attribute, then lang returns |
|
7332 * true if the attribute value is equal to the argument ignoring case, |
|
7333 * or if there is some suffix starting with - such that the attribute |
|
7334 * value is equal to the argument ignoring that suffix of the attribute |
|
7335 * value and ignoring case. |
|
7336 */ |
|
7337 XMLPUBFUNEXPORT void |
|
7338 xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7339 xmlXPathObjectPtr val; |
|
7340 const xmlChar *theLang; |
|
7341 const xmlChar *lang; |
|
7342 int ret = 0; |
|
7343 int i; |
|
7344 |
|
7345 CHECK_ARITY(1); |
|
7346 CAST_TO_STRING; |
|
7347 CHECK_TYPE(XPATH_STRING); |
|
7348 val = valuePop(ctxt); |
|
7349 lang = val->stringval; |
|
7350 theLang = xmlNodeGetLang(ctxt->context->node); |
|
7351 if ((theLang != NULL) && (lang != NULL)) { |
|
7352 for (i = 0;lang[i] != 0;i++) |
|
7353 if (toupper(lang[i]) != toupper(theLang[i])) |
|
7354 goto not_equal; |
|
7355 ret = 1; |
|
7356 } |
|
7357 not_equal: |
|
7358 if (theLang) |
|
7359 xmlFree((void *)theLang); |
|
7360 xmlXPathFreeObject(val); |
|
7361 valuePush(ctxt, xmlXPathNewBoolean(ret)); |
|
7362 } |
|
7363 |
|
7364 /** |
|
7365 * xmlXPathNumberFunction: |
|
7366 * @param ctxt the XPath Parser context |
|
7367 * @param nargs the number of arguments |
|
7368 * |
|
7369 * Implement the number() XPath function |
|
7370 * number number(object?) |
|
7371 */ |
|
7372 XMLPUBFUNEXPORT void |
|
7373 xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7374 xmlXPathObjectPtr cur; |
|
7375 double res; |
|
7376 |
|
7377 if (nargs == 0) { |
|
7378 if (!ctxt->context->node) { |
|
7379 valuePush(ctxt, xmlXPathNewFloat(0.0)); |
|
7380 } else { |
|
7381 xmlChar* content = xmlNodeGetContent(ctxt->context->node); |
|
7382 |
|
7383 res = xmlXPathStringEvalNumber(content); |
|
7384 valuePush(ctxt, xmlXPathNewFloat(res)); |
|
7385 xmlFree(content); |
|
7386 } |
|
7387 return; |
|
7388 } |
|
7389 |
|
7390 CHECK_ARITY(1); |
|
7391 cur = valuePop(ctxt); |
|
7392 cur = xmlXPathConvertNumber(cur); |
|
7393 valuePush(ctxt, cur); |
|
7394 } |
|
7395 |
|
7396 /** |
|
7397 * xmlXPathSumFunction: |
|
7398 * @param ctxt the XPath Parser context |
|
7399 * @param nargs the number of arguments |
|
7400 * |
|
7401 * Implement the sum() XPath function |
|
7402 * number sum(node-set) |
|
7403 * The sum function returns the sum of the values of the nodes in |
|
7404 * the argument node-set. |
|
7405 */ |
|
7406 XMLPUBFUNEXPORT void |
|
7407 xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7408 xmlXPathObjectPtr cur; |
|
7409 int i; |
|
7410 double res = 0.0; |
|
7411 |
|
7412 CHECK_ARITY(1); |
|
7413 if ((ctxt->value == NULL) || |
|
7414 ((ctxt->value->type != XPATH_NODESET) && |
|
7415 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
7416 XP_ERROR(XPATH_INVALID_TYPE); |
|
7417 cur = valuePop(ctxt); |
|
7418 |
|
7419 if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) { |
|
7420 for (i = 0; i < cur->nodesetval->nodeNr; i++) { |
|
7421 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]); |
|
7422 } |
|
7423 } |
|
7424 valuePush(ctxt, xmlXPathNewFloat(res)); |
|
7425 xmlXPathFreeObject(cur); |
|
7426 } |
|
7427 |
|
7428 /** |
|
7429 * xmlXPathFloorFunction: |
|
7430 * @param ctxt the XPath Parser context |
|
7431 * @param nargs the number of arguments |
|
7432 * |
|
7433 * Implement the floor() XPath function |
|
7434 * number floor(number) |
|
7435 * The floor function returns the largest (closest to positive infinity) |
|
7436 * number that is not greater than the argument and that is an integer. |
|
7437 */ |
|
7438 XMLPUBFUNEXPORT void |
|
7439 xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7440 double f; |
|
7441 |
|
7442 CHECK_ARITY(1); |
|
7443 CAST_TO_NUMBER; |
|
7444 CHECK_TYPE(XPATH_NUMBER); |
|
7445 |
|
7446 f = (double)((int) ctxt->value->floatval); |
|
7447 if (f != ctxt->value->floatval) { |
|
7448 if (ctxt->value->floatval > 0) |
|
7449 ctxt->value->floatval = f; |
|
7450 else |
|
7451 ctxt->value->floatval = f - 1; |
|
7452 } |
|
7453 } |
|
7454 |
|
7455 /** |
|
7456 * xmlXPathCeilingFunction: |
|
7457 * @param ctxt the XPath Parser context |
|
7458 * @param nargs the number of arguments |
|
7459 * |
|
7460 * Implement the ceiling() XPath function |
|
7461 * number ceiling(number) |
|
7462 * The ceiling function returns the smallest (closest to negative infinity) |
|
7463 * number that is not less than the argument and that is an integer. |
|
7464 */ |
|
7465 XMLPUBFUNEXPORT void |
|
7466 xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7467 double f; |
|
7468 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
7469 |
|
7470 CHECK_ARITY(1); |
|
7471 CAST_TO_NUMBER; |
|
7472 CHECK_TYPE(XPATH_NUMBER); |
|
7473 |
|
7474 #if 0 |
|
7475 ctxt->value->floatval = ceil(ctxt->value->floatval); |
|
7476 #else |
|
7477 f = (double)((int) ctxt->value->floatval); |
|
7478 if (f != ctxt->value->floatval) { |
|
7479 if (ctxt->value->floatval > 0) |
|
7480 ctxt->value->floatval = f + 1; |
|
7481 else { |
|
7482 if (ctxt->value->floatval < 0 && f == 0) |
|
7483 ctxt->value->floatval = xmlXPathNZERO; |
|
7484 else |
|
7485 ctxt->value->floatval = f; |
|
7486 } |
|
7487 |
|
7488 } |
|
7489 #endif |
|
7490 } |
|
7491 |
|
7492 /** |
|
7493 * xmlXPathRoundFunction: |
|
7494 * @param ctxt the XPath Parser context |
|
7495 * @param nargs the number of arguments |
|
7496 * |
|
7497 * Implement the round() XPath function |
|
7498 * number round(number) |
|
7499 * The round function returns the number that is closest to the |
|
7500 * argument and that is an integer. If there are two such numbers, |
|
7501 * then the one that is even is returned. |
|
7502 */ |
|
7503 XMLPUBFUNEXPORT void |
|
7504 xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7505 double f; |
|
7506 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
7507 |
|
7508 CHECK_ARITY(1); |
|
7509 CAST_TO_NUMBER; |
|
7510 CHECK_TYPE(XPATH_NUMBER); |
|
7511 |
|
7512 if ((xmlXPathIsNaN(ctxt->value->floatval)) || |
|
7513 (xmlXPathIsInf(ctxt->value->floatval) == 1) || |
|
7514 (xmlXPathIsInf(ctxt->value->floatval) == -1) || |
|
7515 (ctxt->value->floatval == 0.0)) |
|
7516 return; |
|
7517 |
|
7518 f = (double)((int) ctxt->value->floatval); |
|
7519 if (ctxt->value->floatval < 0) { |
|
7520 if (ctxt->value->floatval < f - 0.5) |
|
7521 ctxt->value->floatval = f - 1; |
|
7522 else |
|
7523 ctxt->value->floatval = f; |
|
7524 if (ctxt->value->floatval == 0) |
|
7525 ctxt->value->floatval = xmlXPathNZERO; |
|
7526 } else { |
|
7527 if (ctxt->value->floatval < f + 0.5) |
|
7528 ctxt->value->floatval = f; |
|
7529 else |
|
7530 ctxt->value->floatval = f + 1; |
|
7531 } |
|
7532 } |
|
7533 |
|
7534 /************************************************************************ |
|
7535 * * |
|
7536 * The Parser * |
|
7537 * * |
|
7538 ************************************************************************/ |
|
7539 |
|
7540 /* |
|
7541 * a few forward declarations since we use a recursive call based |
|
7542 * implementation. |
|
7543 */ |
|
7544 static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt); |
|
7545 static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter); |
|
7546 static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt); |
|
7547 static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt); |
|
7548 static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, |
|
7549 int qualified); |
|
7550 |
|
7551 /** |
|
7552 * xmlXPathCurrentChar: |
|
7553 * @param ctxt the XPath parser context |
|
7554 * @param cur pointer to the beginning of the char |
|
7555 * @param len pointer to the length of the char read |
|
7556 * |
|
7557 * The current char value, if using UTF-8 this may actually span multiple |
|
7558 * bytes in the input buffer. |
|
7559 * |
|
7560 * Returns the current char value and its length |
|
7561 */ |
|
7562 |
|
7563 static int |
|
7564 xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) { |
|
7565 unsigned char c; |
|
7566 unsigned int val; |
|
7567 const xmlChar *cur; |
|
7568 |
|
7569 if (ctxt == NULL) |
|
7570 return(0); |
|
7571 cur = ctxt->cur; |
|
7572 |
|
7573 /* |
|
7574 * We are supposed to handle UTF8, check it's valid |
|
7575 * From rfc2044: encoding of the Unicode values on UTF-8: |
|
7576 * |
|
7577 * UCS-4 range (hex.) UTF-8 octet sequence (binary) |
|
7578 * 0000 0000-0000 007F 0xxxxxxx |
|
7579 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx |
|
7580 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx |
|
7581 * |
|
7582 * Check for the 0x110000 limit too |
|
7583 */ |
|
7584 c = *cur; |
|
7585 if (c & 0x80) { |
|
7586 if ((cur[1] & 0xc0) != 0x80) |
|
7587 goto encoding_error; |
|
7588 if ((c & 0xe0) == 0xe0) { |
|
7589 |
|
7590 if ((cur[2] & 0xc0) != 0x80) |
|
7591 goto encoding_error; |
|
7592 if ((c & 0xf0) == 0xf0) { |
|
7593 if (((c & 0xf8) != 0xf0) || |
|
7594 ((cur[3] & 0xc0) != 0x80)) |
|
7595 goto encoding_error; |
|
7596 /* 4-byte code */ |
|
7597 *len = 4; |
|
7598 val = (cur[0] & 0x7) << 18; |
|
7599 val |= (cur[1] & 0x3f) << 12; |
|
7600 val |= (cur[2] & 0x3f) << 6; |
|
7601 val |= cur[3] & 0x3f; |
|
7602 } else { |
|
7603 /* 3-byte code */ |
|
7604 *len = 3; |
|
7605 val = (cur[0] & 0xf) << 12; |
|
7606 val |= (cur[1] & 0x3f) << 6; |
|
7607 val |= cur[2] & 0x3f; |
|
7608 } |
|
7609 } else { |
|
7610 /* 2-byte code */ |
|
7611 *len = 2; |
|
7612 val = (cur[0] & 0x1f) << 6; |
|
7613 val |= cur[1] & 0x3f; |
|
7614 } |
|
7615 if (!IS_CHAR(val)) { |
|
7616 XP_ERROR0(XPATH_INVALID_CHAR_ERROR); |
|
7617 } |
|
7618 return(val); |
|
7619 } else { |
|
7620 /* 1-byte code */ |
|
7621 *len = 1; |
|
7622 return((int) *cur); |
|
7623 } |
|
7624 encoding_error: |
|
7625 /* |
|
7626 * If we detect an UTF8 error that probably means that the |
|
7627 * input encoding didn't get properly advertised in the |
|
7628 * declaration header. Report the error and switch the encoding |
|
7629 * to ISO-Latin-1 (if you don't like this policy, just declare the |
|
7630 * encoding !) |
|
7631 */ |
|
7632 *len = 0; |
|
7633 XP_ERROR0(XPATH_ENCODING_ERROR); |
|
7634 } |
|
7635 |
|
7636 /** |
|
7637 * xmlXPathParseNCName: |
|
7638 * @param ctxt the XPath Parser context |
|
7639 * |
|
7640 * parse an XML namespace non qualified name. |
|
7641 * |
|
7642 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)* |
|
7643 * |
|
7644 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | |
|
7645 * CombiningChar | Extender |
|
7646 * |
|
7647 * Returns the namespace name or NULL |
|
7648 * OOM: possible --> returns NULL, but OOM flag should be checked |
|
7649 * (can return NULL in non-OOM) |
|
7650 */ |
|
7651 XMLPUBFUNEXPORT xmlChar* |
|
7652 xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) |
|
7653 { |
|
7654 const xmlChar* in; |
|
7655 /* |
|
7656 * Accelerator for simple ASCII names |
|
7657 */ |
|
7658 in = ctxt->cur; |
|
7659 if (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7660 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7661 (*in == '_')) |
|
7662 { |
|
7663 in++; |
|
7664 while (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7665 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7666 ((*in >= 0x30) && (*in <= 0x39)) || |
|
7667 (*in == '_') || |
|
7668 (*in == '.') || |
|
7669 (*in == '-')) |
|
7670 { |
|
7671 in++; |
|
7672 } |
|
7673 if ((*in == ' ') || (*in == '>') || (*in == '/') || |
|
7674 (*in == '[') || (*in == ']') || (*in == ':') || |
|
7675 (*in == '@') || (*in == '*')) |
|
7676 { |
|
7677 xmlChar* ret; |
|
7678 int count = in - ctxt->cur; |
|
7679 // NotE: count always > 0 here since in++ was done |
|
7680 //if (count == 0) |
|
7681 // return(NULL); |
|
7682 ret = xmlStrndup(ctxt->cur, count); |
|
7683 //if(!ret) |
|
7684 // xmlXPathErr(ctxt, XPATH_MEMORY_ERROR); |
|
7685 ctxt->cur = in; |
|
7686 return(ret); |
|
7687 } |
|
7688 } |
|
7689 return(xmlXPathParseNameComplex(ctxt, 0)); |
|
7690 } |
|
7691 |
|
7692 |
|
7693 /** |
|
7694 * xmlXPathParseQName: |
|
7695 * @param ctxt the XPath Parser context |
|
7696 * @param prefix a xmlChar ** |
|
7697 * |
|
7698 * parse an XML qualified name |
|
7699 * |
|
7700 * [NS 5] QName ::= (Prefix ':')? LocalPart |
|
7701 * |
|
7702 * [NS 6] Prefix ::= NCName |
|
7703 * |
|
7704 * [NS 7] LocalPart ::= NCName |
|
7705 * |
|
7706 * Returns the function returns the local part, and prefix is updated |
|
7707 * to get the Prefix if any. |
|
7708 */ |
|
7709 |
|
7710 static xmlChar * |
|
7711 xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) |
|
7712 { |
|
7713 xmlChar *ret = NULL; |
|
7714 |
|
7715 *prefix = NULL; |
|
7716 ret = xmlXPathParseNCName(ctxt); |
|
7717 if (CUR == ':') { |
|
7718 *prefix = ret; |
|
7719 NEXT; |
|
7720 ret = xmlXPathParseNCName(ctxt); |
|
7721 } |
|
7722 return(ret); |
|
7723 } |
|
7724 |
|
7725 /** |
|
7726 * xmlXPathParseName: |
|
7727 * @param ctxt the XPath Parser context |
|
7728 * |
|
7729 * parse an XML name |
|
7730 * |
|
7731 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | |
|
7732 * CombiningChar | Extender |
|
7733 * |
|
7734 * [5] Name ::= (Letter | '_' | ':') (NameChar)* |
|
7735 * |
|
7736 * Returns the namespace name or NULL |
|
7737 */ |
|
7738 |
|
7739 XMLPUBFUNEXPORT xmlChar * |
|
7740 xmlXPathParseName(xmlXPathParserContextPtr ctxt) |
|
7741 { |
|
7742 const xmlChar *in; |
|
7743 |
|
7744 /* |
|
7745 * Accelerator for simple ASCII names |
|
7746 */ |
|
7747 in = ctxt->cur; |
|
7748 if (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7749 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7750 (*in == '_') || (*in == ':')) |
|
7751 { |
|
7752 in++; |
|
7753 while (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7754 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7755 ((*in >= 0x30) && (*in <= 0x39)) || |
|
7756 (*in == '_') || (*in == '-') || |
|
7757 (*in == ':') || (*in == '.')) |
|
7758 in++; |
|
7759 if ((*in > 0) && (*in < 0x80)) |
|
7760 { |
|
7761 int count = in - ctxt->cur; |
|
7762 xmlChar* ret= xmlStrndup(ctxt->cur, count); |
|
7763 ctxt->cur = in; |
|
7764 return(ret); |
|
7765 } |
|
7766 } |
|
7767 return(xmlXPathParseNameComplex(ctxt, 1)); |
|
7768 } |
|
7769 |
|
7770 /* |
|
7771 * OOM: possible --> check OOM flag |
|
7772 */ |
|
7773 static xmlChar* |
|
7774 xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) |
|
7775 { |
|
7776 xmlChar buf[XML_MAX_NAMELEN + 5]; |
|
7777 int len = 0, l; |
|
7778 int c; |
|
7779 |
|
7780 /* |
|
7781 * Handler for more complex cases |
|
7782 */ |
|
7783 c = CUR_CHAR(l); |
|
7784 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */ |
|
7785 (c == '[') || (c == ']') || (c == '@') || /* accelerators */ |
|
7786 (c == '*') || /* accelerators */ |
|
7787 (!IS_LETTER(c) && |
|
7788 (c != '_') && |
|
7789 qualified && |
|
7790 (c != ':') |
|
7791 ) |
|
7792 ) |
|
7793 { |
|
7794 return(NULL); |
|
7795 } |
|
7796 |
|
7797 while((c != ' ') && (c != '>') && (c != '/') |
|
7798 && /* test bigname.xml */ |
|
7799 ( |
|
7800 IS_LETTER(c) || |
|
7801 IS_DIGIT(c) || |
|
7802 (c == '.') || |
|
7803 (c == '-') || |
|
7804 (c == '_') || |
|
7805 (qualified && (c == ':')) || |
|
7806 IS_COMBINING(c) || |
|
7807 IS_EXTENDER(c) |
|
7808 ) |
|
7809 ) |
|
7810 { |
|
7811 COPY_BUF(l,buf,len,c); |
|
7812 NEXTL(l); |
|
7813 c = CUR_CHAR(l); |
|
7814 if (len >= XML_MAX_NAMELEN) |
|
7815 { |
|
7816 /* |
|
7817 * Okay someone managed to make a huge name, so he's ready to pay |
|
7818 * for the processing speed. |
|
7819 */ |
|
7820 xmlChar* buffer; |
|
7821 int max = len * 2; |
|
7822 |
|
7823 buffer = (xmlChar*) xmlMallocAtomic(max * sizeof(xmlChar)); |
|
7824 if (!buffer) { |
|
7825 XP_ERROR0(XPATH_MEMORY_ERROR); |
|
7826 } |
|
7827 memcpy(buffer, buf, len); |
|
7828 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */ |
|
7829 (c == '.') || (c == '-') || |
|
7830 (c == '_') || ((qualified) && (c == ':')) || |
|
7831 (IS_COMBINING(c)) || |
|
7832 (IS_EXTENDER(c))) |
|
7833 { |
|
7834 if (len + 10 > max) |
|
7835 { |
|
7836 xmlChar* tmp; |
|
7837 max *= 2; |
|
7838 // DONE: Fix xmlRealloc |
|
7839 tmp = (xmlChar*) xmlRealloc(buffer, max * sizeof(xmlChar)); |
|
7840 if (!tmp) { |
|
7841 xmlFree(buffer); |
|
7842 XP_ERROR0(XPATH_MEMORY_ERROR); |
|
7843 } |
|
7844 buffer = tmp; |
|
7845 } |
|
7846 COPY_BUF(l,buffer,len,c); |
|
7847 NEXTL(l); |
|
7848 c = CUR_CHAR(l); |
|
7849 } |
|
7850 buffer[len] = 0; |
|
7851 return(buffer); |
|
7852 } |
|
7853 } |
|
7854 if (len == 0) |
|
7855 return(NULL); |
|
7856 return(xmlStrndup(buf, len)); |
|
7857 } |
|
7858 |
|
7859 #define MAX_FRAC 20 |
|
7860 |
|
7861 /* |
|
7862 * These are used as divisors for the fractional part of a number. |
|
7863 * Since the table includes 1.0 (representing '0' fractional digits), |
|
7864 * it must be dimensioned at MAX_FRAC+1 (bug133921) |
|
7865 */ |
|
7866 static const double my_pow10[MAX_FRAC+1] = { |
|
7867 1.0, 10.0, 100.0, 1000.0, 10000.0, |
|
7868 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0, |
|
7869 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0, |
|
7870 100000000000000.0, |
|
7871 1000000000000000.0, 10000000000000000.0, 100000000000000000.0, |
|
7872 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0 |
|
7873 }; |
|
7874 |
|
7875 |
|
7876 /** |
|
7877 * xmlXPathStringEvalNumber: |
|
7878 * @param str A string to scan |
|
7879 * |
|
7880 * [30a] Float ::= Number ('e' Digits?)? |
|
7881 * |
|
7882 * [30] Number ::= Digits ('.' Digits?)? |
|
7883 * | '.' Digits |
|
7884 * [31] Digits ::= [0-9]+ |
|
7885 * |
|
7886 * Compile a Number in the string |
|
7887 * In complement of the Number expression, this function also handles |
|
7888 * negative values : '-' Number. |
|
7889 * |
|
7890 * Returns the double value. |
|
7891 * |
|
7892 * OOM: never |
|
7893 */ |
|
7894 XMLPUBFUNEXPORT double |
|
7895 xmlXPathStringEvalNumber(const xmlChar *str) { |
|
7896 LOAD_GS_DIRECT |
|
7897 const xmlChar *cur = str; |
|
7898 double ret; |
|
7899 int ok = 0; |
|
7900 int isneg = 0; |
|
7901 int exponent = 0; |
|
7902 int is_exponent_negative = 0; |
|
7903 #ifdef __GNUC__ |
|
7904 unsigned long tmp = 0; |
|
7905 double temp; |
|
7906 #endif |
|
7907 if (cur == NULL) |
|
7908 return(0); |
|
7909 // |
|
7910 while (IS_BLANK_CH(*cur)) |
|
7911 { |
|
7912 cur++; |
|
7913 } |
|
7914 |
|
7915 if ((*cur != '.') && |
|
7916 ((*cur < '0') || (*cur > '9')) && |
|
7917 (*cur != '-')) |
|
7918 { |
|
7919 return(xmlXPathNAN); |
|
7920 } |
|
7921 if (*cur == '-') { |
|
7922 isneg = 1; |
|
7923 cur++; |
|
7924 } |
|
7925 |
|
7926 #ifdef __GNUC__ |
|
7927 /* |
|
7928 * tmp/temp is a workaround against a gcc compilerBug |
|
7929 * http://veillard.com/gcc.bug |
|
7930 */ |
|
7931 ret = 0; |
|
7932 while ((*cur >= '0') && (*cur <= '9')) |
|
7933 { |
|
7934 ret = ret * 10; |
|
7935 tmp = (*cur - '0'); |
|
7936 ok = 1; |
|
7937 cur++; |
|
7938 temp = (double) tmp; |
|
7939 ret = ret + temp; |
|
7940 } |
|
7941 #else |
|
7942 ret = 0; |
|
7943 while ((*cur >= '0') && (*cur <= '9')) |
|
7944 { |
|
7945 ret = ret * 10 + (*cur - '0'); |
|
7946 ok = 1; |
|
7947 cur++; |
|
7948 } |
|
7949 #endif |
|
7950 |
|
7951 if (*cur == '.') |
|
7952 { |
|
7953 int v, frac = 0; |
|
7954 double fraction = 0; |
|
7955 |
|
7956 cur++; |
|
7957 if (((*cur < '0') || (*cur > '9')) && (!ok)) |
|
7958 { |
|
7959 return(xmlXPathNAN); |
|
7960 } |
|
7961 while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) |
|
7962 { |
|
7963 v = (*cur - '0'); |
|
7964 fraction = fraction * 10 + v; |
|
7965 frac = frac + 1; |
|
7966 cur++; |
|
7967 } |
|
7968 fraction /= my_pow10[frac]; |
|
7969 ret = ret + fraction; |
|
7970 while ((*cur >= '0') && (*cur <= '9')) |
|
7971 { |
|
7972 cur++; |
|
7973 } |
|
7974 } |
|
7975 if ((*cur == 'e') || (*cur == 'E')) |
|
7976 { |
|
7977 cur++; |
|
7978 if (*cur == '-') |
|
7979 { |
|
7980 is_exponent_negative = 1; |
|
7981 cur++; |
|
7982 } |
|
7983 while ((*cur >= '0') && (*cur <= '9')) |
|
7984 { |
|
7985 exponent = exponent * 10 + (*cur - '0'); |
|
7986 cur++; |
|
7987 } |
|
7988 } |
|
7989 while (IS_BLANK_CH(*cur)) |
|
7990 { |
|
7991 cur++; |
|
7992 } |
|
7993 if (*cur != 0) |
|
7994 return(xmlXPathNAN); |
|
7995 if (isneg) |
|
7996 ret = -ret; |
|
7997 if (is_exponent_negative) |
|
7998 exponent = -exponent; |
|
7999 // |
|
8000 ret *= pow(10.0, (double)exponent); |
|
8001 return(ret); |
|
8002 } |
|
8003 |
|
8004 /** |
|
8005 * xmlXPathCompNumber: |
|
8006 * @param ctxt the XPath Parser context |
|
8007 * |
|
8008 * [30] Number ::= Digits ('.' Digits?)? |
|
8009 * | '.' Digits |
|
8010 * [31] Digits ::= [0-9]+ |
|
8011 * |
|
8012 * Compile a Number, then push it on the stack |
|
8013 * |
|
8014 */ |
|
8015 static void |
|
8016 xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) |
|
8017 { |
|
8018 double ret = 0.0; |
|
8019 double mult = 1; |
|
8020 int ok = 0; |
|
8021 int exponent = 0; |
|
8022 int is_exponent_negative = 0; |
|
8023 #ifdef __GNUC__ |
|
8024 unsigned long tmp = 0; |
|
8025 double temp; |
|
8026 #endif |
|
8027 |
|
8028 CHECK_ERROR; |
|
8029 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) { |
|
8030 XP_ERROR(XPATH_NUMBER_ERROR); |
|
8031 } |
|
8032 #ifdef __GNUC__ |
|
8033 /* |
|
8034 * tmp/temp is a workaround against a gcc compilerBug |
|
8035 * http://veillard.com/gcc.bug |
|
8036 */ |
|
8037 ret = 0; |
|
8038 while ((CUR >= '0') && (CUR <= '9')) { |
|
8039 ret = ret * 10; |
|
8040 tmp = (CUR - '0'); |
|
8041 ok = 1; |
|
8042 NEXT; |
|
8043 temp = (double) tmp; |
|
8044 ret = ret + temp; |
|
8045 } |
|
8046 #else |
|
8047 ret = 0; |
|
8048 while ((CUR >= '0') && (CUR <= '9')) { |
|
8049 ret = ret * 10 + (CUR - '0'); |
|
8050 ok = 1; |
|
8051 NEXT; |
|
8052 } |
|
8053 #endif |
|
8054 if (CUR == '.') { |
|
8055 NEXT; |
|
8056 if (((CUR < '0') || (CUR > '9')) && (!ok)) { |
|
8057 XP_ERROR(XPATH_NUMBER_ERROR); |
|
8058 } |
|
8059 while ((CUR >= '0') && (CUR <= '9')) { |
|
8060 mult /= 10; |
|
8061 ret = ret + (CUR - '0') * mult; |
|
8062 NEXT; |
|
8063 } |
|
8064 } |
|
8065 if ((CUR == 'e') || (CUR == 'E')) { |
|
8066 NEXT; |
|
8067 if (CUR == '-') { |
|
8068 is_exponent_negative = 1; |
|
8069 NEXT; |
|
8070 } |
|
8071 while ((CUR >= '0') && (CUR <= '9')) { |
|
8072 exponent = exponent * 10 + (CUR - '0'); |
|
8073 NEXT; |
|
8074 } |
|
8075 if (is_exponent_negative) |
|
8076 exponent = -exponent; |
|
8077 ret *= pow(10.0, (double) exponent); |
|
8078 } |
|
8079 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, |
|
8080 xmlXPathNewFloat(ret), NULL); |
|
8081 } |
|
8082 |
|
8083 /** |
|
8084 * xmlXPathParseLiteral: |
|
8085 * @param ctxt the XPath Parser context |
|
8086 * |
|
8087 * Parse a Literal |
|
8088 * |
|
8089 * [29] Literal ::= '"' [^"]* '"' |
|
8090 * | "'" [^']* "'" |
|
8091 * |
|
8092 * Returns the value found or NULL in case of error |
|
8093 * OOM: possible --> NULL is returned; parser is set into error state |
|
8094 */ |
|
8095 static xmlChar* |
|
8096 xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) |
|
8097 { |
|
8098 const xmlChar *q; |
|
8099 xmlChar* ret; |
|
8100 const xmlChar quote = CUR; |
|
8101 // DONE: Optimize: cases with ' and " have almost identical code ==> combine |
|
8102 if (quote == '"' || quote == '\'') { |
|
8103 NEXT; |
|
8104 q = CUR_PTR; |
|
8105 while ((IS_CHAR_CH(CUR)) && (CUR != quote)) |
|
8106 { |
|
8107 NEXT; |
|
8108 } |
|
8109 if (!IS_CHAR_CH(CUR)) { |
|
8110 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR); |
|
8111 } else { |
|
8112 ret = xmlStrndup(q, CUR_PTR - q); |
|
8113 if(!ret){ |
|
8114 XP_ERROR0(XPATH_MEMORY_ERROR); |
|
8115 } |
|
8116 NEXT; |
|
8117 return ret; |
|
8118 } |
|
8119 } else { |
|
8120 XP_ERROR0(XPATH_START_LITERAL_ERROR); |
|
8121 } |
|
8122 } |
|
8123 |
|
8124 /** |
|
8125 * xmlXPathCompLiteral: |
|
8126 * @param ctxt the XPath Parser context |
|
8127 * |
|
8128 * Parse a Literal and push it on the stack. |
|
8129 * |
|
8130 * [29] Literal ::= '"' [^"]* '"' |
|
8131 * | "'" [^']* "'" |
|
8132 * |
|
8133 * |
|
8134 */ |
|
8135 static void |
|
8136 xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) { |
|
8137 const xmlChar *q; |
|
8138 xmlChar *ret = NULL; |
|
8139 |
|
8140 if (CUR == '"') { |
|
8141 NEXT; |
|
8142 q = CUR_PTR; |
|
8143 while ((IS_CHAR_CH(CUR)) && (CUR != '"')) |
|
8144 NEXT; |
|
8145 if (!IS_CHAR_CH(CUR)) { |
|
8146 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); |
|
8147 } else { |
|
8148 ret = xmlStrndup(q, CUR_PTR - q); |
|
8149 NEXT; |
|
8150 } |
|
8151 } else if (CUR == '\'') { |
|
8152 NEXT; |
|
8153 q = CUR_PTR; |
|
8154 while ((IS_CHAR_CH(CUR)) && (CUR != '\'')) |
|
8155 NEXT; |
|
8156 if (!IS_CHAR_CH(CUR)) { |
|
8157 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); |
|
8158 } else { |
|
8159 ret = xmlStrndup(q, CUR_PTR - q); |
|
8160 NEXT; |
|
8161 } |
|
8162 } else { |
|
8163 XP_ERROR(XPATH_START_LITERAL_ERROR); |
|
8164 } |
|
8165 if (ret == NULL) return; |
|
8166 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, |
|
8167 xmlXPathNewString(ret), NULL); |
|
8168 xmlFree(ret); |
|
8169 } |
|
8170 |
|
8171 /** |
|
8172 * xmlXPathCompVariableReference: |
|
8173 * @param ctxt the XPath Parser context |
|
8174 * |
|
8175 * Parse a VariableReference, evaluate it and push it on the stack. |
|
8176 * |
|
8177 * The variable bindings consist of a mapping from variable names |
|
8178 * to variable values. The value of a variable is an object, which can be |
|
8179 * of any of the types that are possible for the value of an expression, |
|
8180 * and may also be of additional types not specified here. |
|
8181 * |
|
8182 * Early evaluation is possible since: |
|
8183 * The variable bindings [...] used to evaluate a subexpression are |
|
8184 * always the same as those used to evaluate the containing expression. |
|
8185 * |
|
8186 * [36] VariableReference ::= '$' QName |
|
8187 */ |
|
8188 static void |
|
8189 xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) { |
|
8190 xmlChar *name; |
|
8191 xmlChar *prefix; |
|
8192 |
|
8193 SKIP_BLANKS; |
|
8194 if (CUR != '$') { |
|
8195 XP_ERROR(XPATH_VARIABLE_REF_ERROR); |
|
8196 } |
|
8197 NEXT; |
|
8198 name = xmlXPathParseQName(ctxt, &prefix); // OOM possible |
|
8199 if (!name) { |
|
8200 XP_ERROR(XPATH_VARIABLE_REF_ERROR); |
|
8201 } |
|
8202 ctxt->comp->last = -1; |
|
8203 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, name, prefix); |
|
8204 SKIP_BLANKS; |
|
8205 } |
|
8206 |
|
8207 /** |
|
8208 * xmlXPathIsNodeType: |
|
8209 * @param name a name string |
|
8210 * |
|
8211 * Is the name given a NodeType one. |
|
8212 * |
|
8213 * [38] NodeType ::= 'comment' |
|
8214 * | 'text' |
|
8215 * | 'processing-instruction' |
|
8216 * | 'node' |
|
8217 * |
|
8218 * Returns 1 if true 0 otherwise |
|
8219 */ |
|
8220 XMLPUBFUNEXPORT int |
|
8221 xmlXPathIsNodeType(const xmlChar *name) { |
|
8222 if (name == NULL) |
|
8223 return(0); |
|
8224 |
|
8225 if (xmlStrEqual(name, BAD_CAST "node")) |
|
8226 return(1); |
|
8227 if (xmlStrEqual(name, BAD_CAST "text")) |
|
8228 return(1); |
|
8229 if (xmlStrEqual(name, BAD_CAST "comment")) |
|
8230 return(1); |
|
8231 if (xmlStrEqual(name, BAD_CAST "processing-instruction")) |
|
8232 return(1); |
|
8233 return(0); |
|
8234 } |
|
8235 |
|
8236 /** |
|
8237 * xmlXPathCompFunctionCall: |
|
8238 * @param ctxt the XPath Parser context |
|
8239 * |
|
8240 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')' |
|
8241 * [17] Argument ::= Expr |
|
8242 * |
|
8243 * Compile a function call, the evaluation of all arguments are |
|
8244 * pushed on the stack |
|
8245 */ |
|
8246 static void |
|
8247 xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) { |
|
8248 xmlChar *name; |
|
8249 xmlChar *prefix; // name and prefix to be freed if abnormal return occurs |
|
8250 int nbargs = 0; |
|
8251 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8252 |
|
8253 name = xmlXPathParseQName(ctxt, &prefix); |
|
8254 if (name == NULL) { |
|
8255 XP_ERROR(XPATH_EXPR_ERROR); |
|
8256 } |
|
8257 SKIP_BLANKS; |
|
8258 #ifdef DEBUG_EXPR |
|
8259 if (prefix == NULL) |
|
8260 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n", |
|
8261 name); |
|
8262 else |
|
8263 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n", |
|
8264 prefix, name); |
|
8265 #endif |
|
8266 |
|
8267 if (CUR != '(') |
|
8268 goto EXPR_Err; |
|
8269 |
|
8270 NEXT; |
|
8271 SKIP_BLANKS; |
|
8272 |
|
8273 ctxt->comp->last = -1; |
|
8274 if (CUR != ')') { |
|
8275 while (CUR != 0) { |
|
8276 int op1 = ctxt->comp->last; |
|
8277 ctxt->comp->last = -1; |
|
8278 xmlXPathCompileExpr(ctxt); |
|
8279 |
|
8280 // CHECK_ERROR; replaced with: |
|
8281 if(ctxt->error != XPATH_EXPRESSION_OK || OOM_FLAG) |
|
8282 goto Err; |
|
8283 |
|
8284 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0); |
|
8285 nbargs++; |
|
8286 if (CUR == ')') |
|
8287 break; |
|
8288 if (CUR != ',') |
|
8289 goto EXPR_Err; |
|
8290 |
|
8291 NEXT; |
|
8292 SKIP_BLANKS; |
|
8293 } |
|
8294 } |
|
8295 // Note: now name and prefix are part of compiled expression |
|
8296 if ( PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1) |
|
8297 goto Err; |
|
8298 NEXT; |
|
8299 SKIP_BLANKS; |
|
8300 return; |
|
8301 //------- |
|
8302 EXPR_Err: |
|
8303 xmlXPathErr(ctxt, XPATH_EXPR_ERROR); |
|
8304 Err: |
|
8305 if(name) |
|
8306 xmlFree(name); |
|
8307 if(prefix) |
|
8308 xmlFree(prefix); |
|
8309 } |
|
8310 |
|
8311 /** |
|
8312 * xmlXPathCompPrimaryExpr: |
|
8313 * @param ctxt the XPath Parser context |
|
8314 * |
|
8315 * [15] PrimaryExpr ::= VariableReference |
|
8316 * | '(' Expr ')' |
|
8317 * | Literal |
|
8318 * | Number |
|
8319 * | FunctionCall |
|
8320 * |
|
8321 * Compile a primary expression. |
|
8322 */ |
|
8323 static void |
|
8324 xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) |
|
8325 { |
|
8326 SKIP_BLANKS; |
|
8327 if (CUR == '$') |
|
8328 { |
|
8329 xmlXPathCompVariableReference(ctxt); |
|
8330 } |
|
8331 else if (CUR == '(') |
|
8332 { |
|
8333 NEXT; |
|
8334 SKIP_BLANKS; |
|
8335 xmlXPathCompileExpr(ctxt); |
|
8336 CHECK_ERROR; |
|
8337 if (CUR != ')') { |
|
8338 XP_ERROR(XPATH_EXPR_ERROR); |
|
8339 } |
|
8340 NEXT; |
|
8341 SKIP_BLANKS; |
|
8342 } |
|
8343 else if (IS_DIGIT_CH(CUR) || (CUR == '.' && IS_DIGIT_CH(NXT(1)))) |
|
8344 { |
|
8345 xmlXPathCompNumber(ctxt); |
|
8346 } |
|
8347 else if ((CUR == '\'') || (CUR == '"')) |
|
8348 { |
|
8349 xmlXPathCompLiteral(ctxt); |
|
8350 } |
|
8351 else |
|
8352 { |
|
8353 xmlXPathCompFunctionCall(ctxt); |
|
8354 } |
|
8355 SKIP_BLANKS; |
|
8356 } |
|
8357 |
|
8358 /** |
|
8359 * xmlXPathCompFilterExpr: |
|
8360 * @param ctxt the XPath Parser context |
|
8361 * |
|
8362 * [20] FilterExpr ::= PrimaryExpr |
|
8363 * | FilterExpr Predicate |
|
8364 * |
|
8365 * Compile a filter expression. |
|
8366 * Square brackets are used to filter expressions in the same way that |
|
8367 * they are used in location paths. It is an error if the expression to |
|
8368 * be filtered does not evaluate to a node-set. The context node list |
|
8369 * used for evaluating the expression in square brackets is the node-set |
|
8370 * to be filtered listed in document order. |
|
8371 */ |
|
8372 |
|
8373 static void |
|
8374 xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) |
|
8375 { |
|
8376 xmlXPathCompPrimaryExpr(ctxt); |
|
8377 CHECK_ERROR; |
|
8378 SKIP_BLANKS; |
|
8379 |
|
8380 while (CUR == '[') { |
|
8381 xmlXPathCompPredicate(ctxt, 1); |
|
8382 SKIP_BLANKS; |
|
8383 } |
|
8384 } |
|
8385 |
|
8386 /** |
|
8387 * xmlXPathScanName: |
|
8388 * @param ctxt the XPath Parser context |
|
8389 * |
|
8390 * Trickery: parse an XML name but without consuming the input flow |
|
8391 * Needed to avoid insanity in the parser state. |
|
8392 * |
|
8393 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | |
|
8394 * CombiningChar | Extender |
|
8395 * |
|
8396 * [5] Name ::= (Letter | '_' | ':') (NameChar)* |
|
8397 * |
|
8398 * [6] Names ::= Name (S Name)* |
|
8399 * |
|
8400 * Returns the Name parsed or NULL |
|
8401 * |
|
8402 * OOM: possible --> always check OOM flag! |
|
8403 */ |
|
8404 |
|
8405 static xmlChar* |
|
8406 xmlXPathScanName(xmlXPathParserContextPtr ctxt) { |
|
8407 xmlChar buf[XML_MAX_NAMELEN]; |
|
8408 int len = 0; |
|
8409 |
|
8410 SKIP_BLANKS; |
|
8411 if (!IS_LETTER_CH(CUR) && |
|
8412 (CUR != '_') && |
|
8413 (CUR != ':')) |
|
8414 { |
|
8415 return(NULL); |
|
8416 } |
|
8417 |
|
8418 while ((IS_LETTER_CH(NXT(len))) || |
|
8419 (IS_DIGIT_CH(NXT(len))) || |
|
8420 (NXT(len) == '.') || |
|
8421 (NXT(len) == '-') || |
|
8422 (NXT(len) == '_') || |
|
8423 (NXT(len) == ':') || |
|
8424 (IS_COMBINING_CH(NXT(len))) || |
|
8425 (IS_EXTENDER_CH(NXT(len)))) |
|
8426 { |
|
8427 buf[len] = NXT(len); |
|
8428 len++; |
|
8429 if (len >= XML_MAX_NAMELEN) |
|
8430 { |
|
8431 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlScanName: reached XML_MAX_NAMELEN limit\n")); |
|
8432 while ( |
|
8433 (IS_LETTER_CH(NXT(len))) || |
|
8434 (IS_DIGIT_CH(NXT(len))) || |
|
8435 (NXT(len) == '.') || |
|
8436 (NXT(len) == '-') || |
|
8437 (NXT(len) == '_') || |
|
8438 (NXT(len) == ':') || |
|
8439 (IS_COMBINING_CH(NXT(len))) || |
|
8440 (IS_EXTENDER_CH(NXT(len)))) |
|
8441 { |
|
8442 len++; |
|
8443 } |
|
8444 break; // while |
|
8445 } |
|
8446 } |
|
8447 return(xmlStrndup(buf, len)); // may cause OOM |
|
8448 } |
|
8449 |
|
8450 /** |
|
8451 * xmlXPathCompPathExpr: |
|
8452 * @param ctxt the XPath Parser context |
|
8453 * |
|
8454 * [19] PathExpr ::= LocationPath |
|
8455 * | FilterExpr |
|
8456 * | FilterExpr '/' RelativeLocationPath |
|
8457 * | FilterExpr '//' RelativeLocationPath |
|
8458 * |
|
8459 * Compile a path expression. |
|
8460 * The / operator and // operators combine an arbitrary expression |
|
8461 * and a relative location path. It is an error if the expression |
|
8462 * does not evaluate to a node-set. |
|
8463 * The / operator does composition in the same way as when / is |
|
8464 * used in a location path. As in location paths, // is short for |
|
8465 * /descendant-or-self::node()/. |
|
8466 * |
|
8467 * OOM: |
|
8468 */ |
|
8469 |
|
8470 static void |
|
8471 xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) { |
|
8472 int lc = 1; /* Should we branch to LocationPath ? */ |
|
8473 xmlChar *name = NULL; /* we may have to preparse a name to find out */ |
|
8474 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8475 |
|
8476 SKIP_BLANKS; |
|
8477 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT_CH(CUR)) || |
|
8478 (CUR == '\'') || (CUR == '"') || (CUR == '.' && IS_DIGIT_CH(NXT(1)))) |
|
8479 { |
|
8480 lc = 0; |
|
8481 } else if (CUR == '*') { |
|
8482 /* relative or absolute location path */ |
|
8483 lc = 1; |
|
8484 } else if (CUR == '/') { |
|
8485 /* relative or absolute location path */ |
|
8486 lc = 1; |
|
8487 } else if (CUR == '@') { |
|
8488 /* relative abbreviated attribute location path */ |
|
8489 lc = 1; |
|
8490 } else if (CUR == '.') { |
|
8491 /* relative abbreviated attribute location path */ |
|
8492 lc = 1; |
|
8493 } else { |
|
8494 /* |
|
8495 * Problem is finding if we have a name here whether it's: |
|
8496 * - a nodetype |
|
8497 * - a function call in which case it's followed by '(' |
|
8498 * - an axis in which case it's followed by ':' |
|
8499 * - a element name |
|
8500 * We do an a priori analysis here rather than having to |
|
8501 * maintain parsed token content through the recursive function |
|
8502 * calls. This looks uglier but makes the code easier to |
|
8503 * read/write/debug. |
|
8504 */ |
|
8505 SKIP_BLANKS; |
|
8506 name = xmlXPathScanName(ctxt); |
|
8507 // Note: if OOM happened then it is handled in the last ELSE branch: |
|
8508 if (name && (xmlStrstr(name, (xmlChar *) "::") != NULL)) { |
|
8509 #ifdef DEBUG_STEP |
|
8510 xmlGenericError(xmlGenericErrorContext, "PathExpr: Axis\n"); |
|
8511 #endif |
|
8512 lc = 1; |
|
8513 xmlFree(name); |
|
8514 } else if (name != NULL) { |
|
8515 int len =xmlStrlen(name); |
|
8516 |
|
8517 |
|
8518 while (NXT(len) != 0) { |
|
8519 if (NXT(len) == '/') { |
|
8520 /* element name */ |
|
8521 #ifdef DEBUG_STEP |
|
8522 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8523 #endif |
|
8524 lc = 1; |
|
8525 break; |
|
8526 } else if (IS_BLANK_CH(NXT(len))) { |
|
8527 /* ignore blanks */ |
|
8528 ; |
|
8529 } else if (NXT(len) == ':') { |
|
8530 #ifdef DEBUG_STEP |
|
8531 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8532 #endif |
|
8533 lc = 1; |
|
8534 break; |
|
8535 } else if ((NXT(len) == '(')) { |
|
8536 /* Note Type or Function */ |
|
8537 if (xmlXPathIsNodeType(name)) { |
|
8538 #ifdef DEBUG_STEP |
|
8539 xmlGenericError(xmlGenericErrorContext, "PathExpr: Type search\n"); |
|
8540 #endif |
|
8541 lc = 1; |
|
8542 } else { |
|
8543 #ifdef DEBUG_STEP |
|
8544 xmlGenericError(xmlGenericErrorContext, "PathExpr: function call\n"); |
|
8545 #endif |
|
8546 lc = 0; |
|
8547 } |
|
8548 break; |
|
8549 } else if ((NXT(len) == '[')) { |
|
8550 /* element name */ |
|
8551 #ifdef DEBUG_STEP |
|
8552 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8553 #endif |
|
8554 lc = 1; |
|
8555 break; |
|
8556 } else if ((NXT(len) == '<') || |
|
8557 (NXT(len) == '>') || |
|
8558 (NXT(len) == '=')) |
|
8559 { |
|
8560 lc = 1; |
|
8561 break; |
|
8562 } else { |
|
8563 lc = 1; |
|
8564 break; |
|
8565 } |
|
8566 len++; |
|
8567 } |
|
8568 if (NXT(len) == 0) { |
|
8569 #ifdef DEBUG_STEP |
|
8570 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8571 #endif |
|
8572 /* element name */ |
|
8573 lc = 1; |
|
8574 } |
|
8575 xmlFree(name); |
|
8576 } else { |
|
8577 // name is NULL -- OOM or bad XPath expression |
|
8578 /* make sure all cases are covered explicitly */ |
|
8579 if(OOM_FLAG){ |
|
8580 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8581 } else { |
|
8582 XP_ERROR(XPATH_EXPR_ERROR); |
|
8583 } |
|
8584 } |
|
8585 } |
|
8586 |
|
8587 if (lc) { |
|
8588 if (CUR == '/') { |
|
8589 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0); |
|
8590 } else { |
|
8591 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0); |
|
8592 } |
|
8593 xmlXPathCompLocationPath(ctxt); |
|
8594 |
|
8595 if(OOM_FLAG) |
|
8596 XP_ERROR(XPATH_MEMORY_ERROR) |
|
8597 } else { |
|
8598 xmlXPathCompFilterExpr(ctxt); |
|
8599 CHECK_ERROR; |
|
8600 if ((CUR == '/') && (NXT(1) == '/')) |
|
8601 { |
|
8602 SKIP(2); |
|
8603 SKIP_BLANKS; |
|
8604 |
|
8605 PUSH_LONG_EXPR( |
|
8606 XPATH_OP_COLLECT, |
|
8607 AXIS_DESCENDANT_OR_SELF, |
|
8608 NODE_TEST_TYPE, |
|
8609 NODE_TYPE_NODE, |
|
8610 NULL, |
|
8611 NULL); |
|
8612 |
|
8613 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0); |
|
8614 |
|
8615 xmlXPathCompRelativeLocationPath(ctxt); |
|
8616 } else if (CUR == '/') { |
|
8617 xmlXPathCompRelativeLocationPath(ctxt); |
|
8618 } |
|
8619 } |
|
8620 SKIP_BLANKS; |
|
8621 } |
|
8622 |
|
8623 /** |
|
8624 * xmlXPathCompUnionExpr: |
|
8625 * @param ctxt the XPath Parser context |
|
8626 * |
|
8627 * [18] UnionExpr ::= PathExpr |
|
8628 * | UnionExpr '|' PathExpr |
|
8629 * |
|
8630 * Compile an union expression. |
|
8631 * |
|
8632 * OOM: |
|
8633 */ |
|
8634 |
|
8635 static void |
|
8636 xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) { |
|
8637 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8638 xmlXPathCompPathExpr(ctxt); |
|
8639 CHECK_ERROR; |
|
8640 if(OOM_FLAG){ |
|
8641 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8642 } |
|
8643 SKIP_BLANKS; |
|
8644 while (CUR == '|') { |
|
8645 int op1 = ctxt->comp->last; |
|
8646 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0); |
|
8647 |
|
8648 NEXT; |
|
8649 SKIP_BLANKS; |
|
8650 xmlXPathCompPathExpr(ctxt); |
|
8651 |
|
8652 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0); |
|
8653 |
|
8654 SKIP_BLANKS; |
|
8655 } |
|
8656 } |
|
8657 |
|
8658 /** |
|
8659 * xmlXPathCompUnaryExpr: |
|
8660 * @param ctxt the XPath Parser context |
|
8661 * |
|
8662 * [27] UnaryExpr ::= UnionExpr |
|
8663 * | '-' UnaryExpr |
|
8664 * |
|
8665 * Compile an unary expression. |
|
8666 * |
|
8667 * OOM: |
|
8668 */ |
|
8669 |
|
8670 static void |
|
8671 xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) { |
|
8672 int minus = 0; |
|
8673 int found = 0; |
|
8674 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8675 |
|
8676 SKIP_BLANKS; |
|
8677 while (CUR == '-') { |
|
8678 minus = 1 - minus; |
|
8679 found = 1; |
|
8680 NEXT; |
|
8681 SKIP_BLANKS; |
|
8682 } |
|
8683 |
|
8684 xmlXPathCompUnionExpr(ctxt); |
|
8685 CHECK_ERROR; |
|
8686 if(OOM_FLAG){ |
|
8687 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8688 } |
|
8689 if (found) { |
|
8690 if (minus) |
|
8691 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0); |
|
8692 else |
|
8693 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0); |
|
8694 } |
|
8695 } |
|
8696 |
|
8697 /** |
|
8698 * xmlXPathCompMultiplicativeExpr: |
|
8699 * @param ctxt the XPath Parser context |
|
8700 * |
|
8701 * [26] MultiplicativeExpr ::= UnaryExpr |
|
8702 * | MultiplicativeExpr MultiplyOperator UnaryExpr |
|
8703 * | MultiplicativeExpr 'div' UnaryExpr |
|
8704 * | MultiplicativeExpr 'mod' UnaryExpr |
|
8705 * [34] MultiplyOperator ::= '*' |
|
8706 * |
|
8707 * Compile an Additive expression. |
|
8708 * |
|
8709 * OOM: |
|
8710 */ |
|
8711 |
|
8712 static void |
|
8713 xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) { |
|
8714 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8715 xmlXPathCompUnaryExpr(ctxt); |
|
8716 CHECK_ERROR; |
|
8717 if(OOM_FLAG){ |
|
8718 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8719 } |
|
8720 SKIP_BLANKS; |
|
8721 while ((CUR == '*') || |
|
8722 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) || |
|
8723 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) { |
|
8724 int op = -1; |
|
8725 int op1 = ctxt->comp->last; |
|
8726 |
|
8727 if (CUR == '*') { |
|
8728 op = 0; |
|
8729 NEXT; |
|
8730 } else if (CUR == 'd') { |
|
8731 op = 1; |
|
8732 SKIP(3); |
|
8733 } else if (CUR == 'm') { |
|
8734 op = 2; |
|
8735 SKIP(3); |
|
8736 } |
|
8737 SKIP_BLANKS; |
|
8738 xmlXPathCompUnaryExpr(ctxt); |
|
8739 CHECK_ERROR; |
|
8740 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0); |
|
8741 SKIP_BLANKS; |
|
8742 } |
|
8743 } |
|
8744 |
|
8745 /** |
|
8746 * xmlXPathCompAdditiveExpr: |
|
8747 * @param ctxt the XPath Parser context |
|
8748 * |
|
8749 * [25] AdditiveExpr ::= MultiplicativeExpr |
|
8750 * | AdditiveExpr '+' MultiplicativeExpr |
|
8751 * | AdditiveExpr '-' MultiplicativeExpr |
|
8752 * |
|
8753 * Compile an Additive expression. |
|
8754 * |
|
8755 * OOM: |
|
8756 */ |
|
8757 |
|
8758 static void |
|
8759 xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) { |
|
8760 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8761 |
|
8762 xmlXPathCompMultiplicativeExpr(ctxt); |
|
8763 CHECK_ERROR; |
|
8764 if(OOM_FLAG){ |
|
8765 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8766 } |
|
8767 SKIP_BLANKS; |
|
8768 while ((CUR == '+') || (CUR == '-')) { |
|
8769 int plus; |
|
8770 int op1 = ctxt->comp->last; |
|
8771 |
|
8772 if (CUR == '+') |
|
8773 plus = 1; |
|
8774 else |
|
8775 plus = 0; |
|
8776 NEXT; |
|
8777 SKIP_BLANKS; |
|
8778 xmlXPathCompMultiplicativeExpr(ctxt); |
|
8779 CHECK_ERROR; |
|
8780 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0); |
|
8781 SKIP_BLANKS; |
|
8782 } |
|
8783 } |
|
8784 |
|
8785 /** |
|
8786 * xmlXPathCompRelationalExpr: |
|
8787 * @param ctxt the XPath Parser context |
|
8788 * |
|
8789 * [24] RelationalExpr ::= AdditiveExpr |
|
8790 * | RelationalExpr '<' AdditiveExpr |
|
8791 * | RelationalExpr '>' AdditiveExpr |
|
8792 * | RelationalExpr '<=' AdditiveExpr |
|
8793 * | RelationalExpr '>=' AdditiveExpr |
|
8794 * |
|
8795 * A <= B > C is allowed ? Answer from James, yes with |
|
8796 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr |
|
8797 * which is basically what got implemented. |
|
8798 * |
|
8799 * Compile a Relational expression, then push the result |
|
8800 * on the stack |
|
8801 * |
|
8802 * OOM: |
|
8803 */ |
|
8804 |
|
8805 static void |
|
8806 xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) |
|
8807 { |
|
8808 xmlChar ch; |
|
8809 int* last; |
|
8810 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8811 // XML ENGINE: Changes/optimization were applied here |
|
8812 xmlXPathCompAdditiveExpr(ctxt); |
|
8813 CHECK_ERROR; |
|
8814 if(OOM_FLAG){ |
|
8815 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8816 } |
|
8817 SKIP_BLANKS; |
|
8818 last = &(ctxt->comp->last); |
|
8819 // NOTE: this loop allows expressions like A > B <= C < D .. etc. |
|
8820 // which will be evaluated in a way that may be unexpected: |
|
8821 // e.g. the example above is evaluated as |
|
8822 // ((A > B) <= C) < D with boolean results within parentheses |
|
8823 // converted to 1 or 0 (from 'true' and 'false') |
|
8824 |
|
8825 for(;;) |
|
8826 { |
|
8827 ch = *ctxt->cur; |
|
8828 if (ch == '<' || ch == '>') |
|
8829 { |
|
8830 int op1 = *last; |
|
8831 int strict = (*(++(ctxt->cur)) == '=') ? 0 : 1; // evaluate 'strict' and NEXT |
|
8832 if (!strict) |
|
8833 ctxt->cur++; // NEXT again if '=' |
|
8834 |
|
8835 SKIP_BLANKS; |
|
8836 xmlXPathCompAdditiveExpr(ctxt); |
|
8837 CHECK_ERROR; |
|
8838 PUSH_BINARY_EXPR( |
|
8839 XPATH_OP_CMP, |
|
8840 op1, |
|
8841 *last, |
|
8842 (ch == '<') & 1, |
|
8843 strict); |
|
8844 SKIP_BLANKS; |
|
8845 } |
|
8846 else |
|
8847 return; |
|
8848 } // for(;;) |
|
8849 } |
|
8850 |
|
8851 /** |
|
8852 * xmlXPathCompEqualityExpr: |
|
8853 * @param ctxt the XPath Parser context |
|
8854 * |
|
8855 * [23] EqualityExpr ::= RelationalExpr |
|
8856 * | EqualityExpr '=' RelationalExpr |
|
8857 * | EqualityExpr '!=' RelationalExpr |
|
8858 * |
|
8859 * A != B != C is allowed ? Answer from James, yes with |
|
8860 * (RelationalExpr = RelationalExpr) = RelationalExpr |
|
8861 * (RelationalExpr != RelationalExpr) != RelationalExpr |
|
8862 * which is basically what got implemented. |
|
8863 * |
|
8864 * Compile an Equality expression. |
|
8865 * |
|
8866 * OOM: |
|
8867 */ |
|
8868 static void |
|
8869 xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) |
|
8870 { |
|
8871 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8872 xmlXPathCompRelationalExpr(ctxt); |
|
8873 CHECK_ERROR; |
|
8874 if(OOM_FLAG){ |
|
8875 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8876 } |
|
8877 SKIP_BLANKS; |
|
8878 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) { |
|
8879 int eq; |
|
8880 int op1 = ctxt->comp->last; |
|
8881 |
|
8882 eq = (CUR == '=') ? 1 : 0; |
|
8883 NEXT; |
|
8884 if (!eq) NEXT; |
|
8885 SKIP_BLANKS; |
|
8886 xmlXPathCompRelationalExpr(ctxt); |
|
8887 CHECK_ERROR; |
|
8888 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0); |
|
8889 SKIP_BLANKS; |
|
8890 } |
|
8891 } |
|
8892 |
|
8893 /** |
|
8894 * xmlXPathCompAndExpr: |
|
8895 * @param ctxt the XPath Parser context |
|
8896 * |
|
8897 * [22] AndExpr ::= EqualityExpr |
|
8898 * | AndExpr 'and' EqualityExpr |
|
8899 * |
|
8900 * Compile an AND expression. |
|
8901 * |
|
8902 * OOM: |
|
8903 */ |
|
8904 static void |
|
8905 xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) { |
|
8906 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8907 xmlXPathCompEqualityExpr(ctxt); |
|
8908 CHECK_ERROR; |
|
8909 if(OOM_FLAG){ |
|
8910 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8911 } |
|
8912 SKIP_BLANKS; |
|
8913 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) { |
|
8914 int op1 = ctxt->comp->last; |
|
8915 SKIP(3); |
|
8916 SKIP_BLANKS; |
|
8917 xmlXPathCompEqualityExpr(ctxt); |
|
8918 CHECK_ERROR; |
|
8919 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0); |
|
8920 SKIP_BLANKS; |
|
8921 } |
|
8922 } |
|
8923 |
|
8924 #define XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS |
|
8925 /** |
|
8926 * xmlXPathCompileExpr: |
|
8927 * @param ctxt the XPath Parser context |
|
8928 * |
|
8929 * [14] Expr ::= OrExpr |
|
8930 * [21] OrExpr ::= AndExpr |
|
8931 * | OrExpr 'or' AndExpr |
|
8932 * |
|
8933 * Parse and compile an expression |
|
8934 * |
|
8935 * OOM: possible --> check OOM flag //// |
|
8936 */ |
|
8937 static void |
|
8938 xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) |
|
8939 { |
|
8940 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
8941 xmlXPathCompAndExpr(ctxt); |
|
8942 CHECK_ERROR; |
|
8943 if(OOM_FLAG){ |
|
8944 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8945 } |
|
8946 SKIP_BLANKS; |
|
8947 while ((CUR == 'o') && (NXT(1) == 'r')) { |
|
8948 int op1 = ctxt->comp->last; |
|
8949 SKIP(2); |
|
8950 SKIP_BLANKS; |
|
8951 xmlXPathCompAndExpr(ctxt); |
|
8952 CHECK_ERROR; |
|
8953 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0); |
|
8954 op1 = ctxt->comp->nbStep; |
|
8955 SKIP_BLANKS; |
|
8956 } |
|
8957 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) { |
|
8958 /* more ops could be optimized too */ |
|
8959 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0); |
|
8960 } |
|
8961 |
|
8962 #ifdef XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS |
|
8963 // Free unused memory in the tail of ctxt->comp->steps array |
|
8964 { |
|
8965 xmlXPathStepOp* tmp; |
|
8966 // DONE: OPTIMIZE: free the rest of preallocated step table |
|
8967 // This is a exeprimental code in XML Engine |
|
8968 // DONE: Fix xmlRealloc (Note: OOM should not happen since the size is smaller, but...) |
|
8969 tmp = (xmlXPathStepOp*)xmlRealloc(ctxt->comp->steps, sizeof(xmlXPathStepOp) * ctxt->comp->nbStep); |
|
8970 if(tmp) |
|
8971 { |
|
8972 ctxt->comp->steps = tmp; |
|
8973 ctxt->comp->maxStep = ctxt->comp->nbStep; |
|
8974 } |
|
8975 } |
|
8976 #endif |
|
8977 // |
|
8978 } |
|
8979 |
|
8980 /** |
|
8981 * xmlXPathCompPredicate: |
|
8982 * @param ctxt the XPath Parser context |
|
8983 * @param filter act as a filter |
|
8984 * |
|
8985 * [8] Predicate ::= '[' PredicateExpr ']' |
|
8986 * [9] PredicateExpr ::= Expr |
|
8987 * |
|
8988 * Compile a predicate expression |
|
8989 */ |
|
8990 static void |
|
8991 xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) { |
|
8992 int op1 = ctxt->comp->last; |
|
8993 |
|
8994 SKIP_BLANKS; |
|
8995 if (CUR != '[') { |
|
8996 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); |
|
8997 } |
|
8998 NEXT; |
|
8999 SKIP_BLANKS; |
|
9000 |
|
9001 ctxt->comp->last = -1; |
|
9002 xmlXPathCompileExpr(ctxt); |
|
9003 CHECK_ERROR; |
|
9004 |
|
9005 if (CUR != ']') { |
|
9006 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); |
|
9007 } |
|
9008 |
|
9009 if (filter){ |
|
9010 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0); |
|
9011 }else{ |
|
9012 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0); |
|
9013 } |
|
9014 NEXT; |
|
9015 SKIP_BLANKS; |
|
9016 } |
|
9017 |
|
9018 /** |
|
9019 * xmlXPathCompNodeTest: |
|
9020 * @param ctxt the XPath Parser context |
|
9021 * @param test pointer to a xmlXPathTestVal |
|
9022 * @param type pointer to a xmlXPathTypeVal |
|
9023 * @param prefix placeholder for a possible name prefix |
|
9024 * |
|
9025 * [7] NodeTest ::= NameTest |
|
9026 * | NodeType '(' ')' |
|
9027 * | 'processing-instruction' '(' Literal ')' |
|
9028 * |
|
9029 * [37] NameTest ::= '*' |
|
9030 * | NCName ':' '*' |
|
9031 * | QName |
|
9032 * [38] NodeType ::= 'comment' |
|
9033 * | 'text' |
|
9034 * | 'processing-instruction' |
|
9035 * | 'node' |
|
9036 * |
|
9037 * Returns the name found and updates test, type and prefix appropriately |
|
9038 */ |
|
9039 static xmlChar * |
|
9040 xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, |
|
9041 xmlXPathTypeVal *type, const xmlChar **prefix, |
|
9042 xmlChar *name) |
|
9043 { |
|
9044 int blanks; |
|
9045 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
9046 |
|
9047 if ((test == NULL) || (type == NULL) || (prefix == NULL)) { |
|
9048 STRANGE; |
|
9049 return(NULL); |
|
9050 } |
|
9051 *type = (xmlXPathTypeVal) 0; |
|
9052 *test = (xmlXPathTestVal) 0; |
|
9053 *prefix = NULL; |
|
9054 SKIP_BLANKS; |
|
9055 |
|
9056 if ((name == NULL) && (CUR == '*')) { |
|
9057 /* |
|
9058 * All elements |
|
9059 */ |
|
9060 NEXT; |
|
9061 *test = NODE_TEST_ALL; |
|
9062 return(NULL); |
|
9063 } |
|
9064 |
|
9065 if (!name){ |
|
9066 name = xmlXPathParseNCName(ctxt); |
|
9067 if (!name){ |
|
9068 if(OOM_FLAG){ |
|
9069 XP_ERROR0(XPATH_MEMORY_ERROR); |
|
9070 } else { |
|
9071 XP_ERROR0(XPATH_EXPR_ERROR); |
|
9072 } |
|
9073 } |
|
9074 } |
|
9075 |
|
9076 blanks = IS_BLANK_CH(CUR); |
|
9077 SKIP_BLANKS; |
|
9078 if (CUR == '(') |
|
9079 { |
|
9080 NEXT; |
|
9081 /* |
|
9082 * NodeType or PI search |
|
9083 */ |
|
9084 if (xmlStrEqual(name, BAD_CAST "comment")) |
|
9085 *type = NODE_TYPE_COMMENT; |
|
9086 else if (xmlStrEqual(name, BAD_CAST "node")) |
|
9087 *type = NODE_TYPE_NODE; |
|
9088 else if (xmlStrEqual(name, BAD_CAST "processing-instruction")) |
|
9089 *type = NODE_TYPE_PI; |
|
9090 else if (xmlStrEqual(name, BAD_CAST "text")) |
|
9091 *type = NODE_TYPE_TEXT; |
|
9092 else { |
|
9093 if (name != NULL) |
|
9094 xmlFree(name); |
|
9095 XP_ERROR0(XPATH_EXPR_ERROR); |
|
9096 } |
|
9097 |
|
9098 *test = NODE_TEST_TYPE; |
|
9099 |
|
9100 SKIP_BLANKS; |
|
9101 if (*type == NODE_TYPE_PI) { |
|
9102 /* |
|
9103 * Specific case: search a PI by name. |
|
9104 */ |
|
9105 if (name) |
|
9106 xmlFree(name); |
|
9107 name = NULL; |
|
9108 if (CUR != ')') { |
|
9109 name = xmlXPathParseLiteral(ctxt); |
|
9110 CHECK_ERROR 0; |
|
9111 *test = NODE_TEST_PI; |
|
9112 SKIP_BLANKS; |
|
9113 } |
|
9114 } |
|
9115 if (CUR != ')') { |
|
9116 if (name != NULL) |
|
9117 xmlFree(name); |
|
9118 XP_ERROR0(XPATH_UNCLOSED_ERROR); |
|
9119 } |
|
9120 NEXT; |
|
9121 return(name); |
|
9122 } |
|
9123 *test = NODE_TEST_NAME; |
|
9124 if ((!blanks) && (CUR == ':')) |
|
9125 { |
|
9126 NEXT; |
|
9127 |
|
9128 /* |
|
9129 * Since currently the parser context don't have a |
|
9130 * namespace list associated: |
|
9131 * The namespace name for this prefix can be computed |
|
9132 * only at evaluation time. The compilation is done |
|
9133 * outside of any context. |
|
9134 */ |
|
9135 #if 0 |
|
9136 *prefix = xmlXPathNsLookup(ctxt->context, name); |
|
9137 if (name != NULL) |
|
9138 xmlFree(name); |
|
9139 if (*prefix == NULL) { |
|
9140 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); |
|
9141 } |
|
9142 #else |
|
9143 *prefix = name; |
|
9144 #endif |
|
9145 |
|
9146 if (CUR == '*') { |
|
9147 /* |
|
9148 * All elements |
|
9149 */ |
|
9150 NEXT; |
|
9151 *test = NODE_TEST_ALL; |
|
9152 return(NULL); |
|
9153 } |
|
9154 |
|
9155 name = xmlXPathParseNCName(ctxt); |
|
9156 if (name == NULL) { |
|
9157 XP_ERROR0(XPATH_EXPR_ERROR); |
|
9158 } |
|
9159 } |
|
9160 return(name); |
|
9161 } |
|
9162 |
|
9163 /** |
|
9164 * xmlXPathIsAxisName: |
|
9165 * @param name a preparsed name token |
|
9166 * |
|
9167 * [6] AxisName ::= 'ancestor' |
|
9168 * | 'ancestor-or-self' |
|
9169 * | 'attribute' |
|
9170 * | 'child' |
|
9171 * | 'descendant' |
|
9172 * | 'descendant-or-self' |
|
9173 * | 'following' |
|
9174 * | 'following-sibling' |
|
9175 * | 'namespace' |
|
9176 * | 'parent' |
|
9177 * | 'preceding' |
|
9178 * | 'preceding-sibling' |
|
9179 * | 'self' |
|
9180 * |
|
9181 * Returns the axis or 0 |
|
9182 * |
|
9183 * OOM: never |
|
9184 */ |
|
9185 static xmlXPathAxisVal |
|
9186 xmlXPathIsAxisName(const xmlChar *name) |
|
9187 { |
|
9188 xmlXPathAxisVal ret = (xmlXPathAxisVal) 0; |
|
9189 switch (name[0]) { |
|
9190 |
|
9191 case 'a': |
|
9192 if (xmlStrEqual(name, BAD_CAST "ancestor")) |
|
9193 ret = AXIS_ANCESTOR; |
|
9194 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self")) |
|
9195 ret = AXIS_ANCESTOR_OR_SELF; |
|
9196 if (xmlStrEqual(name, BAD_CAST "attribute")) |
|
9197 ret = AXIS_ATTRIBUTE; |
|
9198 break; |
|
9199 case 'c': |
|
9200 if (xmlStrEqual(name, BAD_CAST "child")) |
|
9201 ret = AXIS_CHILD; |
|
9202 break; |
|
9203 case 'd': |
|
9204 if (xmlStrEqual(name, BAD_CAST "descendant")) |
|
9205 ret = AXIS_DESCENDANT; |
|
9206 if (xmlStrEqual(name, BAD_CAST "descendant-or-self")) |
|
9207 ret = AXIS_DESCENDANT_OR_SELF; |
|
9208 break; |
|
9209 case 'f': |
|
9210 if (xmlStrEqual(name, BAD_CAST "following")) |
|
9211 ret = AXIS_FOLLOWING; |
|
9212 if (xmlStrEqual(name, BAD_CAST "following-sibling")) |
|
9213 ret = AXIS_FOLLOWING_SIBLING; |
|
9214 break; |
|
9215 case 'n': |
|
9216 if (xmlStrEqual(name, BAD_CAST "namespace")) |
|
9217 ret = AXIS_NAMESPACE; |
|
9218 break; |
|
9219 case 'p': |
|
9220 if (xmlStrEqual(name, BAD_CAST "parent")) |
|
9221 ret = AXIS_PARENT; |
|
9222 if (xmlStrEqual(name, BAD_CAST "preceding")) |
|
9223 ret = AXIS_PRECEDING; |
|
9224 if (xmlStrEqual(name, BAD_CAST "preceding-sibling")) |
|
9225 ret = AXIS_PRECEDING_SIBLING; |
|
9226 break; |
|
9227 case 's': |
|
9228 if (xmlStrEqual(name, BAD_CAST "self")) |
|
9229 ret = AXIS_SELF; |
|
9230 break; |
|
9231 } |
|
9232 return(ret); |
|
9233 } |
|
9234 |
|
9235 /** |
|
9236 * xmlXPathCompStep: |
|
9237 * @param ctxt the XPath Parser context |
|
9238 * |
|
9239 * [4] Step ::= AxisSpecifier NodeTest Predicate* |
|
9240 * | AbbreviatedStep |
|
9241 * |
|
9242 * [12] AbbreviatedStep ::= '.' | '..' |
|
9243 * |
|
9244 * [5] AxisSpecifier ::= AxisName '::' |
|
9245 * | AbbreviatedAxisSpecifier |
|
9246 * |
|
9247 * [13] AbbreviatedAxisSpecifier ::= '@'? |
|
9248 * |
|
9249 * Modified for XPtr range support as: |
|
9250 * |
|
9251 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate* |
|
9252 * | AbbreviatedStep |
|
9253 * | 'range-to' '(' Expr ')' Predicate* |
|
9254 * |
|
9255 * Compile one step in a Location Path |
|
9256 * A location step of . is short for self::node(). This is |
|
9257 * particularly useful in conjunction with //. For example, the |
|
9258 * location path .//para is short for |
|
9259 * self::node()/descendant-or-self::node()/child::para |
|
9260 * and so will select all para descendant elements of the context |
|
9261 * node. |
|
9262 * Similarly, a location step of .. is short for parent::node(). |
|
9263 * For example, ../title is short for parent::node()/child::title |
|
9264 * and so will select the title children of the parent of the context |
|
9265 * node. |
|
9266 * |
|
9267 * OOM: possible --> check OOM flag! |
|
9268 */ |
|
9269 static void |
|
9270 xmlXPathCompStep(xmlXPathParserContextPtr ctxt) |
|
9271 { |
|
9272 |
|
9273 #ifdef LIBXML_XPTR_ENABLED |
|
9274 int rangeto = 0; |
|
9275 int op2 = -1; |
|
9276 #endif |
|
9277 |
|
9278 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
9279 SKIP_BLANKS; |
|
9280 if ((CUR == '.') && (NXT(1) == '.')) |
|
9281 { |
|
9282 SKIP(2); |
|
9283 SKIP_BLANKS; |
|
9284 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT, |
|
9285 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9286 } |
|
9287 else if (CUR == '.') |
|
9288 { |
|
9289 NEXT; |
|
9290 SKIP_BLANKS; |
|
9291 } |
|
9292 else |
|
9293 { |
|
9294 xmlChar *name = NULL; |
|
9295 const xmlChar *prefix = NULL; |
|
9296 xmlXPathTestVal test; |
|
9297 xmlXPathAxisVal axis = (xmlXPathAxisVal) 0; |
|
9298 xmlXPathTypeVal type; |
|
9299 int op1; |
|
9300 |
|
9301 /* |
|
9302 * The modification needed for XPointer change to the production |
|
9303 */ |
|
9304 #ifdef LIBXML_XPTR_ENABLED |
|
9305 if (ctxt->xptr) |
|
9306 { |
|
9307 name = xmlXPathParseNCName(ctxt); // OOM is checked later |
|
9308 if (name && xmlStrEqual(name, BAD_CAST "range-to")) |
|
9309 { |
|
9310 op2 = ctxt->comp->last; |
|
9311 xmlFree(name); |
|
9312 SKIP_BLANKS; |
|
9313 if (CUR != '(') { |
|
9314 XP_ERROR(XPATH_EXPR_ERROR); |
|
9315 } |
|
9316 NEXT; |
|
9317 SKIP_BLANKS; |
|
9318 |
|
9319 xmlXPathCompileExpr(ctxt); |
|
9320 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */ |
|
9321 CHECK_ERROR; |
|
9322 |
|
9323 SKIP_BLANKS; |
|
9324 if (CUR != ')') { |
|
9325 XP_ERROR(XPATH_EXPR_ERROR); |
|
9326 } |
|
9327 NEXT; |
|
9328 rangeto = 1; |
|
9329 goto eval_predicates; |
|
9330 } |
|
9331 } |
|
9332 #endif |
|
9333 if (CUR == '*') { |
|
9334 axis = AXIS_CHILD; |
|
9335 } else { |
|
9336 if (!name){ |
|
9337 name = xmlXPathParseNCName(ctxt); // OOM is checked later |
|
9338 } |
|
9339 if (name) { |
|
9340 axis = xmlXPathIsAxisName(name); |
|
9341 if (axis != 0) { |
|
9342 SKIP_BLANKS; |
|
9343 if ((CUR == ':') && (NXT(1) == ':')) { |
|
9344 SKIP(2); |
|
9345 xmlFree(name); |
|
9346 name = NULL; |
|
9347 } else { |
|
9348 /* an element name can conflict with an axis one :-\ */ |
|
9349 axis = AXIS_CHILD; |
|
9350 } |
|
9351 } else { |
|
9352 axis = AXIS_CHILD; |
|
9353 } |
|
9354 } else if (CUR == '@') { |
|
9355 NEXT; |
|
9356 axis = AXIS_ATTRIBUTE; |
|
9357 } else { |
|
9358 axis = AXIS_CHILD; |
|
9359 } |
|
9360 } |
|
9361 |
|
9362 // instead of CHECK_ERROR: |
|
9363 if (ctxt->error != XPATH_EXPRESSION_OK || OOM_FLAG){ |
|
9364 if(name) |
|
9365 xmlFree(name); |
|
9366 if(OOM_FLAG) |
|
9367 XP_ERROR(XPATH_MEMORY_ERROR); |
|
9368 return; |
|
9369 } |
|
9370 |
|
9371 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name); |
|
9372 if (test == 0) |
|
9373 return; |
|
9374 |
|
9375 #ifdef DEBUG_STEP |
|
9376 xmlGenericError(xmlGenericErrorContext, |
|
9377 "Basis : computing new set\n"); |
|
9378 |
|
9379 xmlGenericError(xmlGenericErrorContext, "Basis : "); |
|
9380 if (ctxt->value == NULL) |
|
9381 xmlGenericError(xmlGenericErrorContext, "no value\n"); |
|
9382 else if (ctxt->value->nodesetval == NULL) |
|
9383 xmlGenericError(xmlGenericErrorContext, "Empty\n"); |
|
9384 else |
|
9385 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval); |
|
9386 #endif |
|
9387 |
|
9388 #ifdef LIBXML_XPTR_ENABLED |
|
9389 eval_predicates: |
|
9390 #endif |
|
9391 op1 = ctxt->comp->last; |
|
9392 ctxt->comp->last = -1; |
|
9393 |
|
9394 SKIP_BLANKS; |
|
9395 while (CUR == '[') { |
|
9396 xmlXPathCompPredicate(ctxt, 0); |
|
9397 } |
|
9398 |
|
9399 #ifdef LIBXML_XPTR_ENABLED |
|
9400 if (rangeto) { |
|
9401 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0); |
|
9402 } else |
|
9403 #endif |
|
9404 { |
|
9405 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis, |
|
9406 test, type, (void *)prefix, (void *)name); |
|
9407 } |
|
9408 } |
|
9409 #ifdef DEBUG_STEP |
|
9410 xmlGenericError(xmlGenericErrorContext, "Step : "); |
|
9411 if (ctxt->value == NULL) |
|
9412 xmlGenericError(xmlGenericErrorContext, "no value\n"); |
|
9413 else if (ctxt->value->nodesetval == NULL) |
|
9414 xmlGenericError(xmlGenericErrorContext, "Empty\n"); |
|
9415 else |
|
9416 xmlGenericErrorContextNodeSet(xmlGenericErrorContext, |
|
9417 ctxt->value->nodesetval); |
|
9418 #endif |
|
9419 } |
|
9420 |
|
9421 /** |
|
9422 * xmlXPathCompRelativeLocationPath: |
|
9423 * @param ctxt the XPath Parser context |
|
9424 * |
|
9425 * [3] RelativeLocationPath ::= Step |
|
9426 * | RelativeLocationPath '/' Step |
|
9427 * | AbbreviatedRelativeLocationPath |
|
9428 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step |
|
9429 * |
|
9430 * Compile a relative location path. |
|
9431 */ |
|
9432 static void |
|
9433 xmlXPathCompRelativeLocationPath |
|
9434 (xmlXPathParserContextPtr ctxt) { |
|
9435 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
9436 SKIP_BLANKS; |
|
9437 if ((CUR == '/') && (NXT(1) == '/')) { |
|
9438 SKIP(2); |
|
9439 SKIP_BLANKS; |
|
9440 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, |
|
9441 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9442 } else if (CUR == '/') { |
|
9443 NEXT; |
|
9444 SKIP_BLANKS; |
|
9445 } |
|
9446 xmlXPathCompStep(ctxt); |
|
9447 if(OOM_FLAG) return; |
|
9448 SKIP_BLANKS; |
|
9449 while (CUR == '/') { |
|
9450 if ((CUR == '/') && (NXT(1) == '/')) { |
|
9451 SKIP(2); |
|
9452 SKIP_BLANKS; |
|
9453 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, |
|
9454 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9455 xmlXPathCompStep(ctxt); |
|
9456 if(OOM_FLAG) return; |
|
9457 } else if (CUR == '/') { |
|
9458 NEXT; |
|
9459 SKIP_BLANKS; |
|
9460 xmlXPathCompStep(ctxt); |
|
9461 } |
|
9462 SKIP_BLANKS; |
|
9463 } |
|
9464 } |
|
9465 |
|
9466 /** |
|
9467 * xmlXPathCompLocationPath: |
|
9468 * @param ctxt the XPath Parser context |
|
9469 * |
|
9470 * [1] LocationPath ::= RelativeLocationPath |
|
9471 * | AbsoluteLocationPath |
|
9472 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? |
|
9473 * | AbbreviatedAbsoluteLocationPath |
|
9474 * [10] AbbreviatedAbsoluteLocationPath ::= |
|
9475 * '//' RelativeLocationPath |
|
9476 * |
|
9477 * Compile a location path |
|
9478 * |
|
9479 * // is short for /descendant-or-self::node()/. For example, |
|
9480 * //para is short for /descendant-or-self::node()/child::para and |
|
9481 * so will select any para element in the document (even a para element |
|
9482 * that is a document element will be selected by //para since the |
|
9483 * document element node is a child of the root node); div//para is |
|
9484 * short for div/descendant-or-self::node()/child::para and so will |
|
9485 * select all para descendants of div children. |
|
9486 * |
|
9487 * OOM: possible --> |
|
9488 */ |
|
9489 static void |
|
9490 xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) |
|
9491 { |
|
9492 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
9493 SKIP_BLANKS; |
|
9494 if (CUR != '/') { |
|
9495 xmlXPathCompRelativeLocationPath(ctxt); |
|
9496 } else { |
|
9497 while (CUR == '/') { |
|
9498 if ((CUR == '/') && (NXT(1) == '/')) |
|
9499 { |
|
9500 SKIP(2); |
|
9501 SKIP_BLANKS; |
|
9502 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, |
|
9503 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9504 xmlXPathCompRelativeLocationPath(ctxt); |
|
9505 if(OOM_FLAG) return; |
|
9506 } |
|
9507 else if (CUR == '/') |
|
9508 { |
|
9509 NEXT; |
|
9510 SKIP_BLANKS; |
|
9511 if ((CUR != 0 ) && |
|
9512 ((IS_LETTER_CH(CUR)) || (CUR == '_') || (CUR == '.') || |
|
9513 (CUR == '@') || (CUR == '*'))) |
|
9514 { |
|
9515 xmlXPathCompRelativeLocationPath(ctxt); |
|
9516 if(OOM_FLAG) return; |
|
9517 } |
|
9518 } |
|
9519 // |
|
9520 CHECK_ERROR; |
|
9521 } // end while |
|
9522 } // end else |
|
9523 } |
|
9524 |
|
9525 /************************************************************************ |
|
9526 * * |
|
9527 * XPath precompiled expression evaluation * |
|
9528 * * |
|
9529 ************************************************************************/ |
|
9530 |
|
9531 static int |
|
9532 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op); |
|
9533 |
|
9534 /** |
|
9535 * xmlXPathNodeCollectAndTest: |
|
9536 * @param ctxt the XPath Parser context |
|
9537 * @param op the XPath precompiled step operation |
|
9538 * @param first pointer to the first element in document order |
|
9539 * @param last pointer to the last element in document order |
|
9540 * |
|
9541 * This is the function implementing a step: based on the current list |
|
9542 * of nodes, it builds up a new list, looking at all nodes under that |
|
9543 * axis and selecting them. It also does the predicate filtering |
|
9544 * |
|
9545 * Pushes the new NodeSet resulting from the search. |
|
9546 * |
|
9547 * Returns the number of nodes traversed or -1 when OOM |
|
9548 */ |
|
9549 static int |
|
9550 xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
|
9551 xmlXPathStepOpPtr op, |
|
9552 xmlNodePtr * first, xmlNodePtr * last) |
|
9553 { |
|
9554 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; |
|
9555 xmlXPathTestVal test = (xmlXPathTestVal) op->value2; |
|
9556 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3; |
|
9557 |
|
9558 const xmlChar* prefix = (xmlChar*) op->value4; |
|
9559 const xmlChar* name = (xmlChar*) op->value5; |
|
9560 const xmlChar* URI = NULL; |
|
9561 |
|
9562 #ifdef DEBUG_STEP |
|
9563 int n = 0; |
|
9564 #endif |
|
9565 |
|
9566 int i, t = 0; |
|
9567 xmlNodeSetPtr ret, list = NULL; |
|
9568 xmlXPathTraversalFunction next = NULL; |
|
9569 void (*addNode) (xmlNodeSetPtr, xmlNodePtr); |
|
9570 xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr); |
|
9571 xmlNodePtr cur = NULL; |
|
9572 xmlXPathObjectPtr obj; |
|
9573 xmlNodeSetPtr nodelist; |
|
9574 xmlNodePtr tmp; |
|
9575 |
|
9576 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
9577 CHECK_TYPE0(XPATH_NODESET); |
|
9578 obj = valuePop(ctxt); |
|
9579 addNode = xmlXPathNodeSetAdd; |
|
9580 mergeNodeSet = xmlXPathNodeSetMerge; |
|
9581 if (prefix) { |
|
9582 URI = xmlXPathNsLookup(ctxt->context, prefix); |
|
9583 if (URI == NULL) |
|
9584 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); |
|
9585 } |
|
9586 |
|
9587 #ifdef DEBUG_STEP |
|
9588 xmlGenericError(xmlGenericErrorContext, "new step : "); |
|
9589 #endif |
|
9590 |
|
9591 switch (axis) |
|
9592 { |
|
9593 |
|
9594 case AXIS_ANCESTOR: { |
|
9595 #ifdef DEBUG_STEP |
|
9596 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' "); |
|
9597 #endif |
|
9598 first = NULL; |
|
9599 next = xmlXPathNextAncestor; |
|
9600 break; |
|
9601 } |
|
9602 |
|
9603 case AXIS_ANCESTOR_OR_SELF: { |
|
9604 #ifdef DEBUG_STEP |
|
9605 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors-or-self' "); |
|
9606 #endif |
|
9607 first = NULL; |
|
9608 next = xmlXPathNextAncestorOrSelf; |
|
9609 break; |
|
9610 } |
|
9611 |
|
9612 case AXIS_ATTRIBUTE:{ |
|
9613 #ifdef DEBUG_STEP |
|
9614 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' "); |
|
9615 #endif |
|
9616 first = NULL; |
|
9617 last = NULL; |
|
9618 next = xmlXPathNextAttribute; |
|
9619 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9620 break; |
|
9621 } |
|
9622 |
|
9623 case AXIS_CHILD:{ |
|
9624 #ifdef DEBUG_STEP |
|
9625 xmlGenericError(xmlGenericErrorContext, "axis 'child' "); |
|
9626 #endif |
|
9627 last = NULL; |
|
9628 next = xmlXPathNextChild; |
|
9629 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9630 break; |
|
9631 } |
|
9632 |
|
9633 case AXIS_DESCENDANT:{ |
|
9634 #ifdef DEBUG_STEP |
|
9635 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' "); |
|
9636 #endif |
|
9637 last = NULL; |
|
9638 next = xmlXPathNextDescendant; |
|
9639 break; |
|
9640 } |
|
9641 |
|
9642 case AXIS_DESCENDANT_OR_SELF:{ |
|
9643 #ifdef DEBUG_STEP |
|
9644 xmlGenericError(xmlGenericErrorContext, "axis 'descendant-or-self' "); |
|
9645 #endif |
|
9646 last = NULL; |
|
9647 next = xmlXPathNextDescendantOrSelf; |
|
9648 break; |
|
9649 } |
|
9650 |
|
9651 case AXIS_FOLLOWING:{ |
|
9652 #ifdef DEBUG_STEP |
|
9653 xmlGenericError(xmlGenericErrorContext, "axis 'following' "); |
|
9654 #endif |
|
9655 last = NULL; |
|
9656 next = xmlXPathNextFollowing; |
|
9657 break; |
|
9658 } |
|
9659 |
|
9660 case AXIS_FOLLOWING_SIBLING:{ |
|
9661 #ifdef DEBUG_STEP |
|
9662 xmlGenericError(xmlGenericErrorContext, "axis 'following-siblings' "); |
|
9663 #endif |
|
9664 last = NULL; |
|
9665 next = xmlXPathNextFollowingSibling; |
|
9666 break; |
|
9667 } |
|
9668 |
|
9669 case AXIS_NAMESPACE:{ |
|
9670 #ifdef DEBUG_STEP |
|
9671 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' "); |
|
9672 #endif |
|
9673 first = NULL; |
|
9674 last = NULL; |
|
9675 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; |
|
9676 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9677 break; |
|
9678 } |
|
9679 |
|
9680 case AXIS_PARENT:{ |
|
9681 #ifdef DEBUG_STEP |
|
9682 xmlGenericError(xmlGenericErrorContext, "axis 'parent' "); |
|
9683 #endif |
|
9684 first = NULL; |
|
9685 next = xmlXPathNextParent; |
|
9686 break; |
|
9687 } |
|
9688 |
|
9689 case AXIS_PRECEDING:{ |
|
9690 #ifdef DEBUG_STEP |
|
9691 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' "); |
|
9692 #endif |
|
9693 first = NULL; |
|
9694 next = xmlXPathNextPrecedingInternal; |
|
9695 break; |
|
9696 } |
|
9697 |
|
9698 case AXIS_PRECEDING_SIBLING:{ |
|
9699 #ifdef DEBUG_STEP |
|
9700 xmlGenericError(xmlGenericErrorContext, "axis 'preceding-sibling' "); |
|
9701 #endif |
|
9702 first = NULL; |
|
9703 next = xmlXPathNextPrecedingSibling; |
|
9704 break; |
|
9705 } |
|
9706 |
|
9707 case AXIS_SELF:{ |
|
9708 #ifdef DEBUG_STEP |
|
9709 xmlGenericError(xmlGenericErrorContext, "axis 'self' "); |
|
9710 #endif |
|
9711 first = NULL; |
|
9712 last = NULL; |
|
9713 next = xmlXPathNextSelf; |
|
9714 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9715 break; |
|
9716 } |
|
9717 |
|
9718 } // switch(axis) |
|
9719 |
|
9720 if (next == NULL) |
|
9721 return(0); |
|
9722 |
|
9723 nodelist = obj->nodesetval; |
|
9724 if (!nodelist) { |
|
9725 xmlXPathFreeObject(obj); |
|
9726 valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); |
|
9727 return(0); |
|
9728 } |
|
9729 addNode = xmlXPathNodeSetAddUnique; |
|
9730 ret = NULL; |
|
9731 |
|
9732 #ifdef DEBUG_STEP |
|
9733 { |
|
9734 xmlGenericError(xmlGenericErrorContext, " context contains %d nodes\n", nodelist->nodeNr); |
|
9735 switch (test) { |
|
9736 case NODE_TEST_NONE: |
|
9737 xmlGenericError(xmlGenericErrorContext, " searching for none !!!\n"); |
|
9738 break; |
|
9739 case NODE_TEST_TYPE: |
|
9740 xmlGenericError(xmlGenericErrorContext, " searching for type %d\n", type); |
|
9741 break; |
|
9742 case NODE_TEST_PI: |
|
9743 xmlGenericError(xmlGenericErrorContext, " searching for PI !!!\n"); |
|
9744 break; |
|
9745 case NODE_TEST_ALL: |
|
9746 xmlGenericError(xmlGenericErrorContext, " searching for *\n"); |
|
9747 break; |
|
9748 case NODE_TEST_NS: |
|
9749 xmlGenericError(xmlGenericErrorContext, " searching for namespace %s\n", prefix); |
|
9750 break; |
|
9751 case NODE_TEST_NAME: |
|
9752 xmlGenericError(xmlGenericErrorContext, " searching for name %s\n", name); |
|
9753 if (prefix != NULL) |
|
9754 xmlGenericError(xmlGenericErrorContext, " with namespace %s\n", prefix); |
|
9755 break; |
|
9756 } |
|
9757 xmlGenericError(xmlGenericErrorContext, "Testing : "); |
|
9758 } |
|
9759 #endif |
|
9760 /* |
|
9761 * 2.3 Node Tests |
|
9762 * - For the attribute axis, the principal node type is attribute. |
|
9763 * - For the namespace axis, the principal node type is namespace. |
|
9764 * - For other axes, the principal node type is element. |
|
9765 * |
|
9766 * A node test * is true for any node of the |
|
9767 * principal node type. For example, child::* will |
|
9768 * select all element children of the context node |
|
9769 */ |
|
9770 tmp = ctxt->context->node; |
|
9771 |
|
9772 for (i = 0; i < nodelist->nodeNr; i++) |
|
9773 { |
|
9774 ctxt->context->node = nodelist->nodeTab[i]; |
|
9775 cur = NULL; |
|
9776 list = xmlXPathNodeSetCreate(NULL); |
|
9777 if(OOM_FLAG) |
|
9778 { // Note: cannot use: goto OOM_obj_list_ret with list!=NULL |
|
9779 list = NULL; |
|
9780 goto OOM_obj_list_ret; |
|
9781 } |
|
9782 // |
|
9783 do { |
|
9784 cur = next(ctxt, cur); |
|
9785 if (cur == NULL) |
|
9786 break; |
|
9787 if (first && (*first == cur)) |
|
9788 break; |
|
9789 if (((t % 256) == 0) && |
|
9790 first && *first && |
|
9791 (xmlXPathCmpNodes(*first, cur) >= 0)) |
|
9792 break; |
|
9793 if (last && (*last == cur)) |
|
9794 break; |
|
9795 if (((t % 256) == 0) && |
|
9796 last && *last && |
|
9797 (xmlXPathCmpNodes(cur, *last) >= 0)) |
|
9798 break; |
|
9799 |
|
9800 t++; |
|
9801 #ifdef DEBUG_STEP |
|
9802 xmlGenericError(xmlGenericErrorContext, " %s", cur->name); |
|
9803 #endif |
|
9804 switch (test) |
|
9805 { |
|
9806 case NODE_TEST_NONE:{ |
|
9807 ctxt->context->node = tmp; |
|
9808 STRANGE return(t); |
|
9809 } |
|
9810 case NODE_TEST_TYPE:{ |
|
9811 |
|
9812 if ((cur->type == type) || |
|
9813 ((type == NODE_TYPE_NODE) && |
|
9814 ((cur->type == XML_DOCUMENT_NODE) || |
|
9815 (cur->type == XML_HTML_DOCUMENT_NODE) || |
|
9816 (cur->type == XML_ELEMENT_NODE) || |
|
9817 (cur->type == XML_NAMESPACE_DECL) || |
|
9818 (cur->type == XML_ATTRIBUTE_NODE) || |
|
9819 (cur->type == XML_PI_NODE) || |
|
9820 (cur->type == XML_COMMENT_NODE) || |
|
9821 (cur->type == XML_CDATA_SECTION_NODE) || |
|
9822 (cur->type == XML_TEXT_NODE))) || |
|
9823 ((type == NODE_TYPE_TEXT) && |
|
9824 (cur->type == XML_CDATA_SECTION_NODE))) |
|
9825 { |
|
9826 #ifdef DEBUG_STEP |
|
9827 n++; |
|
9828 #endif |
|
9829 addNode(list, cur); |
|
9830 } |
|
9831 break; |
|
9832 } |
|
9833 |
|
9834 case NODE_TEST_PI: { |
|
9835 if (cur->type == XML_PI_NODE) |
|
9836 { |
|
9837 if ((name != NULL) && (!xmlStrEqual(name, cur->name))) |
|
9838 break; |
|
9839 #ifdef DEBUG_STEP |
|
9840 n++; |
|
9841 #endif |
|
9842 addNode(list, cur); |
|
9843 } |
|
9844 break; |
|
9845 } |
|
9846 |
|
9847 case NODE_TEST_ALL: { |
|
9848 if (axis == AXIS_ATTRIBUTE) { |
|
9849 if (cur->type == XML_ATTRIBUTE_NODE) { |
|
9850 #ifdef DEBUG_STEP |
|
9851 n++; |
|
9852 #endif |
|
9853 addNode(list, cur); |
|
9854 } |
|
9855 } else |
|
9856 if (axis == AXIS_NAMESPACE) { |
|
9857 if (cur->type == XML_NAMESPACE_DECL) { |
|
9858 #ifdef DEBUG_STEP |
|
9859 n++; |
|
9860 #endif |
|
9861 xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur); |
|
9862 } |
|
9863 } else { |
|
9864 if (cur->type == XML_ELEMENT_NODE) { |
|
9865 if (prefix == NULL) { |
|
9866 #ifdef DEBUG_STEP |
|
9867 n++; |
|
9868 #endif |
|
9869 addNode(list, cur); |
|
9870 if(OOM_FLAG) |
|
9871 { |
|
9872 goto OOM_obj_list_ret; |
|
9873 } |
|
9874 } else |
|
9875 if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) { |
|
9876 #ifdef DEBUG_STEP |
|
9877 n++; |
|
9878 #endif |
|
9879 addNode(list, cur); |
|
9880 } |
|
9881 } |
|
9882 } |
|
9883 break; |
|
9884 } |
|
9885 |
|
9886 case NODE_TEST_NS: { |
|
9887 TODO; |
|
9888 break; |
|
9889 } |
|
9890 case NODE_TEST_NAME:{ |
|
9891 switch (cur->type) { |
|
9892 case XML_ELEMENT_NODE: { |
|
9893 if (xmlStrEqual(name, cur->name)) { |
|
9894 if (prefix == NULL) { |
|
9895 //if (cur->ns == NULL) { // should we add here || cur->ns->pref == NULL ? |
|
9896 if (!cur->ns || !cur->ns->href || !*cur->ns->href) { // XML ENGINE: modified code |
|
9897 #ifdef DEBUG_STEP |
|
9898 n++; |
|
9899 #endif |
|
9900 addNode(list, cur); |
|
9901 if(OOM_FLAG) |
|
9902 { |
|
9903 goto OOM_obj_list; |
|
9904 } |
|
9905 } |
|
9906 } else { |
|
9907 if (cur->ns && (xmlStrEqual(URI, cur->ns->href))) { |
|
9908 #ifdef DEBUG_STEP |
|
9909 n++; |
|
9910 #endif |
|
9911 addNode(list, cur); |
|
9912 } |
|
9913 } |
|
9914 } |
|
9915 break; |
|
9916 } |
|
9917 |
|
9918 case XML_ATTRIBUTE_NODE:{ |
|
9919 xmlAttrPtr attr = (xmlAttrPtr) cur; |
|
9920 |
|
9921 if (xmlStrEqual(name, attr->name)) { |
|
9922 if (prefix == NULL) { |
|
9923 if ((attr->ns == NULL) || (attr->ns->prefix == NULL)) { |
|
9924 #ifdef DEBUG_STEP |
|
9925 n++; |
|
9926 #endif |
|
9927 addNode(list, (xmlNodePtr) attr); |
|
9928 if(OOM_FLAG) |
|
9929 { |
|
9930 goto OOM_obj_list; |
|
9931 } |
|
9932 } |
|
9933 } else { |
|
9934 if ((attr->ns != NULL) && (xmlStrEqual(URI, attr->ns->href))) { |
|
9935 #ifdef DEBUG_STEP |
|
9936 n++; |
|
9937 #endif |
|
9938 addNode(list, (xmlNodePtr) attr); |
|
9939 } |
|
9940 } |
|
9941 } |
|
9942 break; |
|
9943 } |
|
9944 case XML_NAMESPACE_DECL:{ |
|
9945 if (cur->type == XML_NAMESPACE_DECL) { |
|
9946 xmlNsPtr ns = (xmlNsPtr) cur; |
|
9947 |
|
9948 if ((ns->prefix != NULL) && (name != NULL) |
|
9949 && (xmlStrEqual(ns->prefix, name))) |
|
9950 { |
|
9951 #ifdef DEBUG_STEP |
|
9952 n++; |
|
9953 #endif |
|
9954 xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur); |
|
9955 } |
|
9956 } |
|
9957 break; |
|
9958 } |
|
9959 |
|
9960 default: |
|
9961 break; |
|
9962 } |
|
9963 |
|
9964 break; |
|
9965 } |
|
9966 } // switch(test) |
|
9967 } while (cur); |
|
9968 |
|
9969 /* |
|
9970 * If there is some predicate filtering do it now |
|
9971 */ |
|
9972 if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0)) |
|
9973 { |
|
9974 xmlXPathObjectPtr obj2; |
|
9975 |
|
9976 valuePush(ctxt, xmlXPathWrapNodeSet(list)); |
|
9977 if(OOM_FLAG) |
|
9978 { |
|
9979 goto OOM_obj_list_ret; |
|
9980 } |
|
9981 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]); |
|
9982 CHECK_TYPE0(XPATH_NODESET); |
|
9983 obj2 = valuePop(ctxt); |
|
9984 list = obj2->nodesetval; |
|
9985 obj2->nodesetval = NULL; |
|
9986 xmlXPathFreeObject(obj2); |
|
9987 } |
|
9988 if (ret == NULL) { |
|
9989 ret = list; |
|
9990 } else { |
|
9991 ret = mergeNodeSet(ret, list); // either xmlXPathNodeSetMergeUnique or xmlXPathNodeSetMerge called |
|
9992 xmlXPathFreeNodeSet(list); |
|
9993 } |
|
9994 } // for (i = 0; i < nodelist->nodeNr; i++) |
|
9995 |
|
9996 ctxt->context->node = tmp; |
|
9997 |
|
9998 #ifdef DEBUG_STEP |
|
9999 xmlGenericError(xmlGenericErrorContext, "\nExamined %d nodes, found %d nodes at that step\n", t, n); |
|
10000 #endif |
|
10001 |
|
10002 valuePush(ctxt, xmlXPathWrapNodeSet(ret)); |
|
10003 if(OOM_FLAG) |
|
10004 { |
|
10005 goto OOM_obj_list; |
|
10006 } |
|
10007 if ((obj->boolval) && (obj->user != NULL)) { |
|
10008 ctxt->value->boolval = 1; |
|
10009 ctxt->value->user = obj->user; |
|
10010 obj->user = NULL; |
|
10011 obj->boolval = 0; |
|
10012 } |
|
10013 xmlXPathFreeObject(obj); |
|
10014 return(t); |
|
10015 |
|
10016 //-------- OOM cleanup |
|
10017 OOM_obj_list_ret: |
|
10018 if(ret) |
|
10019 xmlXPathFreeNodeSet(ret); |
|
10020 OOM_obj_list: |
|
10021 if(list) |
|
10022 xmlXPathFreeNodeSet(list); |
|
10023 if(obj) |
|
10024 xmlXPathFreeObject(obj); |
|
10025 return(-1); |
|
10026 } |
|
10027 |
|
10028 /** |
|
10029 * xmlXPathNodeCollectAndTestNth: |
|
10030 * @param ctxt the XPath Parser context |
|
10031 * @param op the XPath precompiled step operation |
|
10032 * @param indx the index to collect |
|
10033 * @param first pointer to the first element in document order |
|
10034 * @param last pointer to the last element in document order |
|
10035 * |
|
10036 * This is the function implementing a step: based on the current list |
|
10037 * of nodes, it builds up a new list, looking at all nodes under that |
|
10038 * axis and selecting them. It also does the predicate filtering |
|
10039 * |
|
10040 * Pushes the new NodeSet resulting from the search. |
|
10041 * Returns the number of node traversed or -1 when OOM |
|
10042 */ |
|
10043 static int |
|
10044 xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, |
|
10045 xmlXPathStepOpPtr op, int indx, |
|
10046 xmlNodePtr * first, xmlNodePtr * last) |
|
10047 { |
|
10048 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; |
|
10049 xmlXPathTestVal test = (xmlXPathTestVal) op->value2; |
|
10050 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3; |
|
10051 const xmlChar *prefix = (xmlChar*)op->value4; |
|
10052 const xmlChar *name = (xmlChar*)op->value5; |
|
10053 const xmlChar *URI = NULL; |
|
10054 int n = 0, t = 0; |
|
10055 |
|
10056 int i; |
|
10057 xmlNodeSetPtr list; |
|
10058 xmlXPathTraversalFunction next = NULL; |
|
10059 void (*addNode) (xmlNodeSetPtr, xmlNodePtr); |
|
10060 xmlNodePtr cur = NULL; |
|
10061 xmlXPathObjectPtr obj; |
|
10062 xmlNodeSetPtr nodelist; |
|
10063 xmlNodePtr tmp; |
|
10064 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
10065 |
|
10066 CHECK_TYPE0(XPATH_NODESET); |
|
10067 obj = valuePop(ctxt); |
|
10068 addNode = xmlXPathNodeSetAdd; |
|
10069 if (prefix != NULL) { |
|
10070 URI = xmlXPathNsLookup(ctxt->context, prefix); |
|
10071 if (URI == NULL) |
|
10072 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); |
|
10073 } |
|
10074 #ifdef DEBUG_STEP_NTH |
|
10075 xmlGenericError(xmlGenericErrorContext, "new step : "); |
|
10076 if (first != NULL) { |
|
10077 if (*first != NULL) |
|
10078 xmlGenericError(xmlGenericErrorContext, "first = %s ", |
|
10079 (*first)->name); |
|
10080 else |
|
10081 xmlGenericError(xmlGenericErrorContext, "first = NULL "); |
|
10082 } |
|
10083 if (last != NULL) { |
|
10084 if (*last != NULL) |
|
10085 xmlGenericError(xmlGenericErrorContext, "last = %s ", |
|
10086 (*last)->name); |
|
10087 else |
|
10088 xmlGenericError(xmlGenericErrorContext, "last = NULL "); |
|
10089 } |
|
10090 #endif |
|
10091 switch (axis) { |
|
10092 case AXIS_ANCESTOR: |
|
10093 #ifdef DEBUG_STEP_NTH |
|
10094 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' "); |
|
10095 #endif |
|
10096 first = NULL; |
|
10097 next = xmlXPathNextAncestor; |
|
10098 break; |
|
10099 case AXIS_ANCESTOR_OR_SELF: |
|
10100 #ifdef DEBUG_STEP_NTH |
|
10101 xmlGenericError(xmlGenericErrorContext, |
|
10102 "axis 'ancestors-or-self' "); |
|
10103 #endif |
|
10104 first = NULL; |
|
10105 next = xmlXPathNextAncestorOrSelf; |
|
10106 break; |
|
10107 case AXIS_ATTRIBUTE: |
|
10108 #ifdef DEBUG_STEP_NTH |
|
10109 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' "); |
|
10110 #endif |
|
10111 first = NULL; |
|
10112 last = NULL; |
|
10113 next = xmlXPathNextAttribute; |
|
10114 break; |
|
10115 case AXIS_CHILD: |
|
10116 #ifdef DEBUG_STEP_NTH |
|
10117 xmlGenericError(xmlGenericErrorContext, "axis 'child' "); |
|
10118 #endif |
|
10119 last = NULL; |
|
10120 next = xmlXPathNextChild; |
|
10121 break; |
|
10122 case AXIS_DESCENDANT: |
|
10123 #ifdef DEBUG_STEP_NTH |
|
10124 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' "); |
|
10125 #endif |
|
10126 last = NULL; |
|
10127 next = xmlXPathNextDescendant; |
|
10128 break; |
|
10129 case AXIS_DESCENDANT_OR_SELF: |
|
10130 #ifdef DEBUG_STEP_NTH |
|
10131 xmlGenericError(xmlGenericErrorContext, |
|
10132 "axis 'descendant-or-self' "); |
|
10133 #endif |
|
10134 last = NULL; |
|
10135 next = xmlXPathNextDescendantOrSelf; |
|
10136 break; |
|
10137 case AXIS_FOLLOWING: |
|
10138 #ifdef DEBUG_STEP_NTH |
|
10139 xmlGenericError(xmlGenericErrorContext, "axis 'following' "); |
|
10140 #endif |
|
10141 last = NULL; |
|
10142 next = xmlXPathNextFollowing; |
|
10143 break; |
|
10144 case AXIS_FOLLOWING_SIBLING: |
|
10145 #ifdef DEBUG_STEP_NTH |
|
10146 xmlGenericError(xmlGenericErrorContext, |
|
10147 "axis 'following-siblings' "); |
|
10148 #endif |
|
10149 last = NULL; |
|
10150 next = xmlXPathNextFollowingSibling; |
|
10151 break; |
|
10152 case AXIS_NAMESPACE: |
|
10153 #ifdef DEBUG_STEP_NTH |
|
10154 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' "); |
|
10155 #endif |
|
10156 last = NULL; |
|
10157 first = NULL; |
|
10158 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; |
|
10159 break; |
|
10160 case AXIS_PARENT: |
|
10161 #ifdef DEBUG_STEP_NTH |
|
10162 xmlGenericError(xmlGenericErrorContext, "axis 'parent' "); |
|
10163 #endif |
|
10164 first = NULL; |
|
10165 next = xmlXPathNextParent; |
|
10166 break; |
|
10167 case AXIS_PRECEDING: |
|
10168 #ifdef DEBUG_STEP_NTH |
|
10169 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' "); |
|
10170 #endif |
|
10171 first = NULL; |
|
10172 next = xmlXPathNextPrecedingInternal; |
|
10173 break; |
|
10174 case AXIS_PRECEDING_SIBLING: |
|
10175 #ifdef DEBUG_STEP_NTH |
|
10176 xmlGenericError(xmlGenericErrorContext, |
|
10177 "axis 'preceding-sibling' "); |
|
10178 #endif |
|
10179 first = NULL; |
|
10180 next = xmlXPathNextPrecedingSibling; |
|
10181 break; |
|
10182 case AXIS_SELF: |
|
10183 #ifdef DEBUG_STEP_NTH |
|
10184 xmlGenericError(xmlGenericErrorContext, "axis 'self' "); |
|
10185 #endif |
|
10186 first = NULL; |
|
10187 last = NULL; |
|
10188 next = xmlXPathNextSelf; |
|
10189 break; |
|
10190 } |
|
10191 if (next == NULL) |
|
10192 return(0); |
|
10193 |
|
10194 nodelist = obj->nodesetval; |
|
10195 if (nodelist == NULL) { |
|
10196 xmlXPathFreeObject(obj); |
|
10197 valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); |
|
10198 return(0); |
|
10199 } |
|
10200 addNode = xmlXPathNodeSetAddUnique; |
|
10201 #ifdef DEBUG_STEP_NTH |
|
10202 xmlGenericError(xmlGenericErrorContext, |
|
10203 " context contains %d nodes\n", nodelist->nodeNr); |
|
10204 switch (test) { |
|
10205 case NODE_TEST_NONE: |
|
10206 xmlGenericError(xmlGenericErrorContext, |
|
10207 " searching for none !!!\n"); |
|
10208 break; |
|
10209 case NODE_TEST_TYPE: |
|
10210 xmlGenericError(xmlGenericErrorContext, |
|
10211 " searching for type %d\n", type); |
|
10212 break; |
|
10213 case NODE_TEST_PI: |
|
10214 xmlGenericError(xmlGenericErrorContext, |
|
10215 " searching for PI !!!\n"); |
|
10216 break; |
|
10217 case NODE_TEST_ALL: |
|
10218 xmlGenericError(xmlGenericErrorContext, |
|
10219 " searching for *\n"); |
|
10220 break; |
|
10221 case NODE_TEST_NS: |
|
10222 xmlGenericError(xmlGenericErrorContext, |
|
10223 " searching for namespace %s\n", |
|
10224 prefix); |
|
10225 break; |
|
10226 case NODE_TEST_NAME: |
|
10227 xmlGenericError(xmlGenericErrorContext, |
|
10228 " searching for name %s\n", name); |
|
10229 if (prefix != NULL) |
|
10230 xmlGenericError(xmlGenericErrorContext, |
|
10231 " with namespace %s\n", prefix); |
|
10232 break; |
|
10233 } |
|
10234 xmlGenericError(xmlGenericErrorContext, "Testing : "); |
|
10235 #endif |
|
10236 /* |
|
10237 * 2.3 Node Tests |
|
10238 * - For the attribute axis, the principal node type is attribute. |
|
10239 * - For the namespace axis, the principal node type is namespace. |
|
10240 * - For other axes, the principal node type is element. |
|
10241 * |
|
10242 * A node test * is true for any node of the |
|
10243 * principal node type. For example, child::* will |
|
10244 * select all element children of the context node |
|
10245 */ |
|
10246 tmp = ctxt->context->node; |
|
10247 list = xmlXPathNodeSetCreate(NULL); |
|
10248 if(!list) |
|
10249 goto OOM_obj; |
|
10250 |
|
10251 for (i = 0; i < nodelist->nodeNr; i++) { |
|
10252 ctxt->context->node = nodelist->nodeTab[i]; |
|
10253 |
|
10254 cur = NULL; |
|
10255 n = 0; |
|
10256 do { |
|
10257 cur = next(ctxt, cur); |
|
10258 if (cur == NULL) |
|
10259 break; |
|
10260 if ((first != NULL) && (*first == cur)) |
|
10261 break; |
|
10262 if (((t % 256) == 0) && |
|
10263 (first != NULL) && (*first != NULL) && |
|
10264 (xmlXPathCmpNodes(*first, cur) >= 0)) |
|
10265 break; |
|
10266 if ((last != NULL) && (*last == cur)) |
|
10267 break; |
|
10268 if (((t % 256) == 0) && |
|
10269 (last != NULL) && (*last != NULL) && |
|
10270 (xmlXPathCmpNodes(cur, *last) >= 0)) |
|
10271 break; |
|
10272 t++; |
|
10273 switch (test) { |
|
10274 case NODE_TEST_NONE: |
|
10275 ctxt->context->node = tmp; |
|
10276 STRANGE return(0); |
|
10277 case NODE_TEST_TYPE: |
|
10278 if ((cur->type == type) || |
|
10279 ((type == NODE_TYPE_NODE) && |
|
10280 ((cur->type == XML_DOCUMENT_NODE) || |
|
10281 (cur->type == XML_HTML_DOCUMENT_NODE) || |
|
10282 (cur->type == XML_ELEMENT_NODE) || |
|
10283 (cur->type == XML_PI_NODE) || |
|
10284 (cur->type == XML_COMMENT_NODE) || |
|
10285 (cur->type == XML_CDATA_SECTION_NODE) || |
|
10286 (cur->type == XML_TEXT_NODE))) || |
|
10287 ((type == NODE_TYPE_TEXT) && |
|
10288 (cur->type == XML_CDATA_SECTION_NODE))) { |
|
10289 n++; |
|
10290 if (n == indx) |
|
10291 addNode(list, cur); |
|
10292 } |
|
10293 break; |
|
10294 case NODE_TEST_PI: |
|
10295 if (cur->type == XML_PI_NODE) { |
|
10296 if ((name != NULL) && |
|
10297 (!xmlStrEqual(name, cur->name))) |
|
10298 break; |
|
10299 n++; |
|
10300 if (n == indx) |
|
10301 addNode(list, cur); |
|
10302 } |
|
10303 break; |
|
10304 case NODE_TEST_ALL: |
|
10305 if (axis == AXIS_ATTRIBUTE) { |
|
10306 if (cur->type == XML_ATTRIBUTE_NODE) { |
|
10307 n++; |
|
10308 if (n == indx) |
|
10309 addNode(list, cur); |
|
10310 } |
|
10311 } else if (axis == AXIS_NAMESPACE) { |
|
10312 if (cur->type == XML_NAMESPACE_DECL) { |
|
10313 n++; |
|
10314 if (n == indx) |
|
10315 xmlXPathNodeSetAddNs(list, ctxt->context->node, |
|
10316 (xmlNsPtr) cur); |
|
10317 } |
|
10318 } else { |
|
10319 if (cur->type == XML_ELEMENT_NODE) { |
|
10320 if (prefix == NULL) { |
|
10321 n++; |
|
10322 if (n == indx) |
|
10323 addNode(list, cur); |
|
10324 } else if ((cur->ns != NULL) && |
|
10325 (xmlStrEqual(URI, cur->ns->href))) { |
|
10326 n++; |
|
10327 if (n == indx) |
|
10328 addNode(list, cur); |
|
10329 } |
|
10330 } |
|
10331 } |
|
10332 break; |
|
10333 case NODE_TEST_NS:{ |
|
10334 TODO; |
|
10335 break; |
|
10336 } |
|
10337 case NODE_TEST_NAME: |
|
10338 switch (cur->type) { |
|
10339 case XML_ELEMENT_NODE: |
|
10340 if (xmlStrEqual(name, cur->name)) { |
|
10341 if (prefix == NULL) { |
|
10342 if (cur->ns == NULL) { |
|
10343 n++; |
|
10344 if (n == indx) |
|
10345 addNode(list, cur); |
|
10346 if(OOM_FLAG) |
|
10347 goto OOM_obj_list; |
|
10348 } |
|
10349 } else { |
|
10350 if ((cur->ns != NULL) && |
|
10351 (xmlStrEqual(URI, |
|
10352 cur->ns->href))) { |
|
10353 n++; |
|
10354 if (n == indx) |
|
10355 addNode(list, cur); |
|
10356 } |
|
10357 } |
|
10358 } |
|
10359 break; |
|
10360 case XML_ATTRIBUTE_NODE:{ |
|
10361 xmlAttrPtr attr = (xmlAttrPtr) cur; |
|
10362 |
|
10363 if (xmlStrEqual(name, attr->name)) { |
|
10364 if (prefix == NULL) { |
|
10365 if ((attr->ns == NULL) || |
|
10366 (attr->ns->prefix == NULL)) { |
|
10367 n++; |
|
10368 if (n == indx) |
|
10369 addNode(list, cur); |
|
10370 } |
|
10371 } else { |
|
10372 if ((attr->ns != NULL) && |
|
10373 (xmlStrEqual(URI, |
|
10374 attr->ns-> |
|
10375 href))) { |
|
10376 n++; |
|
10377 if (n == indx) |
|
10378 addNode(list, cur); |
|
10379 } |
|
10380 } |
|
10381 } |
|
10382 break; |
|
10383 } |
|
10384 case XML_NAMESPACE_DECL: |
|
10385 if (cur->type == XML_NAMESPACE_DECL) { |
|
10386 xmlNsPtr ns = (xmlNsPtr) cur; |
|
10387 |
|
10388 if ((ns->prefix != NULL) && (name != NULL) |
|
10389 && (xmlStrEqual(ns->prefix, name))) { |
|
10390 n++; |
|
10391 if (n == indx) |
|
10392 xmlXPathNodeSetAddNs(list, |
|
10393 ctxt->context->node, (xmlNsPtr) cur); |
|
10394 } |
|
10395 } |
|
10396 break; |
|
10397 default: |
|
10398 break; |
|
10399 } |
|
10400 break; |
|
10401 } |
|
10402 } while (n < indx); |
|
10403 } |
|
10404 ctxt->context->node = tmp; |
|
10405 #ifdef DEBUG_STEP_NTH |
|
10406 xmlGenericError(xmlGenericErrorContext, |
|
10407 "\nExamined %d nodes, found %d nodes at that step\n", |
|
10408 t, list->nodeNr); |
|
10409 #endif |
|
10410 valuePush(ctxt, xmlXPathWrapNodeSet(list)); |
|
10411 if(OOM_FLAG) |
|
10412 goto OOM_obj_list; |
|
10413 |
|
10414 if ((obj->boolval) && (obj->user != NULL)) { |
|
10415 ctxt->value->boolval = 1; |
|
10416 ctxt->value->user = obj->user; |
|
10417 obj->user = NULL; |
|
10418 obj->boolval = 0; |
|
10419 } |
|
10420 xmlXPathFreeObject(obj); |
|
10421 return(t); |
|
10422 |
|
10423 //---- OOM |
|
10424 OOM_obj_list: |
|
10425 xmlXPathFreeNodeSet(list); |
|
10426 OOM_obj: |
|
10427 xmlXPathFreeObject(obj); |
|
10428 return(-1); |
|
10429 } |
|
10430 |
|
10431 /** |
|
10432 * xmlXPathCompOpEvalFirst: |
|
10433 * @param ctxt the XPath parser context with the compiled expression |
|
10434 * @param op an XPath compiled operation |
|
10435 * @param first the first elem found so far |
|
10436 * |
|
10437 * Evaluate the Precompiled XPath operation searching only the first |
|
10438 * element in document order |
|
10439 * |
|
10440 * Returns the number of examined objects. |
|
10441 */ |
|
10442 static int |
|
10443 xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, |
|
10444 xmlXPathStepOpPtr op, xmlNodePtr * first) |
|
10445 { |
|
10446 int total = 0, cur; |
|
10447 xmlXPathCompExprPtr comp; |
|
10448 xmlXPathObjectPtr arg1, arg2; |
|
10449 |
|
10450 CHECK_ERROR0; |
|
10451 comp = ctxt->comp; |
|
10452 switch (op->op) { |
|
10453 case XPATH_OP_END: |
|
10454 return (0); |
|
10455 case XPATH_OP_UNION: |
|
10456 total = |
|
10457 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], |
|
10458 first); |
|
10459 CHECK_ERROR0; |
|
10460 if ((ctxt->value != NULL) |
|
10461 && (ctxt->value->type == XPATH_NODESET) |
|
10462 && (ctxt->value->nodesetval != NULL) |
|
10463 && (ctxt->value->nodesetval->nodeNr >= 1)) { |
|
10464 /* |
|
10465 * limit tree traversing to first node in the result |
|
10466 */ |
|
10467 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10468 *first = ctxt->value->nodesetval->nodeTab[0]; |
|
10469 } |
|
10470 cur = |
|
10471 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2], |
|
10472 first); |
|
10473 CHECK_ERROR0; |
|
10474 CHECK_TYPE0(XPATH_NODESET); |
|
10475 arg2 = valuePop(ctxt); |
|
10476 |
|
10477 CHECK_TYPE0(XPATH_NODESET); |
|
10478 arg1 = valuePop(ctxt); |
|
10479 |
|
10480 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, |
|
10481 arg2->nodesetval); |
|
10482 valuePush(ctxt, arg1); |
|
10483 xmlXPathFreeObject(arg2); |
|
10484 /* optimizer */ |
|
10485 if (total > cur) |
|
10486 xmlXPathCompSwap(op); |
|
10487 return (total + cur); |
|
10488 case XPATH_OP_ROOT: |
|
10489 xmlXPathRoot(ctxt); |
|
10490 return (0); |
|
10491 case XPATH_OP_NODE: |
|
10492 if (op->ch1 != -1) |
|
10493 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10494 CHECK_ERROR0; |
|
10495 if (op->ch2 != -1) |
|
10496 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10497 CHECK_ERROR0; |
|
10498 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
10499 return (total); |
|
10500 case XPATH_OP_RESET: |
|
10501 if (op->ch1 != -1) |
|
10502 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10503 CHECK_ERROR0; |
|
10504 if (op->ch2 != -1) |
|
10505 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10506 CHECK_ERROR0; |
|
10507 ctxt->context->node = NULL; |
|
10508 return (total); |
|
10509 case XPATH_OP_COLLECT:{ |
|
10510 if (op->ch1 == -1) |
|
10511 return (total); |
|
10512 |
|
10513 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10514 CHECK_ERROR0; |
|
10515 |
|
10516 /* |
|
10517 * Optimization for [n] selection where n is a number |
|
10518 */ |
|
10519 if ((op->ch2 != -1) && |
|
10520 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && |
|
10521 (comp->steps[op->ch2].ch1 == -1) && |
|
10522 (comp->steps[op->ch2].ch2 != -1) && |
|
10523 (comp->steps[comp->steps[op->ch2].ch2].op == |
|
10524 XPATH_OP_VALUE)) { |
|
10525 xmlXPathObjectPtr val; |
|
10526 |
|
10527 val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4; |
|
10528 if ((val != NULL) && (val->type == XPATH_NUMBER)) { |
|
10529 int indx = (int) val->floatval; |
|
10530 |
|
10531 if (val->floatval == (float) indx) { |
|
10532 xmlXPathNodeCollectAndTestNth(ctxt, op, indx, |
|
10533 first, NULL); |
|
10534 return (total); |
|
10535 } |
|
10536 } |
|
10537 } |
|
10538 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL); |
|
10539 return (total); |
|
10540 } |
|
10541 case XPATH_OP_VALUE: |
|
10542 valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); |
|
10543 return (0); |
|
10544 |
|
10545 case XPATH_OP_SORT: |
|
10546 if (op->ch1 != -1) |
|
10547 total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], first); |
|
10548 CHECK_ERROR0; |
|
10549 if ((ctxt->value != NULL) |
|
10550 && (ctxt->value->type == XPATH_NODESET) |
|
10551 && (ctxt->value->nodesetval != NULL)) |
|
10552 { |
|
10553 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10554 } |
|
10555 return (total); |
|
10556 |
|
10557 default: |
|
10558 return (xmlXPathCompOpEval(ctxt, op)); |
|
10559 } |
|
10560 } |
|
10561 |
|
10562 /** |
|
10563 * xmlXPathCompOpEvalLast: |
|
10564 * @param ctxt the XPath parser context with the compiled expression |
|
10565 * @param op an XPath compiled operation |
|
10566 * @param last the last elem found so far |
|
10567 * |
|
10568 * Evaluate the Precompiled XPath operation searching only the last |
|
10569 * element in document order |
|
10570 * |
|
10571 * Returns the number of nodes traversed |
|
10572 */ |
|
10573 static int |
|
10574 xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, |
|
10575 xmlNodePtr * last) |
|
10576 { |
|
10577 int total = 0, cur; |
|
10578 xmlXPathCompExprPtr comp; |
|
10579 xmlXPathObjectPtr arg1, arg2; |
|
10580 xmlNodePtr bak; |
|
10581 xmlDocPtr bakd; |
|
10582 int pp; |
|
10583 int cs; |
|
10584 |
|
10585 CHECK_ERROR0; |
|
10586 comp = ctxt->comp; |
|
10587 switch (op->op) { |
|
10588 case XPATH_OP_END: |
|
10589 return (0); |
|
10590 case XPATH_OP_UNION: |
|
10591 bakd = ctxt->context->doc; |
|
10592 bak = ctxt->context->node; |
|
10593 pp = ctxt->context->proximityPosition; |
|
10594 cs = ctxt->context->contextSize; |
|
10595 |
|
10596 total = xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last); |
|
10597 |
|
10598 CHECK_ERROR0; |
|
10599 if ((ctxt->value != NULL) |
|
10600 && (ctxt->value->type == XPATH_NODESET) |
|
10601 && (ctxt->value->nodesetval != NULL) |
|
10602 && (ctxt->value->nodesetval->nodeNr >= 1)) { |
|
10603 /* |
|
10604 * limit tree traversing to first node in the result |
|
10605 */ |
|
10606 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10607 *last = |
|
10608 ctxt->value->nodesetval->nodeTab[ctxt->value-> |
|
10609 nodesetval->nodeNr - |
|
10610 1]; |
|
10611 } |
|
10612 ctxt->context->doc = bakd; |
|
10613 ctxt->context->node = bak; |
|
10614 ctxt->context->proximityPosition = pp; |
|
10615 ctxt->context->contextSize = cs; |
|
10616 cur = |
|
10617 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last); |
|
10618 CHECK_ERROR0; |
|
10619 if ((ctxt->value != NULL) |
|
10620 && (ctxt->value->type == XPATH_NODESET) |
|
10621 && (ctxt->value->nodesetval != NULL) |
|
10622 && (ctxt->value->nodesetval->nodeNr >= 1)) { |
|
10623 } |
|
10624 CHECK_TYPE0(XPATH_NODESET); |
|
10625 arg2 = valuePop(ctxt); |
|
10626 |
|
10627 CHECK_TYPE0(XPATH_NODESET); |
|
10628 arg1 = valuePop(ctxt); |
|
10629 |
|
10630 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, |
|
10631 arg2->nodesetval); |
|
10632 valuePush(ctxt, arg1); |
|
10633 xmlXPathFreeObject(arg2); |
|
10634 /* optimizer */ |
|
10635 if (total > cur) |
|
10636 xmlXPathCompSwap(op); |
|
10637 return (total + cur); |
|
10638 case XPATH_OP_ROOT: |
|
10639 xmlXPathRoot(ctxt); |
|
10640 return (0); |
|
10641 case XPATH_OP_NODE: |
|
10642 if (op->ch1 != -1) |
|
10643 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10644 CHECK_ERROR0; |
|
10645 if (op->ch2 != -1) |
|
10646 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10647 CHECK_ERROR0; |
|
10648 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
10649 return (total); |
|
10650 case XPATH_OP_RESET: |
|
10651 if (op->ch1 != -1) |
|
10652 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10653 CHECK_ERROR0; |
|
10654 if (op->ch2 != -1) |
|
10655 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10656 CHECK_ERROR0; |
|
10657 ctxt->context->node = NULL; |
|
10658 return (total); |
|
10659 case XPATH_OP_COLLECT:{ |
|
10660 if (op->ch1 == -1) |
|
10661 return (0); |
|
10662 |
|
10663 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10664 CHECK_ERROR0; |
|
10665 |
|
10666 /* |
|
10667 * Optimization for [n] selection where n is a number |
|
10668 */ |
|
10669 if ((op->ch2 != -1) && |
|
10670 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && |
|
10671 (comp->steps[op->ch2].ch1 == -1) && |
|
10672 (comp->steps[op->ch2].ch2 != -1) && |
|
10673 (comp->steps[comp->steps[op->ch2].ch2].op == |
|
10674 XPATH_OP_VALUE)) { |
|
10675 xmlXPathObjectPtr val; |
|
10676 |
|
10677 val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4; |
|
10678 if ((val != NULL) && (val->type == XPATH_NUMBER)) { |
|
10679 int indx = (int) val->floatval; |
|
10680 |
|
10681 if (val->floatval == (float) indx) { |
|
10682 total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, last); |
|
10683 return (total); |
|
10684 } |
|
10685 } |
|
10686 } |
|
10687 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last); |
|
10688 return (total); |
|
10689 } |
|
10690 case XPATH_OP_VALUE: |
|
10691 valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); |
|
10692 return (0); |
|
10693 |
|
10694 case XPATH_OP_SORT: |
|
10695 if (op->ch1 != -1) |
|
10696 total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last); |
|
10697 CHECK_ERROR0; |
|
10698 if ((ctxt->value != NULL) |
|
10699 && (ctxt->value->type == XPATH_NODESET) |
|
10700 && (ctxt->value->nodesetval != NULL)) |
|
10701 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10702 { |
|
10703 return (total); |
|
10704 } |
|
10705 |
|
10706 default: |
|
10707 return (xmlXPathCompOpEval(ctxt, op)); |
|
10708 } |
|
10709 } |
|
10710 |
|
10711 /** |
|
10712 * xmlXPathCompOpEval: |
|
10713 * @param ctxt the XPath parser context with the compiled expression |
|
10714 * @param op an XPath compiled operation |
|
10715 * |
|
10716 * Evaluate the Precompiled XPath operation |
|
10717 * Returns the number of nodes traversed or -1 when OOM |
|
10718 */ |
|
10719 static int |
|
10720 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
|
10721 { |
|
10722 int total = 0; |
|
10723 int equal, ret; |
|
10724 xmlXPathCompExprPtr comp; |
|
10725 xmlXPathObjectPtr arg1, arg2; |
|
10726 xmlNodePtr bak; |
|
10727 xmlDocPtr bakd; |
|
10728 int pp; |
|
10729 int cs; |
|
10730 LOAD_GS_SAFE_XPATH_PARSER_CTXT(ctxt) |
|
10731 |
|
10732 CHECK_ERROR0; |
|
10733 |
|
10734 comp = ctxt->comp; |
|
10735 |
|
10736 switch (op->op) |
|
10737 { |
|
10738 case XPATH_OP_END: return (0); |
|
10739 case XPATH_OP_AND: { |
|
10740 bakd = ctxt->context->doc; |
|
10741 bak = ctxt->context->node; |
|
10742 pp = ctxt->context->proximityPosition; |
|
10743 cs = ctxt->context->contextSize; |
|
10744 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10745 CHECK_ERROR0; |
|
10746 |
|
10747 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10748 if(ctxt->context->dependencyList) |
|
10749 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10750 //-- END NEW CODE |
|
10751 |
|
10752 xmlXPathBooleanFunction(ctxt, 1); |
|
10753 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0)) |
|
10754 return (total); |
|
10755 arg2 = valuePop(ctxt); |
|
10756 ctxt->context->doc = bakd; |
|
10757 ctxt->context->node = bak; |
|
10758 ctxt->context->proximityPosition = pp; |
|
10759 ctxt->context->contextSize = cs; |
|
10760 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10761 if (ctxt->error) { |
|
10762 xmlXPathFreeObject(arg2); |
|
10763 return(0); |
|
10764 } |
|
10765 |
|
10766 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10767 if(ctxt->context->dependencyList) |
|
10768 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10769 //-- END NEW CODE |
|
10770 xmlXPathBooleanFunction(ctxt, 1); |
|
10771 arg1 = valuePop(ctxt); |
|
10772 arg1->boolval &= arg2->boolval; |
|
10773 valuePush(ctxt, arg1); |
|
10774 xmlXPathFreeObject(arg2); |
|
10775 return (total); |
|
10776 } |
|
10777 |
|
10778 case XPATH_OP_OR: { |
|
10779 bakd = ctxt->context->doc; |
|
10780 bak = ctxt->context->node; |
|
10781 pp = ctxt->context->proximityPosition; |
|
10782 cs = ctxt->context->contextSize; |
|
10783 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10784 CHECK_ERROR0; |
|
10785 |
|
10786 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10787 if(ctxt->context->dependencyList) |
|
10788 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10789 //-- END NEW CODE |
|
10790 xmlXPathBooleanFunction(ctxt, 1); |
|
10791 /* |
|
10792 // in dependency evaluation mode we must always process both parts |
|
10793 // of OR operation in the XPath expression |
|
10794 if (!ctxt->context->dependencyList && (ctxt->value || (ctxt->value->boolval == 1))) |
|
10795 return (total); |
|
10796 |
|
10797 NOTE: there is still work to do... |
|
10798 */ |
|
10799 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1)) |
|
10800 return (total); |
|
10801 /* |
|
10802 end of marked for replacement code |
|
10803 */ |
|
10804 arg2 = valuePop(ctxt); |
|
10805 ctxt->context->doc = bakd; |
|
10806 ctxt->context->node = bak; |
|
10807 ctxt->context->proximityPosition = pp; |
|
10808 ctxt->context->contextSize = cs; |
|
10809 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10810 |
|
10811 if (ctxt->error) { |
|
10812 xmlXPathFreeObject(arg2); |
|
10813 return(0); |
|
10814 } |
|
10815 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10816 if(ctxt->context->dependencyList) |
|
10817 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10818 //-- END NEW CODE |
|
10819 xmlXPathBooleanFunction(ctxt, 1); |
|
10820 arg1 = valuePop(ctxt); |
|
10821 arg1->boolval |= arg2->boolval; |
|
10822 valuePush(ctxt, arg1); |
|
10823 xmlXPathFreeObject(arg2); |
|
10824 return (total); |
|
10825 } |
|
10826 |
|
10827 case XPATH_OP_EQUAL: { |
|
10828 bakd = ctxt->context->doc; |
|
10829 bak = ctxt->context->node; |
|
10830 pp = ctxt->context->proximityPosition; |
|
10831 cs = ctxt->context->contextSize; |
|
10832 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10833 CHECK_ERROR0; |
|
10834 |
|
10835 ctxt->context->doc = bakd; |
|
10836 ctxt->context->node = bak; |
|
10837 ctxt->context->proximityPosition = pp; |
|
10838 ctxt->context->contextSize = cs; |
|
10839 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10840 CHECK_ERROR0; |
|
10841 |
|
10842 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10843 if(ctxt->context->dependencyList) |
|
10844 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10845 //-- END NEW CODE |
|
10846 |
|
10847 if (op->value) |
|
10848 equal = xmlXPathEqualValues(ctxt); |
|
10849 else |
|
10850 equal = xmlXPathNotEqualValues(ctxt); |
|
10851 |
|
10852 valuePush(ctxt, xmlXPathNewBoolean(equal)); |
|
10853 return (total); |
|
10854 } |
|
10855 |
|
10856 case XPATH_OP_CMP: { |
|
10857 bakd = ctxt->context->doc; |
|
10858 bak = ctxt->context->node; |
|
10859 pp = ctxt->context->proximityPosition; |
|
10860 cs = ctxt->context->contextSize; |
|
10861 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10862 CHECK_ERROR0; |
|
10863 |
|
10864 ctxt->context->doc = bakd; |
|
10865 ctxt->context->node = bak; |
|
10866 ctxt->context->proximityPosition = pp; |
|
10867 ctxt->context->contextSize = cs; |
|
10868 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10869 CHECK_ERROR0; |
|
10870 |
|
10871 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10872 if(ctxt->context->dependencyList) |
|
10873 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10874 //-- END NEW CODE |
|
10875 |
|
10876 ret = xmlXPathCompareValues(ctxt, op->value, op->value2); |
|
10877 valuePush(ctxt, xmlXPathNewBoolean(ret)); |
|
10878 return (total); |
|
10879 } |
|
10880 |
|
10881 case XPATH_OP_PLUS: { |
|
10882 bakd = ctxt->context->doc; |
|
10883 bak = ctxt->context->node; |
|
10884 pp = ctxt->context->proximityPosition; |
|
10885 cs = ctxt->context->contextSize; |
|
10886 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10887 CHECK_ERROR0; |
|
10888 |
|
10889 if (op->ch2 != -1) { |
|
10890 ctxt->context->doc = bakd; |
|
10891 ctxt->context->node = bak; |
|
10892 ctxt->context->proximityPosition = pp; |
|
10893 ctxt->context->contextSize = cs; |
|
10894 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10895 } |
|
10896 CHECK_ERROR0; |
|
10897 |
|
10898 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10899 |
|
10900 if(ctxt->context->dependencyList) |
|
10901 { |
|
10902 if (op->value == 0 || op->value == 1) |
|
10903 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10904 else if (op->value == 2) |
|
10905 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10906 } |
|
10907 //-- END NEW CODE |
|
10908 |
|
10909 |
|
10910 if (op->value == 0) |
|
10911 xmlXPathSubValues(ctxt); |
|
10912 else if (op->value == 1) |
|
10913 xmlXPathAddValues(ctxt); |
|
10914 else if (op->value == 2) |
|
10915 xmlXPathValueFlipSign(ctxt); |
|
10916 else if (op->value == 3) { |
|
10917 CAST_TO_NUMBER; |
|
10918 CHECK_TYPE0(XPATH_NUMBER); |
|
10919 } |
|
10920 return (total); |
|
10921 } |
|
10922 |
|
10923 case XPATH_OP_MULT: { |
|
10924 bakd = ctxt->context->doc; |
|
10925 bak = ctxt->context->node; |
|
10926 pp = ctxt->context->proximityPosition; |
|
10927 cs = ctxt->context->contextSize; |
|
10928 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10929 CHECK_ERROR0; |
|
10930 |
|
10931 ctxt->context->doc = bakd; |
|
10932 ctxt->context->node = bak; |
|
10933 ctxt->context->proximityPosition = pp; |
|
10934 ctxt->context->contextSize = cs; |
|
10935 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10936 CHECK_ERROR0; |
|
10937 |
|
10938 // XMLENGINE: NEW CODE -- XForms extensions support |
|
10939 if(ctxt->context->dependencyList) |
|
10940 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10941 //-- END NEW CODE |
|
10942 |
|
10943 if (op->value == 0) |
|
10944 xmlXPathMultValues(ctxt); |
|
10945 else if (op->value == 1) |
|
10946 xmlXPathDivValues(ctxt); |
|
10947 else if (op->value == 2) |
|
10948 xmlXPathModValues(ctxt); |
|
10949 return (total); |
|
10950 } |
|
10951 |
|
10952 case XPATH_OP_UNION: { |
|
10953 bakd = ctxt->context->doc; |
|
10954 bak = ctxt->context->node; |
|
10955 pp = ctxt->context->proximityPosition; |
|
10956 cs = ctxt->context->contextSize; |
|
10957 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10958 CHECK_ERROR0; |
|
10959 |
|
10960 ctxt->context->doc = bakd; |
|
10961 ctxt->context->node = bak; |
|
10962 ctxt->context->proximityPosition = pp; |
|
10963 ctxt->context->contextSize = cs; |
|
10964 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10965 CHECK_ERROR0; |
|
10966 |
|
10967 CHECK_TYPE0(XPATH_NODESET); |
|
10968 arg2 = valuePop(ctxt); |
|
10969 |
|
10970 CHECK_TYPE0(XPATH_NODESET); |
|
10971 arg1 = valuePop(ctxt); |
|
10972 |
|
10973 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, arg2->nodesetval); |
|
10974 valuePush(ctxt, arg1); |
|
10975 xmlXPathFreeObject(arg2); // error for "node-set($var1) | node-set($var2) |
|
10976 // -- part of arg1 that was copied from arg2 is destroyed |
|
10977 return (total); |
|
10978 } |
|
10979 |
|
10980 case XPATH_OP_ROOT: { |
|
10981 xmlXPathRoot(ctxt); |
|
10982 return (total); |
|
10983 } |
|
10984 |
|
10985 case XPATH_OP_NODE: { |
|
10986 if (op->ch1 != -1) |
|
10987 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10988 CHECK_ERROR0; |
|
10989 |
|
10990 if (op->ch2 != -1) |
|
10991 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10992 CHECK_ERROR0; |
|
10993 |
|
10994 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
10995 if(OOM_FLAG) return(-1); |
|
10996 return (total); |
|
10997 } |
|
10998 |
|
10999 case XPATH_OP_RESET: { |
|
11000 if (op->ch1 != -1) |
|
11001 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11002 CHECK_ERROR0; |
|
11003 |
|
11004 if (op->ch2 != -1) |
|
11005 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11006 CHECK_ERROR0; |
|
11007 |
|
11008 ctxt->context->node = NULL; |
|
11009 return (total); |
|
11010 } |
|
11011 |
|
11012 case XPATH_OP_COLLECT: { |
|
11013 if (op->ch1 == -1) |
|
11014 return (total); |
|
11015 |
|
11016 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11017 if(OOM_FLAG) return(-1); |
|
11018 CHECK_ERROR0; |
|
11019 |
|
11020 /* |
|
11021 * Optimization for [n] selection where n is a number |
|
11022 */ |
|
11023 if ((op->ch2 != -1) && |
|
11024 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && |
|
11025 (comp->steps[op->ch2].ch1 == -1) && |
|
11026 (comp->steps[op->ch2].ch2 != -1) && |
|
11027 (comp->steps[comp->steps[op->ch2].ch2].op == XPATH_OP_VALUE)) |
|
11028 { |
|
11029 xmlXPathObjectPtr val; |
|
11030 |
|
11031 val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4; |
|
11032 if ((val != NULL) && (val->type == XPATH_NUMBER)) { |
|
11033 int indx = (int) val->floatval; |
|
11034 |
|
11035 if (val->floatval == (float) indx) { |
|
11036 total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, NULL); |
|
11037 if(OOM_FLAG) return(-1); |
|
11038 return (total); |
|
11039 } |
|
11040 } |
|
11041 } |
|
11042 |
|
11043 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL); |
|
11044 if(OOM_FLAG) return(-1); |
|
11045 return (total); |
|
11046 } |
|
11047 |
|
11048 case XPATH_OP_VALUE: { |
|
11049 valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); |
|
11050 return (total); |
|
11051 } |
|
11052 |
|
11053 case XPATH_OP_VARIABLE: { |
|
11054 xmlXPathObjectPtr val; |
|
11055 |
|
11056 if (op->ch1 != -1) |
|
11057 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11058 |
|
11059 if (!op->value5) { |
|
11060 val = xmlXPathVariableLookup(ctxt->context, (xmlChar*) op->value4); |
|
11061 if (!val) { |
|
11062 ctxt->error = XPATH_UNDEF_VARIABLE_ERROR; |
|
11063 return(0); |
|
11064 } |
|
11065 valuePush(ctxt, val); |
|
11066 } else { |
|
11067 const xmlChar *URI; |
|
11068 |
|
11069 URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5); |
|
11070 if (URI == NULL) { |
|
11071 xmlGenericError(xmlGenericErrorContext, |
|
11072 EMBED_ERRTXT("xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n"), |
|
11073 op->value4, op->value5); |
|
11074 return (total); |
|
11075 } |
|
11076 val = xmlXPathVariableLookupNS(ctxt->context, (xmlChar*) op->value4, URI); |
|
11077 if (val == NULL) { |
|
11078 ctxt->error = XPATH_UNDEF_VARIABLE_ERROR; |
|
11079 return(0); |
|
11080 } |
|
11081 valuePush(ctxt, val); |
|
11082 } |
|
11083 return (total); |
|
11084 } |
|
11085 |
|
11086 case XPATH_OP_FUNCTION: { |
|
11087 xmlXPathFunction func; |
|
11088 const xmlChar *oldFunc, *oldFuncURI; |
|
11089 int i; |
|
11090 |
|
11091 if (op->ch1 != -1) |
|
11092 { |
|
11093 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11094 if(OOM_FLAG) return(-1); |
|
11095 } |
|
11096 |
|
11097 if (ctxt->valueNr < op->value) { |
|
11098 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n")); |
|
11099 ctxt->error = XPATH_INVALID_OPERAND; |
|
11100 return (total); |
|
11101 } |
|
11102 |
|
11103 for (i = 0; i < op->value; i++) |
|
11104 if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) { |
|
11105 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n")); |
|
11106 ctxt->error = XPATH_INVALID_OPERAND; |
|
11107 return (total); |
|
11108 } |
|
11109 |
|
11110 if (op->cache != NULL){ |
|
11111 func = (xmlXPathFunction)op->cache; |
|
11112 }else { |
|
11113 const xmlChar* URI = NULL; |
|
11114 |
|
11115 if (!op->value5){ |
|
11116 func = xmlXPathFunctionLookup(ctxt->context, (xmlChar*)op->value4); |
|
11117 } else { |
|
11118 URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5); |
|
11119 if (!URI) { |
|
11120 xmlGenericError(xmlGenericErrorContext, |
|
11121 EMBED_ERRTXT("xmlXPathCompOpEval: function %s bound to undefined prefix %s\n"), |
|
11122 op->value4, op->value5); |
|
11123 return (total); |
|
11124 } |
|
11125 func = xmlXPathFunctionLookupNS(ctxt->context, (xmlChar*)op->value4, URI); |
|
11126 } |
|
11127 |
|
11128 if (!func) { |
|
11129 xmlGenericError(xmlGenericErrorContext, |
|
11130 EMBED_ERRTXT("xmlXPathCompOpEval: function %s not found\n"), op->value4); |
|
11131 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR); |
|
11132 } |
|
11133 |
|
11134 op->cache = (void*) func; |
|
11135 op->cacheURI = (void*) URI; |
|
11136 } // if (op->cache != NULL) |
|
11137 |
|
11138 oldFunc = ctxt->context->function; |
|
11139 oldFuncURI = ctxt->context->functionURI; |
|
11140 ctxt->context->function = (xmlChar*) op->value4; |
|
11141 ctxt->context->functionURI = (xmlChar*) op->cacheURI; |
|
11142 |
|
11143 // XMLENGINE: NEW CODE -- XForms extensions support |
|
11144 if(ctxt->context->dependencyList) |
|
11145 addNodeSetsFromStackToDependencyList(ctxt, op->value); |
|
11146 //-- END NEW CODE |
|
11147 |
|
11148 func(ctxt, op->value); |
|
11149 if(OOM_FLAG) return(-1); |
|
11150 ctxt->context->function = oldFunc; |
|
11151 ctxt->context->functionURI = oldFuncURI; |
|
11152 return (total); |
|
11153 } |
|
11154 |
|
11155 case XPATH_OP_ARG: { |
|
11156 bakd = ctxt->context->doc; |
|
11157 bak = ctxt->context->node; |
|
11158 if (op->ch1 != -1) |
|
11159 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11160 ctxt->context->doc = bakd; |
|
11161 ctxt->context->node = bak; |
|
11162 CHECK_ERROR0; |
|
11163 |
|
11164 if (op->ch2 != -1) { |
|
11165 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11166 if(OOM_FLAG) return(-1); |
|
11167 ctxt->context->doc = bakd; |
|
11168 ctxt->context->node = bak; |
|
11169 CHECK_ERROR0; |
|
11170 } |
|
11171 return (total); |
|
11172 } |
|
11173 |
|
11174 case XPATH_OP_PREDICATE: |
|
11175 case XPATH_OP_FILTER: { |
|
11176 xmlXPathObjectPtr res; |
|
11177 xmlXPathObjectPtr obj, tmp; |
|
11178 xmlNodeSetPtr newset = NULL; |
|
11179 xmlNodeSetPtr oldset; |
|
11180 xmlNodePtr oldnode; |
|
11181 int i; |
|
11182 |
|
11183 /* |
|
11184 * Optimization for ()[1] selection i.e. the first elem |
|
11185 */ |
|
11186 if ((op->ch1 != -1) && (op->ch2 != -1) && |
|
11187 (comp->steps[op->ch1].op == XPATH_OP_SORT) && |
|
11188 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) |
|
11189 { |
|
11190 xmlXPathObjectPtr val; |
|
11191 |
|
11192 val = (xmlXPathObjectPtr)comp->steps[op->ch2].value4; |
|
11193 |
|
11194 if ((val != NULL) && (val->type == XPATH_NUMBER) && (val->floatval == 1.0)) |
|
11195 { |
|
11196 xmlNodePtr first = NULL; |
|
11197 |
|
11198 total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], &first); |
|
11199 CHECK_ERROR0; |
|
11200 /* |
|
11201 * The nodeset should be in document order, |
|
11202 * Keep only the first value |
|
11203 */ |
|
11204 if ((ctxt->value != NULL) && |
|
11205 (ctxt->value->type == XPATH_NODESET) && |
|
11206 (ctxt->value->nodesetval != NULL) && |
|
11207 (ctxt->value->nodesetval->nodeNr > 1)) |
|
11208 { |
|
11209 ctxt->value->nodesetval->nodeNr = 1; |
|
11210 } |
|
11211 return (total); |
|
11212 } |
|
11213 } |
|
11214 |
|
11215 /* |
|
11216 * Optimization for ()[last()] selection i.e. the last elem |
|
11217 */ |
|
11218 if ((op->ch1 != -1) && (op->ch2 != -1) && |
|
11219 (comp->steps[op->ch1].op == XPATH_OP_SORT) && |
|
11220 (comp->steps[op->ch2].op == XPATH_OP_SORT)) |
|
11221 { |
|
11222 int f = comp->steps[op->ch2].ch1; |
|
11223 |
|
11224 if ((f != -1) && |
|
11225 (comp->steps[f].op == XPATH_OP_FUNCTION) && |
|
11226 (comp->steps[f].value5 == NULL) && |
|
11227 (comp->steps[f].value == 0) && |
|
11228 (comp->steps[f].value4 != NULL) && |
|
11229 (xmlStrEqual((xmlChar*)comp->steps[f].value4, BAD_CAST "last"))) |
|
11230 { |
|
11231 xmlNodePtr last = NULL; |
|
11232 |
|
11233 total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], &last); |
|
11234 CHECK_ERROR0; |
|
11235 /* |
|
11236 * The nodeset should be in document order, |
|
11237 * Keep only the last value |
|
11238 */ |
|
11239 if ((ctxt->value != NULL) && |
|
11240 (ctxt->value->type == XPATH_NODESET) && |
|
11241 (ctxt->value->nodesetval != NULL) && |
|
11242 (ctxt->value->nodesetval->nodeTab != NULL) && |
|
11243 (ctxt->value->nodesetval->nodeNr > 1)) |
|
11244 { |
|
11245 ctxt->value->nodesetval->nodeTab[0] = |
|
11246 ctxt->value->nodesetval->nodeTab[ctxt->value->nodesetval->nodeNr - 1]; |
|
11247 ctxt->value->nodesetval->nodeNr = 1; |
|
11248 } |
|
11249 return (total); |
|
11250 } |
|
11251 } |
|
11252 |
|
11253 |
|
11254 if (op->ch1 != -1) |
|
11255 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11256 |
|
11257 CHECK_ERROR0; |
|
11258 |
|
11259 if (op->ch2 == -1) |
|
11260 return (total); |
|
11261 |
|
11262 if (ctxt->value == NULL) |
|
11263 return (total); |
|
11264 |
|
11265 oldnode = ctxt->context->node; |
|
11266 |
|
11267 #ifdef LIBXML_XPTR_ENABLED |
|
11268 /* |
|
11269 * Hum are we filtering the result of an XPointer expression |
|
11270 */ |
|
11271 if (ctxt->value->type == XPATH_LOCATIONSET) |
|
11272 { |
|
11273 xmlLocationSetPtr newlocset = NULL; |
|
11274 xmlLocationSetPtr oldlocset; |
|
11275 |
|
11276 /* |
|
11277 * Extract the old locset, and then evaluate the result of the |
|
11278 * expression for all the element in the locset. use it to grow |
|
11279 * up a new locset. |
|
11280 */ |
|
11281 CHECK_TYPE0(XPATH_LOCATIONSET); |
|
11282 |
|
11283 obj = valuePop(ctxt); |
|
11284 oldlocset = obj->user; |
|
11285 ctxt->context->node = NULL; |
|
11286 |
|
11287 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { |
|
11288 ctxt->context->contextSize = 0; |
|
11289 ctxt->context->proximityPosition = 0; |
|
11290 |
|
11291 if (op->ch2 != -1) |
|
11292 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11293 |
|
11294 res = valuePop(ctxt); |
|
11295 |
|
11296 if (res != NULL) |
|
11297 xmlXPathFreeObject(res); |
|
11298 |
|
11299 valuePush(ctxt, obj); |
|
11300 CHECK_ERROR0; |
|
11301 |
|
11302 return (total); |
|
11303 } |
|
11304 |
|
11305 newlocset = xmlXPtrLocationSetCreate(NULL); |
|
11306 |
|
11307 for (i = 0; i < oldlocset->locNr; i++) |
|
11308 { |
|
11309 /* |
|
11310 * Run the evaluation with a node list made of a |
|
11311 * single item in the nodelocset. |
|
11312 */ |
|
11313 ctxt->context->node = oldlocset->locTab[i]->user; |
|
11314 ctxt->context->contextSize = oldlocset->locNr; |
|
11315 ctxt->context->proximityPosition = i + 1; |
|
11316 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11317 valuePush(ctxt, tmp); |
|
11318 |
|
11319 if (op->ch2 != -1) |
|
11320 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11321 |
|
11322 CHECK_ERROR0; |
|
11323 /* |
|
11324 * The result of the evaluation need to be tested to |
|
11325 * decided whether the filter succeeded or not |
|
11326 */ |
|
11327 res = valuePop(ctxt); |
|
11328 if (xmlXPathEvaluatePredicateResult(ctxt, res)) |
|
11329 { |
|
11330 xmlXPtrLocationSetAdd(newlocset, xmlXPathObjectCopy(oldlocset->locTab[i])); |
|
11331 } |
|
11332 |
|
11333 /* |
|
11334 * Cleanup |
|
11335 */ |
|
11336 if (res != NULL) |
|
11337 xmlXPathFreeObject(res); |
|
11338 if (ctxt->value == tmp) { |
|
11339 res = valuePop(ctxt); |
|
11340 xmlXPathFreeObject(res); |
|
11341 } |
|
11342 |
|
11343 ctxt->context->node = NULL; |
|
11344 } // for (i = 0; i < oldlocset->locNr; i++) |
|
11345 |
|
11346 /* |
|
11347 * The result is used as the new evaluation locset. |
|
11348 */ |
|
11349 xmlXPathFreeObject(obj); |
|
11350 ctxt->context->node = NULL; |
|
11351 ctxt->context->contextSize = -1; |
|
11352 ctxt->context->proximityPosition = -1; |
|
11353 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); |
|
11354 ctxt->context->node = oldnode; |
|
11355 return (total); |
|
11356 } |
|
11357 #endif /* LIBXML_XPTR_ENABLED */ |
|
11358 |
|
11359 /* |
|
11360 * Extract the old set, and then evaluate the result of the |
|
11361 * expression for all the element in the set. use it to grow |
|
11362 * up a new set. |
|
11363 */ |
|
11364 CHECK_TYPE0(XPATH_NODESET); |
|
11365 |
|
11366 obj = valuePop(ctxt); |
|
11367 oldset = obj->nodesetval; |
|
11368 oldnode = ctxt->context->node; |
|
11369 ctxt->context->node = NULL; |
|
11370 |
|
11371 if ((oldset == NULL) || (oldset->nodeNr == 0)) |
|
11372 { |
|
11373 ctxt->context->contextSize = 0; |
|
11374 ctxt->context->proximityPosition = 0; |
|
11375 |
|
11376 if (op->ch2 != -1) |
|
11377 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11378 |
|
11379 CHECK_ERROR0; |
|
11380 |
|
11381 res = valuePop(ctxt); |
|
11382 if (res != NULL) |
|
11383 xmlXPathFreeObject(res); |
|
11384 |
|
11385 valuePush(ctxt, obj); |
|
11386 ctxt->context->node = oldnode; |
|
11387 CHECK_ERROR0; |
|
11388 } else { |
|
11389 /* |
|
11390 * Initialize the new set. |
|
11391 */ |
|
11392 newset = xmlXPathNodeSetCreate(NULL); |
|
11393 if(!newset) |
|
11394 { |
|
11395 XP_ERROR0(XPATH_MEMORY_ERROR); // returns 0 |
|
11396 } |
|
11397 for (i = 0; i < oldset->nodeNr; i++) { |
|
11398 /* |
|
11399 * Run the evaluation with a node list made of |
|
11400 * a single item in the nodeset. |
|
11401 */ |
|
11402 ctxt->context->node = oldset->nodeTab[i]; |
|
11403 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11404 valuePush(ctxt, tmp); |
|
11405 ctxt->context->contextSize = oldset->nodeNr; |
|
11406 ctxt->context->proximityPosition = i + 1; |
|
11407 |
|
11408 if (op->ch2 != -1) |
|
11409 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11410 |
|
11411 CHECK_ERROR0; |
|
11412 |
|
11413 /* |
|
11414 * The result of the evaluation needs to be tested to |
|
11415 * decide whether the filter succeeded or not |
|
11416 */ |
|
11417 res = valuePop(ctxt); |
|
11418 if (xmlXPathEvaluatePredicateResult(ctxt, res)) |
|
11419 { |
|
11420 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); |
|
11421 } |
|
11422 |
|
11423 /* |
|
11424 * Cleanup |
|
11425 */ |
|
11426 if (res != NULL) |
|
11427 xmlXPathFreeObject(res); |
|
11428 if (ctxt->value == tmp) { |
|
11429 res = valuePop(ctxt); |
|
11430 xmlXPathFreeObject(res); |
|
11431 } |
|
11432 |
|
11433 ctxt->context->node = NULL; |
|
11434 } // (i = 0; i < oldset->nodeNr; i++) |
|
11435 |
|
11436 /* |
|
11437 * The result is used as the new evaluation set. |
|
11438 */ |
|
11439 xmlXPathFreeObject(obj); |
|
11440 ctxt->context->node = NULL; |
|
11441 ctxt->context->contextSize = -1; |
|
11442 ctxt->context->proximityPosition = -1; |
|
11443 valuePush(ctxt, xmlXPathWrapNodeSet(newset)); |
|
11444 } |
|
11445 ctxt->context->node = oldnode; |
|
11446 return (total); |
|
11447 } |
|
11448 |
|
11449 case XPATH_OP_SORT: { |
|
11450 if (op->ch1 != -1) |
|
11451 { |
|
11452 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11453 if(OOM_FLAG) return(-1); |
|
11454 } |
|
11455 |
|
11456 CHECK_ERROR0; |
|
11457 |
|
11458 if (ctxt->value && |
|
11459 ctxt->value->type == XPATH_NODESET && |
|
11460 ctxt->value->nodesetval) |
|
11461 { |
|
11462 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
11463 } |
|
11464 return (total); |
|
11465 } |
|
11466 |
|
11467 #ifdef LIBXML_XPTR_ENABLED |
|
11468 case XPATH_OP_RANGETO: { |
|
11469 xmlXPathObjectPtr range; |
|
11470 xmlXPathObjectPtr res, obj; |
|
11471 xmlXPathObjectPtr tmp; |
|
11472 xmlLocationSetPtr newlocset = NULL; |
|
11473 xmlLocationSetPtr oldlocset; |
|
11474 xmlNodeSetPtr oldset; |
|
11475 int i, j; |
|
11476 |
|
11477 if (op->ch1 != -1) |
|
11478 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11479 if (op->ch2 == -1) |
|
11480 return (total); |
|
11481 |
|
11482 if (ctxt->value->type == XPATH_LOCATIONSET) |
|
11483 { |
|
11484 /* |
|
11485 * Extract the old locset, and then evaluate the result of the |
|
11486 * expression for all the element in the locset. use it to grow |
|
11487 * up a new locset. |
|
11488 */ |
|
11489 CHECK_TYPE0(XPATH_LOCATIONSET); |
|
11490 obj = valuePop(ctxt); |
|
11491 oldlocset = obj->user; |
|
11492 |
|
11493 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { |
|
11494 ctxt->context->node = NULL; |
|
11495 ctxt->context->contextSize = 0; |
|
11496 ctxt->context->proximityPosition = 0; |
|
11497 |
|
11498 total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]); |
|
11499 |
|
11500 res = valuePop(ctxt); |
|
11501 if (res != NULL) |
|
11502 xmlXPathFreeObject(res); |
|
11503 valuePush(ctxt, obj); |
|
11504 CHECK_ERROR0; |
|
11505 return (total); |
|
11506 } |
|
11507 newlocset = xmlXPtrLocationSetCreate(NULL); |
|
11508 |
|
11509 for (i = 0; i < oldlocset->locNr; i++) { |
|
11510 /* |
|
11511 * Run the evaluation with a node list made of a |
|
11512 * single item in the nodelocset. |
|
11513 */ |
|
11514 ctxt->context->node = oldlocset->locTab[i]->user; |
|
11515 ctxt->context->contextSize = oldlocset->locNr; |
|
11516 ctxt->context->proximityPosition = i + 1; |
|
11517 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11518 valuePush(ctxt, tmp); |
|
11519 |
|
11520 if (op->ch2 != -1) |
|
11521 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11522 |
|
11523 CHECK_ERROR0; |
|
11524 |
|
11525 res = valuePop(ctxt); |
|
11526 if (res->type == XPATH_LOCATIONSET) |
|
11527 { |
|
11528 xmlLocationSetPtr rloc = (xmlLocationSetPtr)res->user; |
|
11529 |
|
11530 for (j=0; j<rloc->locNr; j++) { |
|
11531 range = xmlXPtrNewRange( |
|
11532 oldlocset->locTab[i]->user, |
|
11533 oldlocset->locTab[i]->index, |
|
11534 rloc->locTab[j]->user2, |
|
11535 rloc->locTab[j]->index2); |
|
11536 |
|
11537 if (range != NULL) { |
|
11538 xmlXPtrLocationSetAdd(newlocset, range); |
|
11539 } |
|
11540 } |
|
11541 } else { |
|
11542 range = xmlXPtrNewRangeNodeObject((xmlNodePtr)oldlocset->locTab[i]->user, res); |
|
11543 if (range != NULL) { |
|
11544 xmlXPtrLocationSetAdd(newlocset,range); |
|
11545 } |
|
11546 } |
|
11547 |
|
11548 /* |
|
11549 * Cleanup |
|
11550 */ |
|
11551 if (res != NULL) |
|
11552 xmlXPathFreeObject(res); |
|
11553 if (ctxt->value == tmp) { |
|
11554 res = valuePop(ctxt); |
|
11555 xmlXPathFreeObject(res); |
|
11556 } |
|
11557 |
|
11558 ctxt->context->node = NULL; |
|
11559 } |
|
11560 |
|
11561 } |
|
11562 else |
|
11563 { /* Not a location set */ |
|
11564 CHECK_TYPE0(XPATH_NODESET); |
|
11565 obj = valuePop(ctxt); |
|
11566 oldset = obj->nodesetval; |
|
11567 ctxt->context->node = NULL; |
|
11568 |
|
11569 newlocset = xmlXPtrLocationSetCreate(NULL); |
|
11570 |
|
11571 if (oldset != NULL) { |
|
11572 for (i = 0; i < oldset->nodeNr; i++) { |
|
11573 /* |
|
11574 * Run the evaluation with a node list made of a single item |
|
11575 * in the nodeset. |
|
11576 */ |
|
11577 ctxt->context->node = oldset->nodeTab[i]; |
|
11578 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11579 valuePush(ctxt, tmp); |
|
11580 |
|
11581 if (op->ch2 != -1) |
|
11582 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11583 |
|
11584 CHECK_ERROR0; |
|
11585 |
|
11586 res = valuePop(ctxt); |
|
11587 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res); |
|
11588 if (range != NULL) { |
|
11589 xmlXPtrLocationSetAdd(newlocset, range); |
|
11590 } |
|
11591 |
|
11592 /* |
|
11593 * Cleanup |
|
11594 */ |
|
11595 if (res != NULL) |
|
11596 xmlXPathFreeObject(res); |
|
11597 if (ctxt->value == tmp) { |
|
11598 res = valuePop(ctxt); |
|
11599 xmlXPathFreeObject(res); |
|
11600 } |
|
11601 |
|
11602 ctxt->context->node = NULL; |
|
11603 } // for |
|
11604 } |
|
11605 } |
|
11606 |
|
11607 /* |
|
11608 * The result is used as the new evaluation set. |
|
11609 */ |
|
11610 xmlXPathFreeObject(obj); |
|
11611 ctxt->context->node = NULL; |
|
11612 ctxt->context->contextSize = -1; |
|
11613 ctxt->context->proximityPosition = -1; |
|
11614 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); |
|
11615 return (total); |
|
11616 } |
|
11617 #endif /* LIBXML_XPTR_ENABLED */ |
|
11618 } // switch(op->op) |
|
11619 |
|
11620 // None of options matched |
|
11621 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("XPath: unknown precompiled operation %d\n"), op->op); |
|
11622 return (total); |
|
11623 } |
|
11624 |
|
11625 /** |
|
11626 * xmlXPathRunEval: |
|
11627 * @param ctxt the XPath parser context with the compiled expression |
|
11628 * |
|
11629 * Evaluate the Precompiled XPath expression in the given context. |
|
11630 */ |
|
11631 static void |
|
11632 xmlXPathRunEval(xmlXPathParserContextPtr ctxt) { |
|
11633 xmlXPathCompExprPtr comp; |
|
11634 |
|
11635 if (!ctxt || !ctxt->comp) |
|
11636 return; |
|
11637 |
|
11638 if (!ctxt->valueTab) { |
|
11639 /* Allocate the value stack */ |
|
11640 |
|
11641 ctxt->valueTab = (xmlXPathObjectPtr *) |
|
11642 xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); |
|
11643 if (!ctxt->valueTab) { |
|
11644 xmlXPathPErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n")); |
|
11645 xmlFree(ctxt); |
|
11646 } |
|
11647 ctxt->valueNr = 0; |
|
11648 ctxt->valueMax = 10; |
|
11649 ctxt->value = NULL; |
|
11650 } |
|
11651 comp = ctxt->comp; |
|
11652 if(comp->last < 0) { |
|
11653 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathRunEval: last is less than zero\n")); |
|
11654 return; |
|
11655 |
|
11656 } |
|
11657 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]); |
|
11658 } |
|
11659 |
|
11660 /************************************************************************ |
|
11661 * * |
|
11662 * Public interfaces * |
|
11663 * * |
|
11664 ************************************************************************/ |
|
11665 |
|
11666 /** |
|
11667 * xmlXPathEvalPredicate: |
|
11668 * @param ctxt the XPath context |
|
11669 * @param res the Predicate Expression evaluation result |
|
11670 * |
|
11671 * Evaluate a predicate result for the current node. |
|
11672 * A PredicateExpr is evaluated by evaluating the Expr and converting |
|
11673 * the result to a boolean. If the result is a number, the result will |
|
11674 * be converted to true if the number is equal to the position of the |
|
11675 * context node in the context node list (as returned by the position |
|
11676 * function) and will be converted to false otherwise; if the result |
|
11677 * is not a number, then the result will be converted as if by a call |
|
11678 * to the boolean function. |
|
11679 * |
|
11680 * Returns 1 if predicate is true, 0 otherwise |
|
11681 */ |
|
11682 XMLPUBFUNEXPORT int |
|
11683 xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) { |
|
11684 if (res == NULL) return(0); |
|
11685 switch (res->type) { |
|
11686 case XPATH_BOOLEAN: |
|
11687 return(res->boolval); |
|
11688 case XPATH_NUMBER: |
|
11689 return(res->floatval == ctxt->proximityPosition); |
|
11690 case XPATH_NODESET: |
|
11691 case XPATH_XSLT_TREE: |
|
11692 return res->nodesetval |
|
11693 ? |
|
11694 res->nodesetval->nodeNr != 0 |
|
11695 : 0; |
|
11696 case XPATH_STRING: |
|
11697 return |
|
11698 res->stringval && |
|
11699 xmlStrlen(res->stringval) != 0; |
|
11700 default: |
|
11701 STRANGE |
|
11702 } |
|
11703 return(0); |
|
11704 } |
|
11705 |
|
11706 /** |
|
11707 * xmlXPathEvaluatePredicateResult: |
|
11708 * @param ctxt the XPath Parser context |
|
11709 * @param res the Predicate Expression evaluation result |
|
11710 * |
|
11711 * Evaluate a predicate result for the current node. |
|
11712 * A PredicateExpr is evaluated by evaluating the Expr and converting |
|
11713 * the result to a boolean. If the result is a number, the result will |
|
11714 * be converted to true if the number is equal to the position of the |
|
11715 * context node in the context node list (as returned by the position |
|
11716 * function) and will be converted to false otherwise; if the result |
|
11717 * is not a number, then the result will be converted as if by a call |
|
11718 * to the boolean function. |
|
11719 * |
|
11720 * Returns 1 if predicate is true, 0 otherwise |
|
11721 */ |
|
11722 XMLPUBFUNEXPORT int |
|
11723 xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, |
|
11724 xmlXPathObjectPtr res) { |
|
11725 if (res == NULL) return(0); |
|
11726 switch (res->type) { |
|
11727 case XPATH_BOOLEAN: |
|
11728 return(res->boolval); |
|
11729 case XPATH_NUMBER: |
|
11730 #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200)) |
|
11731 return((res->floatval == ctxt->context->proximityPosition) && |
|
11732 (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/ |
|
11733 #else |
|
11734 return(res->floatval == ctxt->context->proximityPosition); |
|
11735 #endif |
|
11736 case XPATH_NODESET: |
|
11737 case XPATH_XSLT_TREE: |
|
11738 if (res->nodesetval == NULL) |
|
11739 return(0); |
|
11740 return(res->nodesetval->nodeNr != 0); |
|
11741 case XPATH_STRING: |
|
11742 return((res->stringval != NULL) && |
|
11743 (xmlStrlen(res->stringval) != 0)); |
|
11744 #ifdef LIBXML_XPTR_ENABLED |
|
11745 case XPATH_LOCATIONSET:{ |
|
11746 xmlLocationSetPtr ptr = res->user; |
|
11747 if (ptr == NULL) |
|
11748 return(0); |
|
11749 return (ptr->locNr != 0); |
|
11750 } |
|
11751 #endif |
|
11752 default: |
|
11753 STRANGE |
|
11754 } |
|
11755 return(0); |
|
11756 } |
|
11757 |
|
11758 /** |
|
11759 * xmlXPathCtxtCompile: |
|
11760 * @param ctxt an XPath context |
|
11761 * @param str the XPath expression |
|
11762 * |
|
11763 * Compile an XPath expression |
|
11764 * |
|
11765 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. |
|
11766 * the caller has to free the object. |
|
11767 */ |
|
11768 XMLPUBFUNEXPORT xmlXPathCompExprPtr |
|
11769 |
|
11770 xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { |
|
11771 xmlXPathParserContextPtr pctxt; |
|
11772 xmlXPathCompExprPtr comp; |
|
11773 LOAD_GS_SAFE_XPATH_CTXT(ctxt) |
|
11774 |
|
11775 xmlXPathInit(); |
|
11776 |
|
11777 pctxt = xmlXPathNewParserContext(str, ctxt); |
|
11778 if(!pctxt) |
|
11779 return NULL; // OOM |
|
11780 |
|
11781 xmlXPathCompileExpr(pctxt); |
|
11782 |
|
11783 if( pctxt->error != XPATH_EXPRESSION_OK || OOM_FLAG) |
|
11784 { |
|
11785 xmlXPathFreeParserContext(pctxt); |
|
11786 return NULL; |
|
11787 } |
|
11788 |
|
11789 if (*pctxt->cur) { |
|
11790 /* |
|
11791 * aleksey: in some cases this line prints *second* error message |
|
11792 * (see bug#78858) and probably this should be fixed. |
|
11793 * However, we are not sure that all error messages are printed |
|
11794 * out in other places. It's not critical so we leave it as-is for now |
|
11795 */ |
|
11796 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); |
|
11797 comp = NULL; |
|
11798 } else { |
|
11799 comp = pctxt->comp; |
|
11800 pctxt->comp = NULL; |
|
11801 } |
|
11802 xmlXPathFreeParserContext(pctxt); |
|
11803 if (comp) { |
|
11804 comp->expr = xmlStrdup(str); |
|
11805 #ifdef DEBUG_EVAL_COUNTS |
|
11806 comp->string = xmlStrdup(str); |
|
11807 comp->nb = 0; |
|
11808 #endif |
|
11809 } |
|
11810 return(comp); |
|
11811 } |
|
11812 |
|
11813 /** |
|
11814 * xmlXPathCompile: |
|
11815 * @param str the XPath expression |
|
11816 * |
|
11817 * Compile an XPath expression |
|
11818 * |
|
11819 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. |
|
11820 * the caller has to free the object. |
|
11821 */ |
|
11822 XMLPUBFUNEXPORT xmlXPathCompExprPtr |
|
11823 xmlXPathCompile(const xmlChar *str) { |
|
11824 return(xmlXPathCtxtCompile(NULL, str)); |
|
11825 } |
|
11826 |
|
11827 /** |
|
11828 * xmlXPathCompiledEval: |
|
11829 * @param comp the compiled XPath expression |
|
11830 * @param ctx the XPath context |
|
11831 * |
|
11832 * Evaluate the Precompiled XPath expression in the given context. |
|
11833 * |
|
11834 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
11835 * the caller has to free the object. |
|
11836 */ |
|
11837 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
11838 xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) { |
|
11839 xmlXPathParserContextPtr ctxt; |
|
11840 xmlXPathObjectPtr res, tmp, init = NULL; |
|
11841 int stack = 0; |
|
11842 #ifndef LIBXML_THREAD_ENABLED |
|
11843 //FIXIT |
|
11844 //static |
|
11845 int reentance = 0; |
|
11846 #endif |
|
11847 LOAD_GS_SAFE_XPATH_CTXT(ctx) |
|
11848 |
|
11849 if (!comp || !ctx) |
|
11850 return(NULL); |
|
11851 xmlXPathInit(); |
|
11852 |
|
11853 CHECK_CONTEXT(ctx) |
|
11854 |
|
11855 #ifndef LIBXML_THREAD_ENABLED |
|
11856 reentance++; |
|
11857 if (reentance > 1) |
|
11858 xmlXPathDisableOptimizer = 1; |
|
11859 #endif |
|
11860 |
|
11861 #ifdef DEBUG_EVAL_COUNTS |
|
11862 comp->nb++; |
|
11863 if ((comp->string != NULL) && (comp->nb > 100)) { |
|
11864 fprintf(stderr, "100 x %s\n", comp->string); |
|
11865 comp->nb = 0; |
|
11866 } |
|
11867 #endif |
|
11868 ctxt = xmlXPathCompParserContext(comp, ctx); |
|
11869 xmlXPathRunEval(ctxt); //might set OOM flag |
|
11870 if(OOM_FLAG) |
|
11871 { |
|
11872 if(ctxt) |
|
11873 { |
|
11874 ctxt->comp = NULL; |
|
11875 xmlXPathFreeParserContext(ctxt); |
|
11876 } |
|
11877 return(NULL); |
|
11878 } |
|
11879 |
|
11880 if (ctxt->value == NULL) { |
|
11881 xmlGenericError(xmlGenericErrorContext, |
|
11882 EMBED_ERRTXT("xmlXPathCompiledEval: evaluation failed\n")); |
|
11883 res = NULL; |
|
11884 } else { |
|
11885 res = valuePop(ctxt); |
|
11886 } |
|
11887 |
|
11888 |
|
11889 do { |
|
11890 tmp = valuePop(ctxt); |
|
11891 if (tmp != NULL) { |
|
11892 if (tmp != init) |
|
11893 stack++; |
|
11894 xmlXPathFreeObject(tmp); |
|
11895 } |
|
11896 } while (tmp != NULL); |
|
11897 if ((stack != 0) && (res != NULL)) { |
|
11898 xmlGenericError(xmlGenericErrorContext, |
|
11899 EMBED_ERRTXT("xmlXPathCompiledEval: %d object left on the stack\n"), |
|
11900 stack); |
|
11901 } |
|
11902 if (ctxt->error != XPATH_EXPRESSION_OK) { |
|
11903 xmlXPathFreeObject(res); |
|
11904 res = NULL; |
|
11905 } |
|
11906 |
|
11907 ctxt->comp = NULL; |
|
11908 xmlXPathFreeParserContext(ctxt); |
|
11909 #ifndef LIBXML_THREAD_ENABLED |
|
11910 reentance--; |
|
11911 #endif |
|
11912 return(res); |
|
11913 } |
|
11914 |
|
11915 /** |
|
11916 * xmlXPathEvalExpr: |
|
11917 * @param ctxt the XPath Parser context |
|
11918 * |
|
11919 * Parse and evaluate an XPath expression in the given context, |
|
11920 * then push the result on the context stack |
|
11921 * |
|
11922 * OOM: possible --> check OOM flag |
|
11923 */ |
|
11924 XMLPUBFUNEXPORT void |
|
11925 xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { |
|
11926 xmlXPathCompileExpr(ctxt); |
|
11927 CHECK_ERROR; |
|
11928 xmlXPathRunEval(ctxt); |
|
11929 } |
|
11930 |
|
11931 /** |
|
11932 * xmlXPathEval: |
|
11933 * @param str the XPath expression |
|
11934 * @param ctx the XPath context |
|
11935 * |
|
11936 * Evaluate the XPath Location Path in the given context. |
|
11937 * |
|
11938 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
11939 * the caller has to free the object. |
|
11940 */ |
|
11941 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
11942 xmlXPathEval(const xmlChar* str, xmlXPathContextPtr ctx) { |
|
11943 xmlXPathParserContextPtr ctxt; |
|
11944 xmlXPathObjectPtr res, tmp, init = NULL; |
|
11945 int stack = 0; |
|
11946 |
|
11947 xmlXPathInit(); |
|
11948 |
|
11949 CHECK_CONTEXT(ctx) |
|
11950 |
|
11951 ctxt = xmlXPathNewParserContext(str, ctx); |
|
11952 xmlXPathEvalExpr(ctxt); |
|
11953 |
|
11954 if (ctxt->value == NULL) { |
|
11955 xmlGenericError(xmlGenericErrorContext, |
|
11956 EMBED_ERRTXT("xmlXPathEval: evaluation failed\n")); |
|
11957 res = NULL; |
|
11958 } else if (*ctxt->cur != 0) { |
|
11959 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); |
|
11960 res = NULL; |
|
11961 } else { |
|
11962 res = valuePop(ctxt); |
|
11963 } |
|
11964 |
|
11965 do { |
|
11966 tmp = valuePop(ctxt); |
|
11967 if (tmp) { |
|
11968 if (tmp != init) |
|
11969 stack++; |
|
11970 xmlXPathFreeObject(tmp); |
|
11971 } |
|
11972 } while (tmp); |
|
11973 |
|
11974 if ((stack != 0) && res) { |
|
11975 xmlGenericError(xmlGenericErrorContext, |
|
11976 EMBED_ERRTXT("xmlXPathEval: %d object left on the stack\n"), |
|
11977 stack); |
|
11978 } |
|
11979 if (ctxt->error != XPATH_EXPRESSION_OK) { |
|
11980 xmlXPathFreeObject(res); |
|
11981 res = NULL; |
|
11982 } |
|
11983 |
|
11984 xmlXPathFreeParserContext(ctxt); |
|
11985 return(res); |
|
11986 } |
|
11987 |
|
11988 /** |
|
11989 * xmlXPathEvalExpression: |
|
11990 * @param str the XPath expression |
|
11991 * @param ctxt the XPath context |
|
11992 * |
|
11993 * Evaluate the XPath expression in the given context. |
|
11994 * |
|
11995 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
11996 * the caller has to free the object. |
|
11997 */ |
|
11998 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
11999 xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) { |
|
12000 xmlXPathParserContextPtr pctxt; |
|
12001 xmlXPathObjectPtr res, tmp; |
|
12002 int stack = 0; |
|
12003 |
|
12004 xmlXPathInit(); |
|
12005 |
|
12006 CHECK_CONTEXT(ctxt) |
|
12007 |
|
12008 pctxt = xmlXPathNewParserContext(str, ctxt); |
|
12009 xmlXPathEvalExpr(pctxt); |
|
12010 |
|
12011 if (*pctxt->cur != 0) { |
|
12012 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); |
|
12013 res = NULL; |
|
12014 } else { |
|
12015 res = valuePop(pctxt); |
|
12016 } |
|
12017 |
|
12018 do { |
|
12019 tmp = valuePop(pctxt); |
|
12020 if (tmp != NULL) { |
|
12021 xmlXPathFreeObject(tmp); |
|
12022 stack++; |
|
12023 } |
|
12024 } while (tmp != NULL); |
|
12025 |
|
12026 if ((stack != 0) && (res != NULL)) { |
|
12027 xmlGenericError(xmlGenericErrorContext, |
|
12028 EMBED_ERRTXT("xmlXPathEvalExpression: %d object left on the stack\n"), |
|
12029 stack); |
|
12030 } |
|
12031 xmlXPathFreeParserContext(pctxt); |
|
12032 return(res); |
|
12033 } |
|
12034 |
|
12035 // XMLENGINE: NEW CODE -- XForms extensions support |
|
12036 |
|
12037 /* First initialises static nodeset variable from deplist (argument). |
|
12038 * Calls xmlXPathCompiledEval. If the result is a nodeset it is copied |
|
12039 * to the deplist. Otherwise modifications in xmlXPathCompOpEval have |
|
12040 * already filled deplist. |
|
12041 */ |
|
12042 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
12043 xmlXPathCompiledEvalWithDependencies(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx, xmlNodeSetPtr deplist) |
|
12044 { |
|
12045 xmlXPathObjectPtr res = NULL; |
|
12046 ctx->dependencyList = deplist; |
|
12047 res = xmlXPathCompiledEval(comp, ctx); |
|
12048 |
|
12049 /* if the result is nodeset there are no other dependencies */ |
|
12050 if (res != NULL && res->type == XPATH_NODESET) { |
|
12051 if (!xmlXPathNodeSetIsEmpty(deplist)) { |
|
12052 ;/* this should not happen */ |
|
12053 } |
|
12054 xmlXPathNodeSetMerge(deplist, res->nodesetval); |
|
12055 // KO: moved out of IF block // xmlXPathNodeSetSort(deplist); |
|
12056 } |
|
12057 // KO: moved out of IF block //else xmlXPathNodeSetSort(deplist); |
|
12058 |
|
12059 xmlXPathNodeSetSort(deplist); /* dep has been filled with nodes */ |
|
12060 |
|
12061 ctx->dependencyList = NULL; |
|
12062 |
|
12063 return res; |
|
12064 } |
|
12065 |
|
12066 /* First pops of nargs values from stack (ctxt) and stores them in temporary |
|
12067 * array. Adds all values of type nodeset to deplist and pushes everything back |
|
12068 * on the stack. |
|
12069 */ |
|
12070 void addNodeSetsFromStackToDependencyList(xmlXPathParserContextPtr ctxt, int nargs) |
|
12071 { |
|
12072 xmlNodeSetPtr deplist = ctxt->context->dependencyList; |
|
12073 #define MAXTMPSTACK 10 |
|
12074 xmlXPathObjectPtr tmpStack[MAXTMPSTACK] = {0,0,0,0,0,0,0,0,0,0}; |
|
12075 xmlXPathObjectPtr cValue; |
|
12076 int i=0; |
|
12077 |
|
12078 if(!deplist) return; |
|
12079 cValue = ctxt->value; // To remove when more efficient code is used |
|
12080 |
|
12081 // KO: If to decide number of popped elements first (min(nargs,MAXTMPSTACK)) |
|
12082 // then no need to prefill tmpStack with zeros and make checks in the second loop |
|
12083 /* copy all arguments of type nodeset to deplist */ |
|
12084 // KO: it is better not to pop and push arguments back, but rather PEEK the values from |
|
12085 // the internal stack: PEEK(i) = ctxt->valueTab[ctxt->valueNr - i - 1]; |
|
12086 // where 'i=1' is the index of the top-most element on the stack |
|
12087 for (i=0; (i < nargs) && (i < MAXTMPSTACK); i++) { |
|
12088 tmpStack[i] = valuePop(ctxt); |
|
12089 } |
|
12090 |
|
12091 for (i= MAXTMPSTACK-1; i>=0 ; i--) { |
|
12092 if (tmpStack[i] != NULL) { |
|
12093 if (tmpStack[i]->type == XPATH_NODESET) |
|
12094 xmlXPathNodeSetMerge(deplist, tmpStack[i]->nodesetval); |
|
12095 valuePush(ctxt, tmpStack[i]); |
|
12096 } |
|
12097 } |
|
12098 ctxt->value = cValue; |
|
12099 //#define __enable_this_test |
|
12100 #ifdef __enable_this_test |
|
12101 |
|
12102 xmlXPathObjectPtr* stack = ctxt->valueTab; |
|
12103 int index = ctxt->valueNr - 1; |
|
12104 for (i= 0; i < nargs ; i++) { |
|
12105 xmlXPathObjectPtr obj = stack[index--]; |
|
12106 if (obj->type == XPATH_NODESET) |
|
12107 xmlXPathNodeSetMerge(deplist, obj->nodesetval); |
|
12108 } |
|
12109 #endif |
|
12110 |
|
12111 #undef __enable_this_test |
|
12112 } |
|
12113 |
|
12114 /* |
|
12115 * xmlXFormsInstanceFunction: |
|
12116 * @param ctxt the XPath Parser context |
|
12117 * @param nargs the number of arguments |
|
12118 * |
|
12119 * Implement the instance() XForms function |
|
12120 * nodeset instance(string) |
|
12121 */ |
|
12122 void |
|
12123 xmlXFormsInstanceFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
12124 xmlXPathObjectPtr cur = NULL; |
|
12125 xmlXPathObjectPtr ret = NULL; |
|
12126 xmlDocPtr doc = NULL; |
|
12127 |
|
12128 CHECK_ARITY(1); |
|
12129 cur = valuePop(ctxt); |
|
12130 if (!cur) |
|
12131 XP_ERROR(XPATH_INVALID_OPERAND); |
|
12132 |
|
12133 /* convert argument to string */ |
|
12134 cur = xmlXPathConvertString(cur); |
|
12135 |
|
12136 |
|
12137 if (ctxt->context->instanceDocs) |
|
12138 { |
|
12139 doc = (xmlDocPtr)xmlHashLookup(ctxt->context->instanceDocs, cur->stringval); |
|
12140 } |
|
12141 |
|
12142 /* create a node set and change context, which will be reset by |
|
12143 the operations: AND, OR, EQUAL, CMP, MULT, UNION, ARG */ |
|
12144 if (doc) |
|
12145 { |
|
12146 ctxt->context->doc = doc; |
|
12147 ctxt->context->node = xmlDocGetRootElement(doc); |
|
12148 ret = xmlXPathNewNodeSet(ctxt->context->node); |
|
12149 } |
|
12150 else |
|
12151 { |
|
12152 ret = xmlXPathNewNodeSet(NULL); |
|
12153 } |
|
12154 |
|
12155 /* push the instance on the stack */ |
|
12156 valuePush(ctxt, ret); |
|
12157 |
|
12158 /* free cur*/ |
|
12159 xmlXPathFreeObject(cur); |
|
12160 } |
|
12161 // |
|
12162 // |
|
12163 //-- END NEW CODE // XMLENGINE XFROMS support |
|
12164 |
|
12165 /************************************************************************ |
|
12166 * * |
|
12167 * Extra functions not pertaining to the XPath spec * |
|
12168 * * |
|
12169 ************************************************************************/ |
|
12170 /** |
|
12171 * xmlXPathEscapeUriFunction: |
|
12172 * @param ctxt the XPath Parser context |
|
12173 * @param nargs the number of arguments |
|
12174 * |
|
12175 * Implement the escape-uri() XPath function |
|
12176 * string escape-uri(string $str, bool $escape-reserved) |
|
12177 * |
|
12178 * This function applies the URI escaping rules defined in section 2 of [RFC |
|
12179 * 2396] to the string supplied as $uri-part, which typically represents all |
|
12180 * or part of a URI. The effect of the function is to replace any special |
|
12181 * character in the string by an escape sequence of the form %xx%yy..., |
|
12182 * where xxyy... is the hexadecimal representation of the octets used to |
|
12183 * represent the character in UTF-8. |
|
12184 * |
|
12185 * The set of characters that are escaped depends on the setting of the |
|
12186 * boolean argument $escape-reserved. |
|
12187 * |
|
12188 * If $escape-reserved is true, all characters are escaped other than lower |
|
12189 * case letters a-z, upper case letters A-Z, digits 0-9, and the characters |
|
12190 * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!" |
|
12191 * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only |
|
12192 * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and |
|
12193 * A-F). |
|
12194 * |
|
12195 * If $escape-reserved is false, the behavior differs in that characters |
|
12196 * referred to in [RFC 2396] as reserved characters are not escaped. These |
|
12197 * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",". |
|
12198 * |
|
12199 * [RFC 2396] does not define whether escaped URIs should use lower case or |
|
12200 * upper case for hexadecimal digits. To ensure that escaped URIs can be |
|
12201 * compared using string comparison functions, this function must always use |
|
12202 * the upper-case letters A-F. |
|
12203 * |
|
12204 * Generally, $escape-reserved should be set to true when escaping a string |
|
12205 * that is to form a single part of a URI, and to false when escaping an |
|
12206 * entire URI or URI reference. |
|
12207 * |
|
12208 * In the case of non-ascii characters, the string is encoded according to |
|
12209 * utf-8 and then converted according to RFC 2396. |
|
12210 * |
|
12211 * Examples |
|
12212 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true()) |
|
12213 * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean" |
|
12214 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false()) |
|
12215 * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean" |
|
12216 * |
|
12217 */ |
|
12218 static void |
|
12219 xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
12220 xmlXPathObjectPtr str; |
|
12221 int escape_reserved; |
|
12222 xmlBufferPtr target; |
|
12223 xmlChar *cptr; |
|
12224 xmlChar escape[4]; |
|
12225 |
|
12226 CHECK_ARITY(2); |
|
12227 |
|
12228 escape_reserved = xmlXPathPopBoolean(ctxt); |
|
12229 |
|
12230 CAST_TO_STRING; |
|
12231 str = valuePop(ctxt); |
|
12232 |
|
12233 target = xmlBufferCreate(); |
|
12234 |
|
12235 escape[0] = '%'; |
|
12236 escape[3] = 0; |
|
12237 |
|
12238 if (target) { |
|
12239 for (cptr = str->stringval; *cptr; cptr++) { |
|
12240 if ((*cptr >= 'A' && *cptr <= 'Z') || |
|
12241 (*cptr >= 'a' && *cptr <= 'z') || |
|
12242 (*cptr >= '0' && *cptr <= '9') || |
|
12243 *cptr == '-' || *cptr == '_' || *cptr == '.' || |
|
12244 *cptr == '!' || *cptr == '~' || *cptr == '*' || |
|
12245 *cptr == '\''|| *cptr == '(' || *cptr == ')' || |
|
12246 (*cptr == '%' && |
|
12247 ((cptr[1] >= 'A' && cptr[1] <= 'F') || |
|
12248 (cptr[1] >= 'a' && cptr[1] <= 'f') || |
|
12249 (cptr[1] >= '0' && cptr[1] <= '9')) && |
|
12250 ((cptr[2] >= 'A' && cptr[2] <= 'F') || |
|
12251 (cptr[2] >= 'a' && cptr[2] <= 'f') || |
|
12252 (cptr[2] >= '0' && cptr[2] <= '9'))) || |
|
12253 (!escape_reserved && |
|
12254 (*cptr == ';' || *cptr == '/' || *cptr == '?' || |
|
12255 *cptr == ':' || *cptr == '@' || *cptr == '&' || |
|
12256 *cptr == '=' || *cptr == '+' || *cptr == '$' || |
|
12257 *cptr == ','))) |
|
12258 { |
|
12259 xmlBufferAdd(target, cptr, 1); |
|
12260 } else { |
|
12261 if ((*cptr >> 4) < 10) |
|
12262 escape[1] = '0' + (*cptr >> 4); |
|
12263 else |
|
12264 escape[1] = 'A' - 10 + (*cptr >> 4); |
|
12265 if ((*cptr & 0xF) < 10) |
|
12266 escape[2] = '0' + (*cptr & 0xF); |
|
12267 else |
|
12268 escape[2] = 'A' - 10 + (*cptr & 0xF); |
|
12269 |
|
12270 xmlBufferAdd(target, &escape[0], 3); |
|
12271 } |
|
12272 } |
|
12273 } |
|
12274 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
12275 xmlBufferFree(target); |
|
12276 xmlXPathFreeObject(str); |
|
12277 } |
|
12278 |
|
12279 /** |
|
12280 * xmlXPathRegisterAllFunctions: |
|
12281 * @param ctxt the XPath context |
|
12282 * |
|
12283 * Registers all default XPath functions in this context |
|
12284 * |
|
12285 * OOM: possible --> check OOM flag |
|
12286 * Prerequisites: TRUE ( ctxt && !ctxt->funcHash ) |
|
12287 */ |
|
12288 XMLPUBFUNEXPORT void |
|
12289 xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt) |
|
12290 { |
|
12291 LOAD_GS_DIRECT |
|
12292 //if (!ctxt || ctxt->funcHash) |
|
12293 // return; |
|
12294 |
|
12295 if (!ctxt->funcHash){ |
|
12296 // Initialize table in this context and then set it for global reuse if needed |
|
12297 ctxt->funcHash = xmlHashCreate(40); // Note: for standard XPath functions |
|
12298 if(!ctxt->funcHash) |
|
12299 return; // OOM |
|
12300 } |
|
12301 |
|
12302 // Note: OOM is possible in each of these functions |
|
12303 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"boolean", xmlXPathBooleanFunction); |
|
12304 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"ceiling", xmlXPathCeilingFunction); |
|
12305 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"count", xmlXPathCountFunction); |
|
12306 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"concat", xmlXPathConcatFunction); |
|
12307 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"contains",xmlXPathContainsFunction); |
|
12308 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"id", xmlXPathIdFunction); |
|
12309 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"false", xmlXPathFalseFunction); |
|
12310 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"floor", xmlXPathFloorFunction); |
|
12311 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"last", xmlXPathLastFunction); |
|
12312 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"lang", xmlXPathLangFunction); |
|
12313 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"local-name", xmlXPathLocalNameFunction); |
|
12314 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"not", xmlXPathNotFunction); |
|
12315 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"name", xmlXPathNameFunction); |
|
12316 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"namespace-uri", xmlXPathNamespaceURIFunction); |
|
12317 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"normalize-space", xmlXPathNormalizeFunction); |
|
12318 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"number", xmlXPathNumberFunction); |
|
12319 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"position", xmlXPathPositionFunction); |
|
12320 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"round", xmlXPathRoundFunction); |
|
12321 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string", xmlXPathStringFunction); |
|
12322 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string-length", xmlXPathStringLengthFunction); |
|
12323 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"starts-with", xmlXPathStartsWithFunction); |
|
12324 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring", xmlXPathSubstringFunction); |
|
12325 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-before", xmlXPathSubstringBeforeFunction); |
|
12326 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-after", xmlXPathSubstringAfterFunction); |
|
12327 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"sum", xmlXPathSumFunction); |
|
12328 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"true", xmlXPathTrueFunction); |
|
12329 xmlXPathRegisterFunc(ctxt, (const xmlChar*)"translate", xmlXPathTranslateFunction); |
|
12330 |
|
12331 xmlXPathRegisterFuncNS(ctxt,(const xmlChar*)"escape-uri", |
|
12332 (const xmlChar*)"http://www.w3.org/2002/08/xquery-functions", xmlXPathEscapeUriFunction); |
|
12333 |
|
12334 |
|
12335 |
|
12336 // XMLENGINE: NEW CODE -- XForms extensions support |
|
12337 xmlXPathRegisterFunc(ctxt, (const xmlChar *)"instance", xmlXFormsInstanceFunction); |
|
12338 //-- END NEW CODE |
|
12339 |
|
12340 if(OOM_FLAG){ |
|
12341 xmlHashFree(ctxt->funcHash, NULL /* deallocator func */); |
|
12342 ctxt->funcHash = NULL; |
|
12343 } |
|
12344 } |
|
12345 |
|
12346 #endif /* LIBXML_XPATH_ENABLED */ |