|
1 /* |
|
2 * xpath.c: XML Path Language implementation |
|
3 * XPath is a language for addressing parts of an XML document, |
|
4 * designed to be used by both XSLT and XPointer |
|
5 * |
|
6 * Reference: W3C Recommendation 16 November 1999 |
|
7 * http://www.w3.org/TR/1999/REC-xpath-19991116 |
|
8 * Public reference: |
|
9 * http://www.w3.org/TR/xpath |
|
10 * |
|
11 * See Copyright for the status of this software |
|
12 * |
|
13 * Author: daniel@veillard.com |
|
14 * |
|
15 */ |
|
16 |
|
17 #define IN_LIBXML |
|
18 #include "XmlEnglibxml.h" |
|
19 |
|
20 #include <string.h> |
|
21 |
|
22 // TODO: XE Cleanup |
|
23 #ifdef HAVE_SYS_TYPES_H |
|
24 #include <sys/types.h> |
|
25 #endif |
|
26 #ifdef HAVE_MATH_H |
|
27 #include <math.h> |
|
28 #endif |
|
29 #if defined(HAVE_CTYPE_H) |
|
30 #include <ctype.h> |
|
31 #endif |
|
32 #ifdef HAVE_SIGNAL_H |
|
33 #include <signal.h> |
|
34 #endif |
|
35 |
|
36 |
|
37 #include "Libxml2_globals.h" |
|
38 //#include "Libxml2_xmlmemory.h" |
|
39 //#include "Libxml2_xpath.h" |
|
40 //#include "Libxml2_tree.h" |
|
41 //#include "Libxml2_valid.h" |
|
42 #include "Libxml2_xpathInternals.h" |
|
43 #include "Libxml2_parserInternals.h" |
|
44 //#include "Libxml2_hash.h" |
|
45 |
|
46 #ifdef LIBXML_XPTR_ENABLED |
|
47 #include "Libxml2_xpointer.h" |
|
48 #endif |
|
49 |
|
50 #ifdef LIBXML_DEBUG_ENABLED |
|
51 #include <debugXML.h> |
|
52 #endif |
|
53 |
|
54 //#include "Libxml2_xmlerror.h" |
|
55 //#include "Libxml2_threads.h" |
|
56 |
|
57 // TODO: remove/rename TODO |
|
58 #define TODO \ |
|
59 xmlGenericError(xmlGenericErrorContext, \ |
|
60 EMBED_ERRTXT("Unimplemented block at %s:%d\n"), \ |
|
61 __FILE__, __LINE__); |
|
62 |
|
63 #if defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XPATH_ENABLED) |
|
64 /************************************************************************ |
|
65 * * |
|
66 * Floating point stuff * |
|
67 * * |
|
68 ************************************************************************/ |
|
69 |
|
70 #ifndef TRIO_REPLACE_STDIO |
|
71 //#define TRIO_PUBLIC static |
|
72 #endif |
|
73 #include "Libxml2_trionan.inc" |
|
74 |
|
75 /** |
|
76 * xmlXPathInit: |
|
77 * |
|
78 * Initialize the XPath environment |
|
79 * |
|
80 * OOM: never (so far -- soon hash tables will be created here) |
|
81 */ |
|
82 void |
|
83 xmlXPathInit(void) { |
|
84 if (xmlXPathInitialized) return; |
|
85 |
|
86 xmlXPathPINF = trio_pinf(); |
|
87 xmlXPathNINF = trio_ninf(); |
|
88 xmlXPathNAN = trio_nan(); |
|
89 xmlXPathNZERO = trio_nzero(); |
|
90 |
|
91 xmlXPathInitialized = 1; |
|
92 |
|
93 // TODO: Register all XPath functions in reusable hash table here |
|
94 } |
|
95 |
|
96 /** |
|
97 * xeXPathCleanup: |
|
98 * |
|
99 * Free any reusable by XPath module resources |
|
100 */ |
|
101 void xeXPathCleanup() |
|
102 { |
|
103 #ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED |
|
104 if (xmlXPathDefaultFunctionsHash) |
|
105 xmlHashFree(xmlXPathDefaultFunctionsHash, NULL); |
|
106 xmlXPathDefaultFunctionsHash = NULL; |
|
107 |
|
108 if (xmlXPathIntermediaryExtensionFunctionsHash) |
|
109 xmlHashFree(xmlXPathIntermediaryExtensionFunctionsHash, NULL); |
|
110 xmlXPathIntermediaryExtensionFunctionsHash = NULL; |
|
111 #endif |
|
112 } |
|
113 |
|
114 // DONE: OPTIMIZE: Define as macro or inliner |
|
115 /** |
|
116 * xmlXPathIsNaN: |
|
117 * @val: a double value |
|
118 * |
|
119 * Provides a portable isnan() function to detect whether a double |
|
120 * is a NotaNumber. Based on trio code |
|
121 * http://sourceforge.net/projects/ctrio/ |
|
122 * |
|
123 * Returns 1 if the value is a NaN, 0 otherwise |
|
124 */ |
|
125 /* |
|
126 inline int xmlXPathIsNaN(double val) { |
|
127 return(trio_isnan(val)); |
|
128 }*/ |
|
129 //#define xmlXPathIsNaN(val) trio_isnan(val) |
|
130 |
|
131 //#define xmlXPathIsNaN(val) trio_isnan(val) |
|
132 |
|
133 // DONE: OPTIMIZE: Define as macro or inliner |
|
134 /** |
|
135 * xmlXPathIsInf: |
|
136 * @val: a double value |
|
137 * |
|
138 * Provides a portable isinf() function to detect whether a double |
|
139 * is a +Infinite or -Infinite. Based on trio code |
|
140 * http://sourceforge.net/projects/ctrio/ |
|
141 * |
|
142 * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise |
|
143 */ |
|
144 /* |
|
145 inline int xmlXPathIsInf(double val) { |
|
146 return(trio_isinf(val)); |
|
147 } |
|
148 */ |
|
149 #define xmlXPathIsInf(val) trio_isinf(val) |
|
150 |
|
151 #endif /* SCHEMAS or XPATH */ |
|
152 |
|
153 |
|
154 #ifdef LIBXML_XPATH_ENABLED |
|
155 |
|
156 |
|
157 // TODO: OPTIMIZE: Define as macro or inliner |
|
158 /** |
|
159 * xmlXPathGetSign: |
|
160 * @val: a double value |
|
161 * |
|
162 * Provides a portable function to detect the sign of a double |
|
163 * Modified from trio code |
|
164 * http://sourceforge.net/projects/ctrio/ |
|
165 * |
|
166 * Returns 1 if the value is Negative, 0 if positive |
|
167 */ |
|
168 /* |
|
169 inline int xmlXPathGetSign(double val) { |
|
170 return(trio_signbit(val)); |
|
171 } |
|
172 */ |
|
173 #define xmlXPathGetSign(val) trio_signbit(val) |
|
174 |
|
175 |
|
176 /* |
|
177 * TODO: when compatibility allows remove all "fake node libxslt" strings |
|
178 * the test should just be name[0] = ' ' |
|
179 */ |
|
180 /* #define DEBUG */ |
|
181 /* #define DEBUG_STEP */ |
|
182 /* #define DEBUG_STEP_NTH */ |
|
183 /* #define DEBUG_EXPR */ |
|
184 /* #define DEBUG_EVAL_COUNTS */ |
|
185 |
|
186 |
|
187 |
|
188 static const xmlNs xmlXPathXMLNamespaceStruct = { |
|
189 NULL, |
|
190 XML_NAMESPACE_DECL, |
|
191 XML_XML_NAMESPACE, |
|
192 BAD_CAST "xml", |
|
193 NULL |
|
194 }; |
|
195 static const xmlNsPtr xmlXPathXMLNamespace = (xmlNsPtr)&xmlXPathXMLNamespaceStruct; |
|
196 |
|
197 /************************************************************************ |
|
198 * * |
|
199 * Error handling routines * |
|
200 * * |
|
201 ************************************************************************/ |
|
202 |
|
203 /* |
|
204 * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError |
|
205 */ |
|
206 const char *const xmlXPathErrorMessages[] = { |
|
207 EMBED_ERRTXT("Ok\n"), |
|
208 EMBED_ERRTXT("Number encoding\n"), |
|
209 EMBED_ERRTXT("Unfinished literal\n"), |
|
210 EMBED_ERRTXT("Start of literal\n"), |
|
211 EMBED_ERRTXT("Expected $ for variable reference\n"), |
|
212 EMBED_ERRTXT("Undefined variable\n"), |
|
213 EMBED_ERRTXT("Invalid predicate\n"), |
|
214 EMBED_ERRTXT("Invalid expression\n"), |
|
215 EMBED_ERRTXT("Missing closing curly brace\n"), |
|
216 EMBED_ERRTXT("Unregistered function\n"), |
|
217 EMBED_ERRTXT("Invalid operand\n"), |
|
218 EMBED_ERRTXT("Invalid type\n"), |
|
219 EMBED_ERRTXT("Invalid number of arguments\n"), |
|
220 EMBED_ERRTXT("Invalid context size\n"), |
|
221 EMBED_ERRTXT("Invalid context position\n"), |
|
222 EMBED_ERRTXT("Memory allocation error\n"), |
|
223 EMBED_ERRTXT("Syntax error\n"), |
|
224 EMBED_ERRTXT("Resource error\n"), |
|
225 EMBED_ERRTXT("Sub resource error\n"), |
|
226 EMBED_ERRTXT("Undefined namespace prefix\n"), |
|
227 EMBED_ERRTXT("Encoding error\n"), |
|
228 EMBED_ERRTXT("Char out of XML range\n") |
|
229 }; |
|
230 |
|
231 |
|
232 // TODO: OPTIMIZATION: Not required for Symbian |
|
233 /** |
|
234 * xmlXPathErrMemory: |
|
235 * @ctxt: an XPath context |
|
236 * @extra: extra informations |
|
237 * |
|
238 * Handle a redefinition of attribute error |
|
239 */ |
|
240 static void |
|
241 xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) |
|
242 { |
|
243 if (ctxt != NULL) { |
|
244 if (extra) { |
|
245 xmlChar buf[200]; |
|
246 |
|
247 xmlStrPrintf(buf, 200, |
|
248 BAD_CAST EMBED_ERRTXT("Memory allocation failed : %s\n"), extra); |
|
249 ctxt->lastError.message = (char *) xmlStrdup(buf); |
|
250 } else { |
|
251 ctxt->lastError.message = (char *) |
|
252 xmlStrdup(BAD_CAST EMBED_ERRTXT("Memory allocation failed\n")); |
|
253 } |
|
254 ctxt->lastError.domain = XML_FROM_XPATH; |
|
255 ctxt->lastError.code = XML_ERR_NO_MEMORY; |
|
256 if (ctxt->error != NULL) |
|
257 ctxt->error(ctxt->userData, &ctxt->lastError); |
|
258 } else { |
|
259 if (extra) |
|
260 __xmlRaiseError(NULL, NULL, NULL, |
|
261 NULL, NULL, XML_FROM_XPATH, |
|
262 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
|
263 extra, NULL, NULL, 0, 0, |
|
264 EMBED_ERRTXT("Memory allocation failed : %s\n"), extra); |
|
265 else |
|
266 __xmlRaiseError(NULL, NULL, NULL, |
|
267 NULL, NULL, XML_FROM_XPATH, |
|
268 XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
|
269 NULL, NULL, NULL, 0, 0, |
|
270 EMBED_ERRTXT("Memory allocation failed\n")); |
|
271 } |
|
272 } |
|
273 |
|
274 /** |
|
275 * xmlXPathErrMemory: |
|
276 * @ctxt: an XPath parser context |
|
277 * @extra: extra informations |
|
278 * |
|
279 * Handle a redefinition of attribute error |
|
280 */ |
|
281 // TODO: Obviously, something wrong here: ctxt!=NULL for sure |
|
282 static void |
|
283 xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra) |
|
284 { |
|
285 ctxt->error = XPATH_MEMORY_ERROR; |
|
286 if (ctxt == NULL) |
|
287 xmlXPathErrMemory(NULL, extra); |
|
288 else |
|
289 xmlXPathErrMemory(ctxt->context, extra); |
|
290 } |
|
291 |
|
292 /** |
|
293 * xmlXPathErr: |
|
294 * @ctxt: a XPath parser context |
|
295 * @error: the error code |
|
296 * |
|
297 * Handle a Relax NG Parsing error |
|
298 */ |
|
299 void |
|
300 xmlXPathErr(xmlXPathParserContextPtr ctxt, int error) |
|
301 { |
|
302 // TODO: OPTIMIZE: Combine two (or even all three) IF blocks into one |
|
303 if (ctxt == NULL) { |
|
304 __xmlRaiseError(NULL, NULL, NULL, |
|
305 NULL, NULL, XML_FROM_XPATH, |
|
306 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
|
307 XML_ERR_ERROR, NULL, 0, |
|
308 NULL, NULL, NULL, 0, 0, |
|
309 xmlXPathErrorMessages[error]); |
|
310 return; |
|
311 } |
|
312 ctxt->error = error; |
|
313 if (ctxt->context == NULL) { |
|
314 __xmlRaiseError(NULL, NULL, NULL, |
|
315 NULL, NULL, XML_FROM_XPATH, |
|
316 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
|
317 XML_ERR_ERROR, NULL, 0, |
|
318 (const char *) ctxt->base, NULL, NULL, |
|
319 ctxt->cur - ctxt->base, 0, |
|
320 xmlXPathErrorMessages[error]); |
|
321 return; |
|
322 } |
|
323 // TODO: OPTIMIZE: make a proxy for lastError |
|
324 ctxt->context->lastError.domain = XML_FROM_XPATH; |
|
325 ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK; |
|
326 ctxt->context->lastError.level = XML_ERR_ERROR; |
|
327 if (ctxt->context->lastError.str1) // agathe added |
|
328 xmlFree(ctxt->context->lastError.str1); |
|
329 ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base); |
|
330 ctxt->context->lastError.int1 = ctxt->cur - ctxt->base; |
|
331 ctxt->context->lastError.node = ctxt->context->debugNode; |
|
332 |
|
333 if (ctxt->context->error != NULL) { |
|
334 ctxt->context->error(ctxt->context->userData, &ctxt->context->lastError); |
|
335 } else { |
|
336 __xmlRaiseError(NULL, NULL, NULL, |
|
337 NULL, ctxt->context->debugNode, XML_FROM_XPATH, |
|
338 error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
|
339 XML_ERR_ERROR, NULL, 0, |
|
340 (const char *) ctxt->base, NULL, NULL, |
|
341 ctxt->cur - ctxt->base, 0, |
|
342 xmlXPathErrorMessages[error]); |
|
343 } |
|
344 } |
|
345 |
|
346 // DONE: OPTIMIZE: Use macro instead of function call |
|
347 // Macro added in xpathInternals.h |
|
348 /** |
|
349 * xmlXPatherror: |
|
350 * @ctxt: the XPath Parser context |
|
351 * @file: the file name |
|
352 * @line: the line number |
|
353 * @no: the error number |
|
354 * |
|
355 * Formats an error message. |
|
356 */ |
|
357 //void |
|
358 //xmlXPatherror(xmlXPathParserContextPtr ctxt, const char* file ATTRIBUTE_UNUSED, |
|
359 // int line ATTRIBUTE_UNUSED , int no) |
|
360 //{ |
|
361 // xmlXPathErr(ctxt, no); |
|
362 //} |
|
363 |
|
364 |
|
365 /************************************************************************ |
|
366 * * |
|
367 * Parser Types * |
|
368 * * |
|
369 ************************************************************************/ |
|
370 |
|
371 /* |
|
372 * Types are private: |
|
373 */ |
|
374 typedef enum { |
|
375 AXIS_ANCESTOR = 1, |
|
376 AXIS_ANCESTOR_OR_SELF, |
|
377 AXIS_ATTRIBUTE, |
|
378 AXIS_CHILD, |
|
379 AXIS_DESCENDANT, |
|
380 AXIS_DESCENDANT_OR_SELF, |
|
381 AXIS_FOLLOWING, |
|
382 AXIS_FOLLOWING_SIBLING, |
|
383 AXIS_NAMESPACE, |
|
384 AXIS_PARENT, |
|
385 AXIS_PRECEDING, |
|
386 AXIS_PRECEDING_SIBLING, |
|
387 AXIS_SELF |
|
388 } xmlXPathAxisVal; |
|
389 |
|
390 typedef enum { |
|
391 NODE_TEST_NONE = 0, |
|
392 NODE_TEST_TYPE = 1, |
|
393 NODE_TEST_PI = 2, |
|
394 NODE_TEST_ALL = 3, |
|
395 NODE_TEST_NS = 4, |
|
396 NODE_TEST_NAME = 5 |
|
397 } xmlXPathTestVal; |
|
398 |
|
399 typedef enum { |
|
400 NODE_TYPE_NODE = 0, |
|
401 NODE_TYPE_COMMENT = XML_COMMENT_NODE, |
|
402 NODE_TYPE_TEXT = XML_TEXT_NODE, |
|
403 NODE_TYPE_PI = XML_PI_NODE |
|
404 } xmlXPathTypeVal; |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 /************************************************************************ |
|
410 * * |
|
411 * Parser Type functions * |
|
412 * * |
|
413 ************************************************************************/ |
|
414 |
|
415 /** |
|
416 * xmlXPathNewCompExpr: |
|
417 * |
|
418 * Create a new Xpath component |
|
419 * |
|
420 * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error |
|
421 */ |
|
422 static xmlXPathCompExprPtr |
|
423 xmlXPathNewCompExpr(void) { |
|
424 xmlXPathCompExprPtr cur; |
|
425 |
|
426 cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr)); |
|
427 if (cur == NULL) { |
|
428 xmlXPathErrMemory(NULL, EMBED_ERRTXT("allocating component\n")); |
|
429 return(NULL); |
|
430 } |
|
431 memset(cur, 0, sizeof(xmlXPathCompExpr)); |
|
432 cur->maxStep = 10; /* TODO: XPath context can be configured by maxStep parameter */ |
|
433 cur->nbStep = 0; |
|
434 cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * sizeof(xmlXPathStepOp)); |
|
435 if (cur->steps == NULL) { |
|
436 xmlXPathErrMemory(NULL, EMBED_ERRTXT("allocating steps\n")); |
|
437 xmlFree(cur); |
|
438 return(NULL); |
|
439 } |
|
440 memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp)); |
|
441 cur->last = -1; |
|
442 #ifdef DEBUG_EVAL_COUNTS |
|
443 cur->nb = 0; |
|
444 #endif |
|
445 return(cur); |
|
446 } |
|
447 |
|
448 /** |
|
449 * xmlXPathFreeCompExpr: |
|
450 * @comp: an XPATH comp |
|
451 * |
|
452 * Free up the memory allocated by @comp |
|
453 */ |
|
454 void |
|
455 xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) |
|
456 { |
|
457 xmlXPathStepOpPtr op; |
|
458 int i; |
|
459 |
|
460 if (comp == NULL) |
|
461 return; |
|
462 if (comp->dict == NULL) { |
|
463 for (i = 0; i < comp->nbStep; i++) { |
|
464 op = &comp->steps[i]; |
|
465 if (op->value4 != NULL) { |
|
466 if (op->op == XPATH_OP_VALUE) |
|
467 xmlXPathFreeObject((xmlXPathObjectPtr)op->value4); |
|
468 else |
|
469 xmlFree(op->value4); |
|
470 } |
|
471 if (op->value5 != NULL) |
|
472 xmlFree(op->value5); |
|
473 } |
|
474 } else { |
|
475 for (i = 0; i < comp->nbStep; i++) { |
|
476 op = &comp->steps[i]; |
|
477 if (op->value4 != NULL) { |
|
478 if (op->op == XPATH_OP_VALUE) |
|
479 xmlXPathFreeObject((xmlXPathObjectPtr)op->value4); |
|
480 } |
|
481 } |
|
482 xmlDictFree(comp->dict); |
|
483 } |
|
484 |
|
485 if (comp->steps != NULL) { |
|
486 xmlFree(comp->steps); |
|
487 } |
|
488 #ifdef DEBUG_EVAL_COUNTS |
|
489 if (comp->string != NULL) { |
|
490 xmlFree(comp->string); |
|
491 } |
|
492 #endif |
|
493 if (comp->expr != NULL) { |
|
494 xmlFree(comp->expr); |
|
495 } |
|
496 |
|
497 xmlFree(comp); |
|
498 } |
|
499 |
|
500 /** |
|
501 * xmlXPathCompExprAdd: |
|
502 * @comp: the compiled expression |
|
503 * @ch1: first child index |
|
504 * @ch2: second child index |
|
505 * @op: an op |
|
506 * @value: the first int value |
|
507 * @value2: the second int value |
|
508 * @value3: the third int value |
|
509 * @value4: the first string value |
|
510 * @value5: the second string value |
|
511 * |
|
512 * Add a step to an XPath Compiled Expression |
|
513 * |
|
514 * Returns -1 in case of failure, the index otherwise |
|
515 */ |
|
516 static int |
|
517 xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2, |
|
518 xmlXPathOp op, int value, |
|
519 int value2, int value3, void *value4, void *value5) |
|
520 { |
|
521 xmlXPathStepOpPtr step; |
|
522 |
|
523 if (comp->nbStep >= comp->maxStep) |
|
524 { |
|
525 xmlXPathStepOp *real; |
|
526 |
|
527 comp->maxStep *= 2; |
|
528 real = (xmlXPathStepOp *) xmlRealloc(comp->steps, comp->maxStep * sizeof(xmlXPathStepOp)); |
|
529 if (real == NULL) { |
|
530 comp->maxStep /= 2; |
|
531 xmlXPathErrMemory(NULL, EMBED_ERRTXT("adding step\n")); |
|
532 return(-1); |
|
533 } |
|
534 comp->steps = real; |
|
535 } |
|
536 // DONE: OPTIMIZE: make a proxy for comp->steps[comp->nbStep] |
|
537 step = &(comp->steps[comp->nbStep]); |
|
538 // |
|
539 comp->last = comp->nbStep; |
|
540 step->ch1 = ch1; |
|
541 step->ch2 = ch2; |
|
542 step->op = op; |
|
543 step->value = value; |
|
544 step->value2 = value2; |
|
545 step->value3 = value3; |
|
546 |
|
547 if ((comp->dict != NULL) && |
|
548 ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) || |
|
549 (op == XPATH_OP_COLLECT))) |
|
550 { |
|
551 if (value4 != NULL) { |
|
552 step->value4 = (xmlChar*) |
|
553 (void*)xmlDictLookup(comp->dict, (xmlChar*) value4, -1); |
|
554 xmlFree(value4); |
|
555 } else |
|
556 step->value4 = NULL; |
|
557 |
|
558 if (value5 != NULL) { |
|
559 step->value5 = (xmlChar*) |
|
560 (void*)xmlDictLookup(comp->dict, (xmlChar*)value5, -1); |
|
561 xmlFree(value5); |
|
562 }else |
|
563 step->value5 = NULL; |
|
564 } |
|
565 else |
|
566 { |
|
567 step->value4 = value4; |
|
568 step->value5 = value5; |
|
569 } |
|
570 step->cache = NULL; |
|
571 return(comp->nbStep++); |
|
572 } |
|
573 |
|
574 /** |
|
575 * xmlXPathCompSwap: |
|
576 * @comp: the compiled expression |
|
577 * @op: operation index |
|
578 * |
|
579 * Swaps 2 operations in the compiled expression |
|
580 */ |
|
581 static void |
|
582 xmlXPathCompSwap(xmlXPathStepOpPtr op) { |
|
583 int tmp; |
|
584 |
|
585 #ifndef LIBXML_THREAD_ENABLED |
|
586 /* |
|
587 * Since this manipulates possibly shared variables, this is |
|
588 * disabled if one detects that the library is used in a multithreaded |
|
589 * application |
|
590 */ |
|
591 if (xmlXPathDisableOptimizer) |
|
592 return; |
|
593 #endif |
|
594 |
|
595 tmp = op->ch1; |
|
596 op->ch1 = op->ch2; |
|
597 op->ch2 = tmp; |
|
598 } |
|
599 |
|
600 |
|
601 #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \ |
|
602 xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), (op), (val), (val2), (val3), (val4), (val5)) |
|
603 |
|
604 #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \ |
|
605 xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, (op), (val), (val2), (val3), (val4), (val5)) |
|
606 |
|
607 #define PUSH_LEAVE_EXPR(op, val, val2) \ |
|
608 xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL) |
|
609 |
|
610 #define PUSH_UNARY_EXPR(op, ch, val, val2) \ |
|
611 xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL) |
|
612 |
|
613 #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \ |
|
614 xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), (val), (val2), 0 ,NULL ,NULL) |
|
615 |
|
616 /************************************************************************ |
|
617 * * |
|
618 * Debugging related functions * |
|
619 * * |
|
620 ************************************************************************/ |
|
621 |
|
622 // TODO: OPTIMIZE: Create a function and do function call in macro |
|
623 #define STRANGE \ |
|
624 xmlGenericError(xmlGenericErrorContext, \ |
|
625 EMBED_ERRTXT("Internal error at %s:%d\n"), \ |
|
626 __FILE__, __LINE__); |
|
627 |
|
628 #ifdef LIBXML_DEBUG_ENABLED |
|
629 static void |
|
630 xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) { |
|
631 int i; |
|
632 char shift[100]; |
|
633 |
|
634 for (i = 0;((i < depth) && (i < 25));i++) |
|
635 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
636 shift[2 * i] = shift[2 * i + 1] = 0; |
|
637 if (cur == NULL) { |
|
638 fprintf(output, shift); |
|
639 fprintf(output, "Node is NULL !\n"); |
|
640 return; |
|
641 |
|
642 } |
|
643 |
|
644 if ((cur->type == XML_DOCUMENT_NODE) || |
|
645 (cur->type == XML_HTML_DOCUMENT_NODE)) { |
|
646 fprintf(output, shift); |
|
647 fprintf(output, " /\n"); |
|
648 } else if (cur->type == XML_ATTRIBUTE_NODE) |
|
649 xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth); |
|
650 else |
|
651 xmlDebugDumpOneNode(output, cur, depth); |
|
652 } |
|
653 |
|
654 static void |
|
655 xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) { |
|
656 xmlNodePtr tmp; |
|
657 int i; |
|
658 char shift[100]; |
|
659 |
|
660 for (i = 0;((i < depth) && (i < 25));i++) |
|
661 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
662 shift[2 * i] = shift[2 * i + 1] = 0; |
|
663 if (cur == NULL) { |
|
664 fprintf(output, shift); |
|
665 fprintf(output, "Node is NULL !\n"); |
|
666 return; |
|
667 } |
|
668 |
|
669 while (cur != NULL) { |
|
670 tmp = cur; |
|
671 cur = cur->next; |
|
672 xmlDebugDumpOneNode(output, tmp, depth); |
|
673 } |
|
674 } |
|
675 |
|
676 static void |
|
677 xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) { |
|
678 int i; |
|
679 char shift[100]; |
|
680 |
|
681 for (i = 0;((i < depth) && (i < 25));i++) |
|
682 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
683 |
|
684 shift[2 * i] = shift[2 * i + 1] = 0; |
|
685 |
|
686 if (cur == NULL) { |
|
687 fprintf(output, shift); |
|
688 fprintf(output, "NodeSet is NULL !\n"); |
|
689 return; |
|
690 } |
|
691 |
|
692 if (cur != NULL) { |
|
693 fprintf(output, "Set contains %d nodes:\n", cur->nodeNr); |
|
694 for (i = 0;i < cur->nodeNr;i++) { |
|
695 fprintf(output, shift); |
|
696 fprintf(output, "%d", i + 1); |
|
697 xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1); |
|
698 } |
|
699 } |
|
700 } |
|
701 |
|
702 static void |
|
703 xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) { |
|
704 int i; |
|
705 char shift[100]; |
|
706 |
|
707 for (i = 0;((i < depth) && (i < 25));i++) |
|
708 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
709 shift[2 * i] = shift[2 * i + 1] = 0; |
|
710 |
|
711 if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) { |
|
712 fprintf(output, shift); |
|
713 fprintf(output, "Value Tree is NULL !\n"); |
|
714 return; |
|
715 |
|
716 } |
|
717 |
|
718 fprintf(output, shift); |
|
719 fprintf(output, "%d", i + 1); |
|
720 xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1); |
|
721 } |
|
722 #if defined(LIBXML_XPTR_ENABLED) |
|
723 static void |
|
724 xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) { |
|
725 int i; |
|
726 char shift[100]; |
|
727 |
|
728 for (i = 0;((i < depth) && (i < 25));i++) |
|
729 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
730 shift[2 * i] = shift[2 * i + 1] = 0; |
|
731 |
|
732 if (cur == NULL) { |
|
733 fprintf(output, shift); |
|
734 fprintf(output, "LocationSet is NULL !\n"); |
|
735 return; |
|
736 |
|
737 } |
|
738 |
|
739 for (i = 0;i < cur->locNr;i++) { |
|
740 fprintf(output, shift); |
|
741 fprintf(output, "%d : ", i + 1); |
|
742 xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1); |
|
743 } |
|
744 } |
|
745 #endif /* LIBXML_XPTR_ENABLED */ |
|
746 |
|
747 /** |
|
748 * xmlXPathDebugDumpObject: |
|
749 * @output: the FILE * to dump the output |
|
750 * @cur: the object to inspect |
|
751 * @depth: indentation level |
|
752 * |
|
753 * Dump the content of the object for debugging purposes |
|
754 */ |
|
755 void |
|
756 xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) { |
|
757 int i; |
|
758 char shift[100]; |
|
759 |
|
760 for (i = 0;((i < depth) && (i < 25));i++) |
|
761 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
762 shift[2 * i] = shift[2 * i + 1] = 0; |
|
763 |
|
764 fprintf(output, shift); |
|
765 |
|
766 if (cur == NULL) { |
|
767 fprintf(output, "Object is empty (NULL)\n"); |
|
768 return; |
|
769 } |
|
770 switch(cur->type) { |
|
771 case XPATH_UNDEFINED: |
|
772 fprintf(output, "Object is uninitialized\n"); |
|
773 break; |
|
774 case XPATH_NODESET: |
|
775 fprintf(output, "Object is a Node Set :\n"); |
|
776 xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth); |
|
777 break; |
|
778 case XPATH_XSLT_TREE: |
|
779 fprintf(output, "Object is an XSLT value tree :\n"); |
|
780 xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth); |
|
781 break; |
|
782 case XPATH_BOOLEAN: |
|
783 fprintf(output, "Object is a Boolean : "); |
|
784 if (cur->boolval) fprintf(output, "true\n"); |
|
785 else fprintf(output, "false\n"); |
|
786 break; |
|
787 case XPATH_NUMBER: |
|
788 switch (xmlXPathIsInf(cur->floatval)) { |
|
789 case 1: |
|
790 fprintf(output, "Object is a number : Infinity\n"); |
|
791 break; |
|
792 case -1: |
|
793 fprintf(output, "Object is a number : -Infinity\n"); |
|
794 break; |
|
795 default: |
|
796 if (xmlXPathIsNaN(cur->floatval)) { |
|
797 fprintf(output, "Object is a number : NaN\n"); |
|
798 } else if (cur->floatval == 0 && xmlXPathGetSign(cur->floatval) != 0) { |
|
799 fprintf(output, "Object is a number : 0\n"); |
|
800 } else { |
|
801 fprintf(output, "Object is a number : %0g\n", cur->floatval); |
|
802 } |
|
803 } |
|
804 break; |
|
805 case XPATH_STRING: |
|
806 fprintf(output, "Object is a string : "); |
|
807 xmlDebugDumpString(output, cur->stringval); |
|
808 fprintf(output, "\n"); |
|
809 break; |
|
810 case XPATH_POINT: |
|
811 fprintf(output, "Object is a point : index %d in node", cur->index); |
|
812 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); |
|
813 fprintf(output, "\n"); |
|
814 break; |
|
815 case XPATH_RANGE: |
|
816 if ((cur->user2 == NULL) || |
|
817 ((cur->user2 == cur->user) && (cur->index == cur->index2))) { |
|
818 fprintf(output, "Object is a collapsed range :\n"); |
|
819 fprintf(output, shift); |
|
820 if (cur->index >= 0) |
|
821 fprintf(output, "index %d in ", cur->index); |
|
822 fprintf(output, "node\n"); |
|
823 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, |
|
824 depth + 1); |
|
825 } else { |
|
826 fprintf(output, "Object is a range :\n"); |
|
827 fprintf(output, shift); |
|
828 fprintf(output, "From "); |
|
829 if (cur->index >= 0) |
|
830 fprintf(output, "index %d in ", cur->index); |
|
831 fprintf(output, "node\n"); |
|
832 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, |
|
833 depth + 1); |
|
834 fprintf(output, shift); |
|
835 fprintf(output, "To "); |
|
836 if (cur->index2 >= 0) |
|
837 fprintf(output, "index %d in ", cur->index2); |
|
838 fprintf(output, "node\n"); |
|
839 xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2, |
|
840 depth + 1); |
|
841 fprintf(output, "\n"); |
|
842 } |
|
843 break; |
|
844 case XPATH_LOCATIONSET: |
|
845 #if defined(LIBXML_XPTR_ENABLED) |
|
846 fprintf(output, "Object is a Location Set:\n"); |
|
847 xmlXPathDebugDumpLocationSet(output, |
|
848 (xmlLocationSetPtr) cur->user, depth); |
|
849 #endif |
|
850 break; |
|
851 case XPATH_USERS: |
|
852 fprintf(output, "Object is user defined\n"); |
|
853 break; |
|
854 } |
|
855 } |
|
856 |
|
857 static void |
|
858 xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp, |
|
859 xmlXPathStepOpPtr op, int depth) { |
|
860 int i; |
|
861 char shift[100]; |
|
862 |
|
863 for (i = 0;((i < depth) && (i < 25));i++) |
|
864 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
865 shift[2 * i] = shift[2 * i + 1] = 0; |
|
866 |
|
867 fprintf(output, shift); |
|
868 if (op == NULL) { |
|
869 fprintf(output, "Step is NULL\n"); |
|
870 return; |
|
871 } |
|
872 switch (op->op) { |
|
873 case XPATH_OP_END: |
|
874 fprintf(output, "END"); break; |
|
875 case XPATH_OP_AND: |
|
876 fprintf(output, "AND"); break; |
|
877 case XPATH_OP_OR: |
|
878 fprintf(output, "OR"); break; |
|
879 case XPATH_OP_EQUAL: |
|
880 if (op->value) |
|
881 fprintf(output, "EQUAL ="); |
|
882 else |
|
883 fprintf(output, "EQUAL !="); |
|
884 break; |
|
885 case XPATH_OP_CMP: |
|
886 if (op->value) |
|
887 fprintf(output, "CMP <"); |
|
888 else |
|
889 fprintf(output, "CMP >"); |
|
890 if (!op->value2) |
|
891 fprintf(output, "="); |
|
892 break; |
|
893 case XPATH_OP_PLUS: |
|
894 if (op->value == 0) |
|
895 fprintf(output, "PLUS -"); |
|
896 else if (op->value == 1) |
|
897 fprintf(output, "PLUS +"); |
|
898 else if (op->value == 2) |
|
899 fprintf(output, "PLUS unary -"); |
|
900 else if (op->value == 3) |
|
901 fprintf(output, "PLUS unary - -"); |
|
902 break; |
|
903 case XPATH_OP_MULT: |
|
904 if (op->value == 0) |
|
905 fprintf(output, "MULT *"); |
|
906 else if (op->value == 1) |
|
907 fprintf(output, "MULT div"); |
|
908 else |
|
909 fprintf(output, "MULT mod"); |
|
910 break; |
|
911 case XPATH_OP_UNION: |
|
912 fprintf(output, "UNION"); break; |
|
913 case XPATH_OP_ROOT: |
|
914 fprintf(output, "ROOT"); break; |
|
915 case XPATH_OP_NODE: |
|
916 fprintf(output, "NODE"); break; |
|
917 case XPATH_OP_RESET: |
|
918 fprintf(output, "RESET"); break; |
|
919 case XPATH_OP_SORT: |
|
920 fprintf(output, "SORT"); break; |
|
921 case XPATH_OP_COLLECT: { |
|
922 xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value; |
|
923 xmlXPathTestVal test = (xmlXPathTestVal)op->value2; |
|
924 xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3; |
|
925 const xmlChar *prefix = op->value4; |
|
926 const xmlChar *name = op->value5; |
|
927 |
|
928 fprintf(output, "COLLECT "); |
|
929 switch (axis) { |
|
930 case AXIS_ANCESTOR: |
|
931 fprintf(output, " 'ancestors' "); break; |
|
932 case AXIS_ANCESTOR_OR_SELF: |
|
933 fprintf(output, " 'ancestors-or-self' "); break; |
|
934 case AXIS_ATTRIBUTE: |
|
935 fprintf(output, " 'attributes' "); break; |
|
936 case AXIS_CHILD: |
|
937 fprintf(output, " 'child' "); break; |
|
938 case AXIS_DESCENDANT: |
|
939 fprintf(output, " 'descendant' "); break; |
|
940 case AXIS_DESCENDANT_OR_SELF: |
|
941 fprintf(output, " 'descendant-or-self' "); break; |
|
942 case AXIS_FOLLOWING: |
|
943 fprintf(output, " 'following' "); break; |
|
944 case AXIS_FOLLOWING_SIBLING: |
|
945 fprintf(output, " 'following-siblings' "); break; |
|
946 case AXIS_NAMESPACE: |
|
947 fprintf(output, " 'namespace' "); break; |
|
948 case AXIS_PARENT: |
|
949 fprintf(output, " 'parent' "); break; |
|
950 case AXIS_PRECEDING: |
|
951 fprintf(output, " 'preceding' "); break; |
|
952 case AXIS_PRECEDING_SIBLING: |
|
953 fprintf(output, " 'preceding-sibling' "); break; |
|
954 case AXIS_SELF: |
|
955 fprintf(output, " 'self' "); break; |
|
956 } |
|
957 switch (test) { |
|
958 case NODE_TEST_NONE: |
|
959 fprintf(output, "'none' "); break; |
|
960 case NODE_TEST_TYPE: |
|
961 fprintf(output, "'type' "); break; |
|
962 case NODE_TEST_PI: |
|
963 fprintf(output, "'PI' "); break; |
|
964 case NODE_TEST_ALL: |
|
965 fprintf(output, "'all' "); break; |
|
966 case NODE_TEST_NS: |
|
967 fprintf(output, "'namespace' "); break; |
|
968 case NODE_TEST_NAME: |
|
969 fprintf(output, "'name' "); break; |
|
970 } |
|
971 switch (type) { |
|
972 case NODE_TYPE_NODE: |
|
973 fprintf(output, "'node' "); break; |
|
974 case NODE_TYPE_COMMENT: |
|
975 fprintf(output, "'comment' "); break; |
|
976 case NODE_TYPE_TEXT: |
|
977 fprintf(output, "'text' "); break; |
|
978 case NODE_TYPE_PI: |
|
979 fprintf(output, "'PI' "); break; |
|
980 } |
|
981 if (prefix != NULL) |
|
982 fprintf(output, "%s:", prefix); |
|
983 if (name != NULL) |
|
984 fprintf(output, "%s", (const char *) name); |
|
985 break; |
|
986 |
|
987 } |
|
988 case XPATH_OP_VALUE: { |
|
989 xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4; |
|
990 |
|
991 fprintf(output, "ELEM "); |
|
992 xmlXPathDebugDumpObject(output, object, 0); |
|
993 goto finish; |
|
994 } |
|
995 case XPATH_OP_VARIABLE: { |
|
996 const xmlChar *prefix = op->value5; |
|
997 const xmlChar *name = op->value4; |
|
998 |
|
999 if (prefix != NULL) |
|
1000 fprintf(output, "VARIABLE %s:%s", prefix, name); |
|
1001 else |
|
1002 fprintf(output, "VARIABLE %s", name); |
|
1003 break; |
|
1004 } |
|
1005 case XPATH_OP_FUNCTION: { |
|
1006 int nbargs = op->value; |
|
1007 const xmlChar *prefix = op->value5; |
|
1008 const xmlChar *name = op->value4; |
|
1009 |
|
1010 if (prefix != NULL) |
|
1011 fprintf(output, "FUNCTION %s:%s(%d args)", |
|
1012 prefix, name, nbargs); |
|
1013 else |
|
1014 fprintf(output, "FUNCTION %s(%d args)", name, nbargs); |
|
1015 break; |
|
1016 } |
|
1017 case XPATH_OP_ARG: fprintf(output, "ARG"); break; |
|
1018 case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break; |
|
1019 case XPATH_OP_FILTER: fprintf(output, "FILTER"); break; |
|
1020 #ifdef LIBXML_XPTR_ENABLED |
|
1021 case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break; |
|
1022 #endif |
|
1023 default: |
|
1024 fprintf(output, "UNKNOWN %d\n", op->op); return; |
|
1025 } |
|
1026 fprintf(output, "\n"); |
|
1027 finish: |
|
1028 if (op->ch1 >= 0) |
|
1029 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1); |
|
1030 if (op->ch2 >= 0) |
|
1031 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1); |
|
1032 } |
|
1033 |
|
1034 /** |
|
1035 * xmlXPathDebugDumpCompExpr: |
|
1036 * @output: the FILE * for the output |
|
1037 * @comp: the precompiled XPath expression |
|
1038 * @depth: the indentation level. |
|
1039 * |
|
1040 * Dumps the tree of the compiled XPath expression. |
|
1041 */ |
|
1042 void |
|
1043 xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp, |
|
1044 int depth) { |
|
1045 int i; |
|
1046 char shift[100]; |
|
1047 |
|
1048 for (i = 0;((i < depth) && (i < 25));i++) |
|
1049 shift[2 * i] = shift[2 * i + 1] = ' '; |
|
1050 shift[2 * i] = shift[2 * i + 1] = 0; |
|
1051 |
|
1052 fprintf(output, shift); |
|
1053 |
|
1054 if (comp == NULL) { |
|
1055 fprintf(output, "Compiled Expression is NULL\n"); |
|
1056 return; |
|
1057 } |
|
1058 fprintf(output, "Compiled Expression : %d elements\n", |
|
1059 comp->nbStep); |
|
1060 i = comp->last; |
|
1061 xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1); |
|
1062 } |
|
1063 #endif /* LIBXML_DEBUG_ENABLED */ |
|
1064 |
|
1065 /************************************************************************ |
|
1066 * * |
|
1067 * Parser stacks related functions and macros * |
|
1068 * * |
|
1069 ************************************************************************/ |
|
1070 |
|
1071 /** |
|
1072 * valuePop: |
|
1073 * @ctxt: an XPath evaluation context |
|
1074 * |
|
1075 * Pops the top XPath object from the value stack |
|
1076 * |
|
1077 * Returns the XPath object just removed |
|
1078 */ |
|
1079 extern xmlXPathObjectPtr |
|
1080 valuePop(xmlXPathParserContextPtr ctxt) |
|
1081 { |
|
1082 xmlXPathObjectPtr ret; |
|
1083 |
|
1084 if (ctxt->valueNr <= 0) |
|
1085 return (0); |
|
1086 ctxt->valueNr--; |
|
1087 if (ctxt->valueNr > 0) |
|
1088 ctxt->value = ctxt->valueTab[ctxt->valueNr - 1]; |
|
1089 else |
|
1090 ctxt->value = NULL; |
|
1091 ret = ctxt->valueTab[ctxt->valueNr]; |
|
1092 ctxt->valueTab[ctxt->valueNr] = 0; |
|
1093 return (ret); |
|
1094 } |
|
1095 /** |
|
1096 * valuePush: |
|
1097 * @ctxt: an XPath evaluation context |
|
1098 * @value: the XPath object |
|
1099 * |
|
1100 * Pushes a new XPath object on top of the value stack |
|
1101 * |
|
1102 * returns the number of items on the value stack |
|
1103 * |
|
1104 * OOM: possible --> returns 0 |
|
1105 */ |
|
1106 int |
|
1107 valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value) |
|
1108 { |
|
1109 if (ctxt->valueNr >= ctxt->valueMax) |
|
1110 { |
|
1111 xmlXPathObjectPtr* tmp; |
|
1112 // DONE: Fix xmlRealloc |
|
1113 tmp = (xmlXPathObjectPtr*)xmlRealloc(ctxt->valueTab, |
|
1114 ctxt->valueMax * |
|
1115 2 * sizeof(ctxt->valueTab[0])); |
|
1116 if (!tmp) { |
|
1117 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("realloc failed !\n")); |
|
1118 return (0); |
|
1119 } |
|
1120 ctxt->valueTab = tmp; |
|
1121 ctxt->valueMax *= 2; |
|
1122 } |
|
1123 ctxt->valueTab[ctxt->valueNr] = value; |
|
1124 ctxt->value = value; |
|
1125 return (ctxt->valueNr++); |
|
1126 } |
|
1127 |
|
1128 /** |
|
1129 * xmlXPathPopBoolean: |
|
1130 * @ctxt: an XPath parser context |
|
1131 * |
|
1132 * Pops a boolean from the stack, handling conversion if needed. |
|
1133 * Check error with #xmlXPathCheckError. |
|
1134 * |
|
1135 * Returns the boolean |
|
1136 */ |
|
1137 int |
|
1138 xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) { |
|
1139 xmlXPathObjectPtr obj; |
|
1140 int ret; |
|
1141 |
|
1142 obj = valuePop(ctxt); |
|
1143 if (obj == NULL) { |
|
1144 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1145 return(0); |
|
1146 } |
|
1147 if (obj->type != XPATH_BOOLEAN) |
|
1148 ret = xmlXPathCastToBoolean(obj); |
|
1149 else |
|
1150 ret = obj->boolval; |
|
1151 xmlXPathFreeObject(obj); |
|
1152 return(ret); |
|
1153 } |
|
1154 |
|
1155 /** |
|
1156 * xmlXPathPopNumber: |
|
1157 * @ctxt: an XPath parser context |
|
1158 * |
|
1159 * Pops a number from the stack, handling conversion if needed. |
|
1160 * Check error with #xmlXPathCheckError. |
|
1161 * |
|
1162 * Returns the number |
|
1163 */ |
|
1164 double |
|
1165 xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) { |
|
1166 xmlXPathObjectPtr obj; |
|
1167 double ret; |
|
1168 |
|
1169 obj = valuePop(ctxt); |
|
1170 if (obj == NULL) { |
|
1171 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1172 return(0); |
|
1173 } |
|
1174 if (obj->type != XPATH_NUMBER) |
|
1175 ret = xmlXPathCastToNumber(obj); |
|
1176 else |
|
1177 ret = obj->floatval; |
|
1178 xmlXPathFreeObject(obj); |
|
1179 return(ret); |
|
1180 } |
|
1181 |
|
1182 /** |
|
1183 * xmlXPathPopString: |
|
1184 * @ctxt: an XPath parser context |
|
1185 * |
|
1186 * Pops a string from the stack, handling conversion if needed. |
|
1187 * Check error with #xmlXPathCheckError. |
|
1188 * |
|
1189 * Returns the string |
|
1190 */ |
|
1191 xmlChar * |
|
1192 xmlXPathPopString (xmlXPathParserContextPtr ctxt) { |
|
1193 xmlXPathObjectPtr obj; |
|
1194 xmlChar * ret; |
|
1195 |
|
1196 obj = valuePop(ctxt); |
|
1197 if (obj == NULL) { |
|
1198 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1199 return(NULL); |
|
1200 } |
|
1201 ret = xmlXPathCastToString(obj); /* this does required strdup */ |
|
1202 /* TODO: needs refactoring somewhere else */ |
|
1203 if (obj->stringval == ret) |
|
1204 obj->stringval = NULL; |
|
1205 xmlXPathFreeObject(obj); |
|
1206 return(ret); |
|
1207 } |
|
1208 |
|
1209 /** |
|
1210 * xmlXPathPopNodeSet: |
|
1211 * @ctxt: an XPath parser context |
|
1212 * |
|
1213 * Pops a node-set from the stack, handling conversion if needed. |
|
1214 * Check error with #xmlXPathCheckError. |
|
1215 * |
|
1216 * Returns the node-set |
|
1217 */ |
|
1218 xmlNodeSetPtr |
|
1219 xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) { |
|
1220 xmlXPathObjectPtr obj; |
|
1221 xmlNodeSetPtr ret; |
|
1222 |
|
1223 if (ctxt->value == NULL) { |
|
1224 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1225 return(NULL); |
|
1226 } |
|
1227 if (!xmlXPathStackIsNodeSet(ctxt)) { |
|
1228 xmlXPathSetTypeError(ctxt); |
|
1229 return(NULL); |
|
1230 } |
|
1231 obj = valuePop(ctxt); |
|
1232 ret = obj->nodesetval; |
|
1233 /* to fix memory leak of not clearing obj->user */ |
|
1234 if (obj->boolval && obj->user != NULL) |
|
1235 xmlFreeNodeList((xmlNodePtr) obj->user); |
|
1236 xmlXPathFreeNodeSetList(obj); |
|
1237 return(ret); |
|
1238 } |
|
1239 |
|
1240 /** |
|
1241 * xmlXPathPopExternal: |
|
1242 * @ctxt: an XPath parser context |
|
1243 * |
|
1244 * Pops an external object from the stack, handling conversion if needed. |
|
1245 * Check error with #xmlXPathCheckError. |
|
1246 * |
|
1247 * Returns the object |
|
1248 */ |
|
1249 void * |
|
1250 xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) { |
|
1251 xmlXPathObjectPtr obj; |
|
1252 void * ret; |
|
1253 |
|
1254 if (ctxt->value == NULL) { |
|
1255 xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
|
1256 return(NULL); |
|
1257 } |
|
1258 if (ctxt->value->type != XPATH_USERS) { |
|
1259 xmlXPathSetTypeError(ctxt); |
|
1260 return(NULL); |
|
1261 } |
|
1262 obj = valuePop(ctxt); |
|
1263 ret = obj->user; |
|
1264 xmlXPathFreeObject(obj); |
|
1265 return(ret); |
|
1266 } |
|
1267 |
|
1268 /* |
|
1269 * Macros for accessing the content. Those should be used only by the parser, |
|
1270 * and not exported. |
|
1271 * |
|
1272 * Dirty macros, i.e. one need to make assumption on the context to use them |
|
1273 * |
|
1274 * CUR_PTR return the current pointer to the xmlChar to be parsed. |
|
1275 * CUR returns the current xmlChar value, i.e. a 8 bit value |
|
1276 * in ISO-Latin or UTF-8. |
|
1277 * This should be used internally by the parser |
|
1278 * only to compare to ASCII values otherwise it would break when |
|
1279 * running with UTF-8 encoding. |
|
1280 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only |
|
1281 * to compare on ASCII based substring. |
|
1282 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined |
|
1283 * strings within the parser. |
|
1284 * CURRENT Returns the current char value, with the full decoding of |
|
1285 * UTF-8 if we are using this mode. It returns an int. |
|
1286 * NEXT Skip to the next character, this does the proper decoding |
|
1287 * in UTF-8 mode. It also pop-up unfinished entities on the fly. |
|
1288 * It returns the pointer to the current xmlChar. |
|
1289 */ |
|
1290 |
|
1291 #define CUR (*ctxt->cur) |
|
1292 #define SKIP(val) ctxt->cur += (val) |
|
1293 #define NXT(val) ctxt->cur[(val)] |
|
1294 #define CUR_PTR ctxt->cur |
|
1295 #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l) |
|
1296 |
|
1297 #define COPY_BUF(len,b,i,v) \ |
|
1298 if (len == 1) b[i++] = (xmlChar) v; \ |
|
1299 else i += xmlCopyChar(len,&b[i],v) |
|
1300 |
|
1301 #define NEXTL(k) ctxt->cur += k |
|
1302 |
|
1303 // XMLENGINE: NEXT was replaced with ctxt->cur++ since *ctxt->cur always !=0 there |
|
1304 // OOM: never |
|
1305 #define SKIP_BLANKS while (IS_BLANK_CH(*(ctxt->cur))) ctxt->cur++ |
|
1306 |
|
1307 #define CURRENT (*ctxt->cur) |
|
1308 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) |
|
1309 |
|
1310 |
|
1311 #ifndef DBL_DIG |
|
1312 #define DBL_DIG 16 |
|
1313 #endif |
|
1314 #ifndef DBL_EPSILON |
|
1315 #define DBL_EPSILON 1E-9 |
|
1316 #endif |
|
1317 |
|
1318 #define UPPER_DOUBLE 1E9 |
|
1319 #define LOWER_DOUBLE 1E-5 |
|
1320 |
|
1321 #define INTEGER_DIGITS DBL_DIG |
|
1322 #define FRACTION_DIGITS (DBL_DIG + 1) |
|
1323 #define EXPONENT_DIGITS (3 + 2) |
|
1324 |
|
1325 #if (_MSC_VER >= 1300) && (WINVER < 0x0500) |
|
1326 //VC7 or later, building with pre-VC7 runtime libraries |
|
1327 //extern "C" long _ftol( double ); //defined by VC6 C libs |
|
1328 //extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource ); } |
|
1329 #endif |
|
1330 |
|
1331 /** |
|
1332 * xmlXPathFormatNumber: |
|
1333 * @number: number to format |
|
1334 * @buffer: output buffer |
|
1335 * @buffersize: size of output buffer |
|
1336 * |
|
1337 * Convert the number into a string representation. |
|
1338 * |
|
1339 * OOM: never |
|
1340 */ |
|
1341 static void |
|
1342 xmlXPathFormatNumber(double number, char buffer[], int buffersize) |
|
1343 { // TODO: use IFs for clearer code / combine calls to snprintf() |
|
1344 switch (xmlXPathIsInf(number)) { |
|
1345 case 1: |
|
1346 if (buffersize > (int)sizeof("Infinity")) |
|
1347 snprintf(buffer, buffersize, "Infinity"); |
|
1348 break; |
|
1349 case -1: |
|
1350 if (buffersize > (int)sizeof("-Infinity")) |
|
1351 snprintf(buffer, buffersize, "-Infinity"); |
|
1352 break; |
|
1353 default: |
|
1354 if (xmlXPathIsNaN(number)) { |
|
1355 if (buffersize > (int)sizeof("NaN")) |
|
1356 snprintf(buffer, buffersize, "NaN"); |
|
1357 } else |
|
1358 if (number == 0 && xmlXPathGetSign(number) != 0) { |
|
1359 snprintf(buffer, buffersize, "0"); |
|
1360 } else |
|
1361 if (number == ((int) number)) { |
|
1362 char work[30]; |
|
1363 char *ptr, *cur; |
|
1364 int res, value = (int) number; |
|
1365 |
|
1366 ptr = &buffer[0]; |
|
1367 if (value < 0) { |
|
1368 *ptr++ = '-'; |
|
1369 value = -value; |
|
1370 } |
|
1371 if (value == 0) { |
|
1372 *ptr++ = '0'; |
|
1373 } else { |
|
1374 cur = &work[0]; |
|
1375 while (value != 0) { |
|
1376 res = value % 10; |
|
1377 value = value / 10; |
|
1378 *cur++ = '0' + res; |
|
1379 } |
|
1380 cur--; |
|
1381 while ((cur >= &work[0]) && (ptr - buffer < buffersize)) { |
|
1382 *ptr++ = *cur--; |
|
1383 } |
|
1384 } |
|
1385 if (ptr - buffer < buffersize) { |
|
1386 *ptr = 0; |
|
1387 } else if (buffersize > 0) { |
|
1388 ptr--; |
|
1389 *ptr = 0; |
|
1390 } |
|
1391 } else { |
|
1392 /* 3 is sign, decimal point, and terminating zero */ |
|
1393 char work[DBL_DIG + EXPONENT_DIGITS + 3]; |
|
1394 int integer_place, fraction_place; |
|
1395 char *ptr; |
|
1396 char *after_fraction; |
|
1397 double absolute_value; |
|
1398 int size; |
|
1399 |
|
1400 absolute_value = fabs(number); |
|
1401 |
|
1402 /* |
|
1403 * First choose format - scientific or regular floating point. |
|
1404 * In either case, result is in work, and after_fraction points |
|
1405 * just past the fractional part. |
|
1406 */ |
|
1407 if (((absolute_value > UPPER_DOUBLE) || (absolute_value < LOWER_DOUBLE)) && |
|
1408 (absolute_value != 0.0)) |
|
1409 { |
|
1410 /* Use scientific notation */ |
|
1411 integer_place = DBL_DIG + EXPONENT_DIGITS + 1; |
|
1412 fraction_place = DBL_DIG - 1; |
|
1413 snprintf(work, sizeof(work),"%*.*e", |
|
1414 integer_place, fraction_place, number); |
|
1415 after_fraction = strchr(work + DBL_DIG, 'e'); |
|
1416 } |
|
1417 else { |
|
1418 /* Use regular notation */ |
|
1419 if (absolute_value > 0.0) |
|
1420 integer_place = 1 + (int)log10(absolute_value); |
|
1421 else |
|
1422 integer_place = 0; |
|
1423 |
|
1424 fraction_place = (integer_place > 0) |
|
1425 ? DBL_DIG - integer_place |
|
1426 : DBL_DIG; |
|
1427 size = snprintf(work, sizeof(work), "%0.*f", fraction_place, number); |
|
1428 after_fraction = work + size; |
|
1429 } |
|
1430 |
|
1431 /* Remove fractional trailing zeroes */ |
|
1432 ptr = after_fraction; |
|
1433 while (*(--ptr) == '0') {}// EMPTY LOOP |
|
1434 |
|
1435 if (*ptr != '.') |
|
1436 ptr++; |
|
1437 while ((*ptr++ = *after_fraction++) != 0){} // EMPTY LOOP |
|
1438 |
|
1439 /* Finally copy result back to caller */ |
|
1440 size = strlen(work) + 1; |
|
1441 if (size > buffersize) { |
|
1442 work[buffersize - 1] = 0; |
|
1443 size = buffersize; |
|
1444 } |
|
1445 memmove(buffer, work, size); |
|
1446 } |
|
1447 break; |
|
1448 } |
|
1449 } |
|
1450 |
|
1451 |
|
1452 /************************************************************************ |
|
1453 * * |
|
1454 * Routines to handle NodeSets * |
|
1455 * * |
|
1456 ************************************************************************/ |
|
1457 |
|
1458 /** |
|
1459 * xmlXPathOrderDocElems: |
|
1460 * @doc: an input document |
|
1461 * |
|
1462 * Call this routine to speed up XPath computation on static documents. |
|
1463 * This stamps all the element nodes with the document order |
|
1464 * Like for line information, the order is kept in the element->content |
|
1465 * field, the value stored is actually - the node number (starting at -1) |
|
1466 * to be able to differentiate from line numbers. |
|
1467 * |
|
1468 * Returns the number of elements found in the document or -1 in case |
|
1469 * of error. |
|
1470 */ |
|
1471 long |
|
1472 xmlXPathOrderDocElems(xmlDocPtr doc) { |
|
1473 long count = 0; |
|
1474 xmlNodePtr cur; |
|
1475 |
|
1476 if (doc == NULL) |
|
1477 return(-1); |
|
1478 cur = doc->children; |
|
1479 while (cur != NULL) { |
|
1480 if (cur->type == XML_ELEMENT_NODE) { |
|
1481 cur->content = (xmlChar*)((void *) (-(++count))); |
|
1482 if (cur->children != NULL) { |
|
1483 cur = cur->children; |
|
1484 continue; |
|
1485 } |
|
1486 } |
|
1487 if (cur->next != NULL) { |
|
1488 cur = cur->next; |
|
1489 continue; |
|
1490 } |
|
1491 do { |
|
1492 cur = cur->parent; |
|
1493 if (cur == NULL) |
|
1494 break; |
|
1495 if (cur == (xmlNodePtr) doc) { |
|
1496 cur = NULL; |
|
1497 break; |
|
1498 } |
|
1499 if (cur->next != NULL) { |
|
1500 cur = cur->next; |
|
1501 break; |
|
1502 } |
|
1503 } while (cur != NULL); |
|
1504 } |
|
1505 return(count); |
|
1506 } |
|
1507 |
|
1508 /** |
|
1509 * xmlXPathCmpNodes: |
|
1510 * @node1: the first node |
|
1511 * @node2: the second node |
|
1512 * |
|
1513 * Compare two nodes w.r.t document order |
|
1514 * |
|
1515 * Returns -2 in case of error 1 if first point < second point, 0 if |
|
1516 * it's the same node, -1 otherwise |
|
1517 * |
|
1518 * OOM: never |
|
1519 */ |
|
1520 int |
|
1521 xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) { |
|
1522 int depth1, depth2; |
|
1523 int attr1 = 0, attr2 = 0; |
|
1524 xmlNodePtr attrNode1 = NULL, attrNode2 = NULL; |
|
1525 xmlNodePtr cur, root; |
|
1526 |
|
1527 if ((node1 == NULL) || (node2 == NULL)) |
|
1528 return(-2); |
|
1529 /* |
|
1530 * a couple of optimizations which will avoid computations in most cases |
|
1531 */ |
|
1532 if (node1->type == XML_ATTRIBUTE_NODE) { |
|
1533 attr1 = 1; |
|
1534 attrNode1 = node1; |
|
1535 node1 = node1->parent; |
|
1536 } |
|
1537 if (node2->type == XML_ATTRIBUTE_NODE) { |
|
1538 attr2 = 1; |
|
1539 attrNode2 = node2; |
|
1540 node2 = node2->parent; |
|
1541 } |
|
1542 if (node1 == node2) { |
|
1543 if (attr1 == attr2) { |
|
1544 /* not required, but we keep attributes in order */ |
|
1545 if (attr1 != 0) { |
|
1546 cur = attrNode2->prev; |
|
1547 while (cur != NULL) { |
|
1548 if (cur == attrNode1) |
|
1549 return (1); |
|
1550 cur = cur->prev; |
|
1551 } |
|
1552 return (-1); |
|
1553 } |
|
1554 return(0); |
|
1555 } |
|
1556 if (attr2 == 1) |
|
1557 return(1); |
|
1558 return(-1); |
|
1559 } |
|
1560 if ((node1->type == XML_NAMESPACE_DECL) || |
|
1561 (node2->type == XML_NAMESPACE_DECL)) |
|
1562 return(1); |
|
1563 if (node1 == node2->prev) |
|
1564 return(1); |
|
1565 if (node1 == node2->next) |
|
1566 return(-1); |
|
1567 |
|
1568 /* |
|
1569 * Speedup using document order if availble. |
|
1570 */ |
|
1571 if ((node1->type == XML_ELEMENT_NODE) && |
|
1572 (node2->type == XML_ELEMENT_NODE) && |
|
1573 (0 > (long) node1->content) && |
|
1574 (0 > (long) node2->content) && |
|
1575 (node1->doc == node2->doc)) { |
|
1576 long l1, l2; |
|
1577 |
|
1578 l1 = -((long) node1->content); |
|
1579 l2 = -((long) node2->content); |
|
1580 if (l1 < l2) |
|
1581 return(1); |
|
1582 if (l1 > l2) |
|
1583 return(-1); |
|
1584 } |
|
1585 |
|
1586 /* |
|
1587 * compute depth to root |
|
1588 */ |
|
1589 for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { |
|
1590 if (cur == node1) |
|
1591 return(1); |
|
1592 depth2++; |
|
1593 } |
|
1594 root = cur; |
|
1595 for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { |
|
1596 if (cur == node2) |
|
1597 return(-1); |
|
1598 depth1++; |
|
1599 } |
|
1600 /* |
|
1601 * Distinct document (or distinct entities :-( ) case. |
|
1602 */ |
|
1603 if (root != cur) { |
|
1604 return(-2); |
|
1605 } |
|
1606 /* |
|
1607 * get the nearest common ancestor. |
|
1608 */ |
|
1609 while (depth1 > depth2) { |
|
1610 depth1--; |
|
1611 node1 = node1->parent; |
|
1612 } |
|
1613 while (depth2 > depth1) { |
|
1614 depth2--; |
|
1615 node2 = node2->parent; |
|
1616 } |
|
1617 while (node1->parent != node2->parent) { |
|
1618 node1 = node1->parent; |
|
1619 node2 = node2->parent; |
|
1620 /* should not happen but just in case ... */ |
|
1621 if ((node1 == NULL) || (node2 == NULL)) |
|
1622 return(-2); |
|
1623 } |
|
1624 /* |
|
1625 * Find who's first. |
|
1626 */ |
|
1627 if (node1 == node2->prev) |
|
1628 return(1); |
|
1629 if (node1 == node2->next) |
|
1630 return(-1); |
|
1631 /* |
|
1632 * Speedup using document order if availble. |
|
1633 */ |
|
1634 if ((node1->type == XML_ELEMENT_NODE) && |
|
1635 (node2->type == XML_ELEMENT_NODE) && |
|
1636 (0 > (long) node1->content) && |
|
1637 (0 > (long) node2->content) && |
|
1638 (node1->doc == node2->doc)) { |
|
1639 long l1, l2; |
|
1640 |
|
1641 l1 = -((long) node1->content); |
|
1642 l2 = -((long) node2->content); |
|
1643 if (l1 < l2) |
|
1644 return(1); |
|
1645 if (l1 > l2) |
|
1646 return(-1); |
|
1647 } |
|
1648 |
|
1649 for (cur = node1->next;cur != NULL;cur = cur->next) |
|
1650 if (cur == node2) |
|
1651 return(1); |
|
1652 return(-1); /* assume there is no sibling list corruption */ |
|
1653 } |
|
1654 |
|
1655 /** |
|
1656 * xmlXPathNodeSetSort: |
|
1657 * @set: the node set |
|
1658 * |
|
1659 * Sort the node set in document order |
|
1660 * |
|
1661 * OOM: never |
|
1662 */ |
|
1663 void |
|
1664 xmlXPathNodeSetSort(xmlNodeSetPtr set) { |
|
1665 int i, j, incr, len; |
|
1666 xmlNodePtr tmp; |
|
1667 |
|
1668 if (set == NULL) |
|
1669 return; |
|
1670 |
|
1671 /* Use Shell's sort to sort the node-set */ |
|
1672 len = set->nodeNr; |
|
1673 for (incr = len / 2; incr > 0; incr /= 2) { |
|
1674 for (i = incr; i < len; i++) { |
|
1675 j = i - incr; |
|
1676 while (j >= 0) { |
|
1677 if (xmlXPathCmpNodes(set->nodeTab[j], set->nodeTab[j + incr]) == -1) |
|
1678 { |
|
1679 tmp = set->nodeTab[j]; |
|
1680 set->nodeTab[j] = set->nodeTab[j + incr]; |
|
1681 set->nodeTab[j + incr] = tmp; |
|
1682 j -= incr; |
|
1683 } else |
|
1684 break; |
|
1685 } |
|
1686 } |
|
1687 } |
|
1688 } |
|
1689 |
|
1690 #define XML_NODESET_DEFAULT 10 |
|
1691 /** |
|
1692 * xmlXPathNodeSetDupNs: |
|
1693 * @node: the parent node of the namespace XPath node |
|
1694 * @ns: the libxml namespace declaration node. |
|
1695 * |
|
1696 * Namespace node in libxml don't match the XPath semantic. In a node set |
|
1697 * the namespace nodes are duplicated and the next pointer is set to the |
|
1698 * parent node in the XPath semantic. |
|
1699 * |
|
1700 * Returns the newly created object. |
|
1701 * |
|
1702 * OOM: possible --> returns NULL for valid arguments, sets OOM flag |
|
1703 */ |
|
1704 static xmlNodePtr |
|
1705 xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) { |
|
1706 xmlNsPtr cur; |
|
1707 |
|
1708 if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) |
|
1709 return(NULL); |
|
1710 if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) |
|
1711 return((xmlNodePtr) ns); |
|
1712 |
|
1713 /* |
|
1714 * Allocate a new Namespace and fill the fields. |
|
1715 */ |
|
1716 cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); |
|
1717 if (cur == NULL) { |
|
1718 xmlXPathErrMemory(NULL, EMBED_ERRTXT("duplicating namespace\n")); |
|
1719 return(NULL); |
|
1720 } |
|
1721 memset(cur, 0, sizeof(xmlNs)); |
|
1722 cur->type = XML_NAMESPACE_DECL; |
|
1723 // TODO: Check OOM conditions.. |
|
1724 if (ns->href != NULL) |
|
1725 if(! (cur->href = xmlStrdup(ns->href))) |
|
1726 goto OOM;; |
|
1727 if (ns->prefix != NULL) |
|
1728 if(! (cur->prefix = xmlStrdup(ns->prefix))) |
|
1729 goto OOM; |
|
1730 |
|
1731 cur->next = (xmlNsPtr) node; |
|
1732 return((xmlNodePtr) cur); |
|
1733 //--------------------- |
|
1734 OOM: |
|
1735 xmlFree(cur); |
|
1736 return NULL; |
|
1737 } |
|
1738 |
|
1739 /** |
|
1740 * xmlXPathNodeSetFreeNs: |
|
1741 * @ns: the XPath namespace node found in a nodeset. |
|
1742 * |
|
1743 * Namespace nodes in libxml don't match the XPath semantic. In a node set |
|
1744 * the namespace nodes are duplicated and the next pointer is set to the |
|
1745 * parent node in the XPath semantic. Check if such a node needs to be freed |
|
1746 * |
|
1747 * OOM: never |
|
1748 */ |
|
1749 void |
|
1750 xmlXPathNodeSetFreeNs(xmlNsPtr ns) { |
|
1751 if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL)) |
|
1752 return; |
|
1753 |
|
1754 if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) { |
|
1755 if (ns->href != NULL) |
|
1756 xmlFree((xmlChar *)ns->href); |
|
1757 if (ns->prefix != NULL) |
|
1758 xmlFree((xmlChar *)ns->prefix); |
|
1759 xmlFree(ns); |
|
1760 } |
|
1761 } |
|
1762 |
|
1763 /** |
|
1764 * xmlXPathNodeSetCreate: |
|
1765 * @val: an initial xmlNodePtr, or NULL |
|
1766 * |
|
1767 * Create a new xmlNodeSetPtr of type double and of value @val |
|
1768 * |
|
1769 * Returns the newly created object. |
|
1770 * |
|
1771 * OOM: possible --> returns NULL and sets OOM flag |
|
1772 */ |
|
1773 xmlNodeSetPtr |
|
1774 xmlXPathNodeSetCreate(xmlNodePtr val) { |
|
1775 xmlNodeSetPtr ret; |
|
1776 |
|
1777 ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet)); |
|
1778 if (ret == NULL) { |
|
1779 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n")); |
|
1780 return(NULL); |
|
1781 } |
|
1782 memset(ret, 0 , (size_t) sizeof(xmlNodeSet)); |
|
1783 |
|
1784 if (val != NULL) { |
|
1785 xmlNodePtr nval; |
|
1786 // TODO: Optimize or make adjustable the allocation size of NodeSet |
|
1787 ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); |
|
1788 if (ret->nodeTab == NULL) { |
|
1789 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n")); |
|
1790 goto OOM; |
|
1791 } |
|
1792 memset(ret->nodeTab, 0, XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
1793 ret->nodeMax = XML_NODESET_DEFAULT; |
|
1794 if (val->type == XML_NAMESPACE_DECL) { |
|
1795 xmlNsPtr ns = (xmlNsPtr) val; |
|
1796 nval = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
1797 // TODO: OOM: Chrck OOM |
|
1798 if(OOM_FLAG) |
|
1799 goto OOM; |
|
1800 } else { |
|
1801 nval = val; |
|
1802 } |
|
1803 ret->nodeTab[ret->nodeNr++] = nval; |
|
1804 } |
|
1805 return(ret); |
|
1806 //------------------- |
|
1807 OOM: |
|
1808 xmlFree(ret); |
|
1809 return(NULL); |
|
1810 } |
|
1811 |
|
1812 /** |
|
1813 * xmlXPathNodeSetContains: |
|
1814 * @cur: the node-set |
|
1815 * @val: the node |
|
1816 * |
|
1817 * checks whether @cur contains @val |
|
1818 * |
|
1819 * Returns true (1) if @cur contains @val, false (0) otherwise |
|
1820 * |
|
1821 * OOM: never |
|
1822 */ |
|
1823 int |
|
1824 xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) { |
|
1825 int i; |
|
1826 |
|
1827 if (val->type == XML_NAMESPACE_DECL) { |
|
1828 for (i = 0; i < cur->nodeNr; i++) { |
|
1829 if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
1830 xmlNsPtr ns1, ns2; |
|
1831 |
|
1832 ns1 = (xmlNsPtr) val; |
|
1833 ns2 = (xmlNsPtr) cur->nodeTab[i]; |
|
1834 if (ns1 == ns2) |
|
1835 return(1); |
|
1836 if ((ns1->next != NULL) && |
|
1837 (ns2->next == ns1->next) && |
|
1838 (xmlStrEqual(ns1->prefix, ns2->prefix))) |
|
1839 return(1); |
|
1840 } |
|
1841 } |
|
1842 } else { |
|
1843 for (i = 0; i < cur->nodeNr; i++) { |
|
1844 if (cur->nodeTab[i] == val) |
|
1845 return(1); |
|
1846 } |
|
1847 } |
|
1848 return(0); |
|
1849 } |
|
1850 |
|
1851 |
|
1852 |
|
1853 /* |
|
1854 TODO: OPTIMIZATION: xmlXPathNodeSetAddNs, xmlXPathNodeSetAdd and xmlXPathNodeSetAddUnique are the same code |
|
1855 |
|
1856 */ |
|
1857 |
|
1858 /** |
|
1859 * xmlXPathNodeSetAddNs: |
|
1860 * @cur: the initial node set |
|
1861 * @node: the hosting node |
|
1862 * @ns: a the namespace node |
|
1863 * |
|
1864 * add a new namespace node to an existing NodeSet |
|
1865 */ |
|
1866 void |
|
1867 xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
|
1868 int i; |
|
1869 |
|
1870 if ((ns == NULL) || (node == NULL) || (ns->type != XML_NAMESPACE_DECL) || |
|
1871 (node->type != XML_ELEMENT_NODE)) |
|
1872 return; |
|
1873 |
|
1874 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
1875 /* |
|
1876 * prevent duplicates |
|
1877 */ |
|
1878 for (i = 0;i < cur->nodeNr;i++) { |
|
1879 if ((cur->nodeTab[i] != NULL) && |
|
1880 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) && |
|
1881 (((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) && |
|
1882 (xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix))) |
|
1883 return; |
|
1884 } |
|
1885 |
|
1886 /* |
|
1887 * grow the nodeTab if needed |
|
1888 */ |
|
1889 if (cur->nodeMax == 0) { |
|
1890 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
|
1891 sizeof(xmlNodePtr)); |
|
1892 if (cur->nodeTab == NULL) { |
|
1893 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1894 return; |
|
1895 } |
|
1896 memset(cur->nodeTab, 0 , |
|
1897 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
1898 cur->nodeMax = XML_NODESET_DEFAULT; |
|
1899 } else if (cur->nodeNr == cur->nodeMax) { |
|
1900 xmlNodePtr *temp; |
|
1901 |
|
1902 cur->nodeMax *= 2; |
|
1903 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * |
|
1904 sizeof(xmlNodePtr)); |
|
1905 if (temp == NULL) { |
|
1906 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1907 return; |
|
1908 } |
|
1909 cur->nodeTab = temp; |
|
1910 } |
|
1911 cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns); |
|
1912 } |
|
1913 |
|
1914 /** |
|
1915 * xmlXPathNodeSetAdd: |
|
1916 * @cur: the initial node set |
|
1917 * @val: a new xmlNodePtr |
|
1918 * |
|
1919 * add a new xmlNodePtr to an existing NodeSet |
|
1920 */ |
|
1921 void |
|
1922 xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
|
1923 int i; |
|
1924 |
|
1925 if (val == NULL) return; |
|
1926 |
|
1927 #if 0 |
|
1928 if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) |
|
1929 return; /* an XSLT fake node */ |
|
1930 #endif |
|
1931 |
|
1932 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
1933 /* |
|
1934 * prevent duplcates |
|
1935 */ |
|
1936 for (i = 0;i < cur->nodeNr;i++) |
|
1937 if (cur->nodeTab[i] == val) return; |
|
1938 |
|
1939 /* |
|
1940 * grow the nodeTab if needed |
|
1941 */ |
|
1942 if (cur->nodeMax == 0) { |
|
1943 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
|
1944 sizeof(xmlNodePtr)); |
|
1945 if (cur->nodeTab == NULL) { |
|
1946 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1947 return; |
|
1948 } |
|
1949 memset(cur->nodeTab, 0 , |
|
1950 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
1951 cur->nodeMax = XML_NODESET_DEFAULT; |
|
1952 } else if (cur->nodeNr == cur->nodeMax) { |
|
1953 xmlNodePtr *temp; |
|
1954 // TODO: Realocation policy may be managed |
|
1955 cur->nodeMax *= 2; |
|
1956 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * |
|
1957 sizeof(xmlNodePtr)); |
|
1958 if (temp == NULL) { |
|
1959 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
1960 return; |
|
1961 } |
|
1962 cur->nodeTab = temp; |
|
1963 } |
|
1964 if (val->type == XML_NAMESPACE_DECL) { |
|
1965 xmlNsPtr ns = (xmlNsPtr) val; |
|
1966 |
|
1967 cur->nodeTab[cur->nodeNr++] = |
|
1968 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
1969 } else |
|
1970 cur->nodeTab[cur->nodeNr++] = val; |
|
1971 } |
|
1972 |
|
1973 /** |
|
1974 * xmlXPathNodeSetAddUnique: |
|
1975 * @cur: the initial node set |
|
1976 * @val: a new xmlNodePtr |
|
1977 * |
|
1978 * add a new xmlNodePtr to an existing NodeSet, optimized version |
|
1979 * when we are sure the node is not already in the set. |
|
1980 */ |
|
1981 void |
|
1982 xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { |
|
1983 if (val == NULL) return; |
|
1984 |
|
1985 // TODO: Rudiments of XSLT - find out how these affect the XPath implementation |
|
1986 #if 0 |
|
1987 if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) |
|
1988 return; /* an XSLT fake node */ |
|
1989 #endif |
|
1990 |
|
1991 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
1992 /* |
|
1993 * grow the nodeTab if needed |
|
1994 */ |
|
1995 if (cur->nodeMax == 0) { |
|
1996 cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr)); |
|
1997 |
|
1998 if (cur->nodeTab == NULL) { |
|
1999 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
2000 return; |
|
2001 } |
|
2002 memset(cur->nodeTab, 0 , XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
2003 |
|
2004 cur->nodeMax = XML_NODESET_DEFAULT; |
|
2005 } else if (cur->nodeNr == cur->nodeMax) { |
|
2006 xmlNodePtr *temp; |
|
2007 |
|
2008 cur->nodeMax *= 2; |
|
2009 temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * |
|
2010 sizeof(xmlNodePtr)); |
|
2011 if (temp == NULL) { |
|
2012 xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n")); |
|
2013 return; |
|
2014 } |
|
2015 cur->nodeTab = temp; |
|
2016 } |
|
2017 if (val->type == XML_NAMESPACE_DECL) { |
|
2018 xmlNsPtr ns = (xmlNsPtr) val; |
|
2019 |
|
2020 cur->nodeTab[cur->nodeNr++] = |
|
2021 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
2022 } else |
|
2023 cur->nodeTab[cur->nodeNr++] = val; |
|
2024 } |
|
2025 |
|
2026 /** |
|
2027 * xmlXPathNodeSetMerge: |
|
2028 * @val1: the first NodeSet or NULL |
|
2029 * @val2: the second NodeSet |
|
2030 * |
|
2031 * Merges two nodesets, all nodes from @val2 are added to @val1 |
|
2032 * if @val1 is NULL, a new set is created and copied from @val2 |
|
2033 * |
|
2034 * Returns @val1 once extended or NULL in case of error. |
|
2035 */ |
|
2036 xmlNodeSetPtr |
|
2037 xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
|
2038 int i, j, initNr, skip; |
|
2039 |
|
2040 if (val2 == NULL) return(val1); |
|
2041 if (val1 == NULL) { |
|
2042 val1 = xmlXPathNodeSetCreate(NULL); |
|
2043 } |
|
2044 |
|
2045 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2046 initNr = val1->nodeNr; |
|
2047 |
|
2048 for (i = 0;i < val2->nodeNr;i++) { |
|
2049 /* |
|
2050 * check against duplicates |
|
2051 */ |
|
2052 skip = 0; |
|
2053 for (j = 0; j < initNr; j++) { |
|
2054 if (val1->nodeTab[j] == val2->nodeTab[i]) { |
|
2055 skip = 1; |
|
2056 break; |
|
2057 } else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) && |
|
2058 (val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) { |
|
2059 xmlNsPtr ns1, ns2; |
|
2060 ns1 = (xmlNsPtr) val1->nodeTab[j]; |
|
2061 ns2 = (xmlNsPtr) val2->nodeTab[i]; |
|
2062 if ((ns1->next == ns2->next) && |
|
2063 (xmlStrEqual(ns1->prefix, ns2->prefix))) { |
|
2064 skip = 1; |
|
2065 break; |
|
2066 } |
|
2067 } |
|
2068 } |
|
2069 if (skip) |
|
2070 continue; |
|
2071 |
|
2072 /* |
|
2073 * grow the nodeTab if needed |
|
2074 */ |
|
2075 if (val1->nodeMax == 0) { |
|
2076 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
|
2077 sizeof(xmlNodePtr)); |
|
2078 if (val1->nodeTab == NULL) { |
|
2079 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2080 return(NULL); |
|
2081 } |
|
2082 memset(val1->nodeTab, 0 , |
|
2083 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
2084 val1->nodeMax = XML_NODESET_DEFAULT; |
|
2085 } else if (val1->nodeNr == val1->nodeMax) { |
|
2086 xmlNodePtr *temp; |
|
2087 |
|
2088 val1->nodeMax *= 2; |
|
2089 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * |
|
2090 sizeof(xmlNodePtr)); |
|
2091 if (temp == NULL) { |
|
2092 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2093 return(NULL); |
|
2094 } |
|
2095 val1->nodeTab = temp; |
|
2096 } |
|
2097 if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
2098 xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; |
|
2099 |
|
2100 val1->nodeTab[val1->nodeNr++] = |
|
2101 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
2102 } else |
|
2103 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; |
|
2104 } |
|
2105 |
|
2106 return(val1); |
|
2107 } |
|
2108 |
|
2109 /** |
|
2110 * xmlXPathNodeSetMergeUnique: |
|
2111 * @val1: the first NodeSet or NULL |
|
2112 * @val2: the second NodeSet |
|
2113 * |
|
2114 * Merges two nodesets, all nodes from @val2 are added to @val1 |
|
2115 * if @val1 is NULL, a new set is created and copied from @val2 |
|
2116 * |
|
2117 * Returns @val1 once extended or NULL in case of error. |
|
2118 */ |
|
2119 static xmlNodeSetPtr |
|
2120 xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
|
2121 int i; |
|
2122 |
|
2123 if (val2 == NULL) return(val1); |
|
2124 if (val1 == NULL) { |
|
2125 val1 = xmlXPathNodeSetCreate(NULL); |
|
2126 } |
|
2127 |
|
2128 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2129 |
|
2130 for (i = 0;i < val2->nodeNr;i++) { |
|
2131 /* |
|
2132 * grow the nodeTab if needed |
|
2133 */ |
|
2134 if (val1->nodeMax == 0) { |
|
2135 val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
|
2136 sizeof(xmlNodePtr)); |
|
2137 if (val1->nodeTab == NULL) { |
|
2138 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2139 return(NULL); |
|
2140 } |
|
2141 memset(val1->nodeTab, 0 , |
|
2142 XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
|
2143 val1->nodeMax = XML_NODESET_DEFAULT; |
|
2144 } else if (val1->nodeNr == val1->nodeMax) { |
|
2145 xmlNodePtr *temp; |
|
2146 |
|
2147 val1->nodeMax *= 2; |
|
2148 temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * |
|
2149 sizeof(xmlNodePtr)); |
|
2150 if (temp == NULL) { |
|
2151 xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n")); |
|
2152 return(NULL); |
|
2153 } |
|
2154 val1->nodeTab = temp; |
|
2155 } |
|
2156 if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
2157 xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; |
|
2158 |
|
2159 val1->nodeTab[val1->nodeNr++] = |
|
2160 xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
|
2161 } else |
|
2162 val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; |
|
2163 } |
|
2164 |
|
2165 return(val1); |
|
2166 } |
|
2167 |
|
2168 /** |
|
2169 * xmlXPathNodeSetDel: |
|
2170 * @cur: the initial node set |
|
2171 * @val: an xmlNodePtr |
|
2172 * |
|
2173 * Removes an xmlNodePtr from an existing NodeSet |
|
2174 */ |
|
2175 void |
|
2176 xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) { |
|
2177 int i; |
|
2178 |
|
2179 if (cur == NULL) return; |
|
2180 if (val == NULL) return; |
|
2181 |
|
2182 /* |
|
2183 * find node in nodeTab |
|
2184 */ |
|
2185 for (i = 0;i < cur->nodeNr;i++) |
|
2186 if (cur->nodeTab[i] == val) break; |
|
2187 |
|
2188 if (i >= cur->nodeNr) { /* not found */ |
|
2189 #ifdef DEBUG |
|
2190 xmlGenericError(xmlGenericErrorContext, |
|
2191 "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n", |
|
2192 val->name); |
|
2193 #endif |
|
2194 return; |
|
2195 } |
|
2196 if ((cur->nodeTab[i] != NULL) && |
|
2197 (cur->nodeTab[i]->type == XML_NAMESPACE_DECL)) |
|
2198 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]); |
|
2199 cur->nodeNr--; |
|
2200 for (;i < cur->nodeNr;i++) |
|
2201 cur->nodeTab[i] = cur->nodeTab[i + 1]; |
|
2202 cur->nodeTab[cur->nodeNr] = NULL; |
|
2203 } |
|
2204 |
|
2205 /** |
|
2206 * xmlXPathNodeSetRemove: |
|
2207 * @cur: the initial node set |
|
2208 * @val: the index to remove |
|
2209 * |
|
2210 * Removes an entry from an existing NodeSet list. |
|
2211 */ |
|
2212 void |
|
2213 xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) { |
|
2214 if (cur == NULL) return; |
|
2215 if (val >= cur->nodeNr) return; |
|
2216 if ((cur->nodeTab[val] != NULL) && |
|
2217 (cur->nodeTab[val]->type == XML_NAMESPACE_DECL)) |
|
2218 xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]); |
|
2219 cur->nodeNr--; |
|
2220 for (;val < cur->nodeNr;val++) |
|
2221 cur->nodeTab[val] = cur->nodeTab[val + 1]; |
|
2222 cur->nodeTab[cur->nodeNr] = NULL; |
|
2223 } |
|
2224 |
|
2225 /** |
|
2226 * xmlXPathFreeNodeSet: |
|
2227 * @obj: the xmlNodeSetPtr to free |
|
2228 * |
|
2229 * Free the NodeSet compound (not the actual nodes !). |
|
2230 */ |
|
2231 void |
|
2232 xmlXPathFreeNodeSet(xmlNodeSetPtr obj) { |
|
2233 if (obj == NULL) return; |
|
2234 if (obj->nodeTab != NULL) { |
|
2235 int i; |
|
2236 |
|
2237 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2238 for (i = 0;i < obj->nodeNr;i++) |
|
2239 if ((obj->nodeTab[i] != NULL) && |
|
2240 (obj->nodeTab[i]->type == XML_NAMESPACE_DECL)) |
|
2241 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); |
|
2242 xmlFree(obj->nodeTab); |
|
2243 } |
|
2244 xmlFree(obj); |
|
2245 } |
|
2246 |
|
2247 /** |
|
2248 * xmlXPathFreeValueTree: |
|
2249 * @obj: the xmlNodeSetPtr to free |
|
2250 * |
|
2251 * Free the NodeSet compound and the actual tree, this is different |
|
2252 * from xmlXPathFreeNodeSet() |
|
2253 */ |
|
2254 static void |
|
2255 xmlXPathFreeValueTree(xmlNodeSetPtr obj) { |
|
2256 int i; |
|
2257 |
|
2258 if (obj == NULL) return; |
|
2259 |
|
2260 if (obj->nodeTab != NULL) { |
|
2261 for (i = 0;i < obj->nodeNr;i++) { |
|
2262 if (obj->nodeTab[i] != NULL) { |
|
2263 if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
|
2264 xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]); |
|
2265 } else { |
|
2266 xmlFreeNodeList(obj->nodeTab[i]); |
|
2267 } |
|
2268 } |
|
2269 } |
|
2270 xmlFree(obj->nodeTab); |
|
2271 } |
|
2272 xmlFree(obj); |
|
2273 } |
|
2274 |
|
2275 #if defined(DEBUG) || defined(DEBUG_STEP) |
|
2276 /** |
|
2277 * xmlGenericErrorContextNodeSet: |
|
2278 * @output: a FILE * for the output |
|
2279 * @obj: the xmlNodeSetPtr to display |
|
2280 * |
|
2281 * Quick display of a NodeSet |
|
2282 */ |
|
2283 void |
|
2284 xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) { |
|
2285 int i; |
|
2286 |
|
2287 if (output == NULL) output = xmlGenericErrorContext; |
|
2288 if (obj == NULL) { |
|
2289 fprintf(output, "NodeSet == NULL !\n"); |
|
2290 return; |
|
2291 } |
|
2292 if (obj->nodeNr == 0) { |
|
2293 fprintf(output, "NodeSet is empty\n"); |
|
2294 return; |
|
2295 } |
|
2296 if (obj->nodeTab == NULL) { |
|
2297 fprintf(output, " nodeTab == NULL !\n"); |
|
2298 return; |
|
2299 } |
|
2300 for (i = 0; i < obj->nodeNr; i++) { |
|
2301 if (obj->nodeTab[i] == NULL) { |
|
2302 fprintf(output, " NULL !\n"); |
|
2303 return; |
|
2304 } |
|
2305 if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) || |
|
2306 (obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE)) |
|
2307 fprintf(output, " /"); |
|
2308 else if (obj->nodeTab[i]->name == NULL) |
|
2309 fprintf(output, " noname!"); |
|
2310 else fprintf(output, " %s", obj->nodeTab[i]->name); |
|
2311 } |
|
2312 fprintf(output, "\n"); |
|
2313 } |
|
2314 #endif |
|
2315 |
|
2316 /** |
|
2317 * xmlXPathNewNodeSet: |
|
2318 * @val: the NodePtr value |
|
2319 * |
|
2320 * Create a new xmlXPathObjectPtr of type NodeSet and initialize |
|
2321 * it with the single Node @val |
|
2322 * |
|
2323 * Returns the newly created object. |
|
2324 */ |
|
2325 xmlXPathObjectPtr |
|
2326 xmlXPathNewNodeSet(xmlNodePtr val) { |
|
2327 xmlXPathObjectPtr ret; |
|
2328 |
|
2329 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
2330 if (ret == NULL) { |
|
2331 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n")); |
|
2332 return(NULL); |
|
2333 } |
|
2334 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
2335 ret->type = XPATH_NODESET; |
|
2336 ret->boolval = 0; |
|
2337 ret->nodesetval = xmlXPathNodeSetCreate(val); |
|
2338 if(OOM_FLAG) |
|
2339 { |
|
2340 xmlXPathFreeObject(ret); |
|
2341 return(NULL); |
|
2342 } |
|
2343 /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
|
2344 return(ret); |
|
2345 } |
|
2346 |
|
2347 /** |
|
2348 * xmlXPathNewValueTree: |
|
2349 * @val: the NodePtr value |
|
2350 * |
|
2351 * Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize |
|
2352 * it with the tree root @val |
|
2353 * |
|
2354 * Returns the newly created object. |
|
2355 */ |
|
2356 xmlXPathObjectPtr |
|
2357 xmlXPathNewValueTree(xmlNodePtr val) { |
|
2358 xmlXPathObjectPtr ret; |
|
2359 |
|
2360 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
2361 if (ret == NULL) { |
|
2362 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating result value tree\n")); |
|
2363 return(NULL); |
|
2364 } |
|
2365 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
2366 ret->type = XPATH_XSLT_TREE; |
|
2367 ret->boolval = 1; |
|
2368 ret->user = (void *) val; |
|
2369 ret->nodesetval = xmlXPathNodeSetCreate(val); |
|
2370 return(ret); |
|
2371 } |
|
2372 |
|
2373 /** |
|
2374 * xmlXPathNewNodeSetList: |
|
2375 * @val: an existing NodeSet |
|
2376 * |
|
2377 * Create a new xmlXPathObjectPtr of type NodeSet and initialize |
|
2378 * it with the Nodeset @val |
|
2379 * |
|
2380 * Returns the newly created object. |
|
2381 */ |
|
2382 xmlXPathObjectPtr |
|
2383 xmlXPathNewNodeSetList(xmlNodeSetPtr val) |
|
2384 { |
|
2385 xmlXPathObjectPtr ret; |
|
2386 int i; |
|
2387 // TODO: CHECK OOM everywhere |
|
2388 if (val == NULL) |
|
2389 ret = NULL; |
|
2390 else if (val->nodeTab == NULL) |
|
2391 ret = xmlXPathNewNodeSet(NULL); |
|
2392 else { |
|
2393 ret = xmlXPathNewNodeSet(val->nodeTab[0]); |
|
2394 for (i = 1; i < val->nodeNr; ++i) |
|
2395 xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]); |
|
2396 } |
|
2397 |
|
2398 return (ret); |
|
2399 } |
|
2400 |
|
2401 /** |
|
2402 * xmlXPathWrapNodeSet: |
|
2403 * @val: the NodePtr value |
|
2404 * |
|
2405 * Wrap the Nodeset @val in a new xmlXPathObjectPtr |
|
2406 * |
|
2407 * Returns the newly created object. |
|
2408 */ |
|
2409 xmlXPathObjectPtr |
|
2410 xmlXPathWrapNodeSet(xmlNodeSetPtr val) { |
|
2411 xmlXPathObjectPtr ret; |
|
2412 |
|
2413 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
2414 if (ret == NULL) { |
|
2415 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating node set object\n")); |
|
2416 return(NULL); |
|
2417 } |
|
2418 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
2419 ret->type = XPATH_NODESET; |
|
2420 ret->nodesetval = val; |
|
2421 return(ret); |
|
2422 } |
|
2423 |
|
2424 /** |
|
2425 * xmlXPathFreeNodeSetList: |
|
2426 * @obj: an existing NodeSetList object |
|
2427 * |
|
2428 * Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in |
|
2429 * the list contrary to xmlXPathFreeObject(). |
|
2430 */ |
|
2431 void |
|
2432 xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) { |
|
2433 if (obj == NULL) return; |
|
2434 xmlFree(obj); |
|
2435 } |
|
2436 |
|
2437 /** |
|
2438 * xmlXPathDifference: |
|
2439 * @nodes1: a node-set |
|
2440 * @nodes2: a node-set |
|
2441 * |
|
2442 * Implements the EXSLT - Sets difference() function: |
|
2443 * node-set set:difference (node-set, node-set) |
|
2444 * |
|
2445 * Returns the difference between the two node sets, or nodes1 if |
|
2446 * nodes2 is empty |
|
2447 */ |
|
2448 xmlNodeSetPtr |
|
2449 xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2450 xmlNodeSetPtr ret; |
|
2451 int i, l1; |
|
2452 xmlNodePtr cur; |
|
2453 |
|
2454 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2455 return(nodes1); |
|
2456 |
|
2457 ret = xmlXPathNodeSetCreate(NULL); |
|
2458 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2459 return(ret); |
|
2460 |
|
2461 l1 = xmlXPathNodeSetGetLength(nodes1); |
|
2462 |
|
2463 for (i = 0; i < l1; i++) { |
|
2464 cur = xmlXPathNodeSetItem(nodes1, i); |
|
2465 if (!xmlXPathNodeSetContains(nodes2, cur)) |
|
2466 xmlXPathNodeSetAddUnique(ret, cur); |
|
2467 } |
|
2468 return(ret); |
|
2469 } |
|
2470 |
|
2471 /** |
|
2472 * xmlXPathIntersection: |
|
2473 * @nodes1: a node-set |
|
2474 * @nodes2: a node-set |
|
2475 * |
|
2476 * Implements the EXSLT - Sets intersection() function: |
|
2477 * node-set set:intersection (node-set, node-set) |
|
2478 * |
|
2479 * Returns a node set comprising the nodes that are within both the |
|
2480 * node sets passed as arguments |
|
2481 */ |
|
2482 xmlNodeSetPtr |
|
2483 xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2484 xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL); |
|
2485 int i, l1; |
|
2486 xmlNodePtr cur; |
|
2487 |
|
2488 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2489 return(ret); |
|
2490 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2491 return(ret); |
|
2492 |
|
2493 l1 = xmlXPathNodeSetGetLength(nodes1); |
|
2494 |
|
2495 for (i = 0; i < l1; i++) { |
|
2496 cur = xmlXPathNodeSetItem(nodes1, i); |
|
2497 if (xmlXPathNodeSetContains(nodes2, cur)) |
|
2498 xmlXPathNodeSetAddUnique(ret, cur); |
|
2499 } |
|
2500 return(ret); |
|
2501 } |
|
2502 |
|
2503 /** |
|
2504 * xmlXPathDistinctSorted: |
|
2505 * @nodes: a node-set, sorted by document order |
|
2506 * |
|
2507 * Implements the EXSLT - Sets distinct() function: |
|
2508 * node-set set:distinct (node-set) |
|
2509 * |
|
2510 * Returns a subset of the nodes contained in @nodes, or @nodes if |
|
2511 * it is empty |
|
2512 */ |
|
2513 xmlNodeSetPtr |
|
2514 xmlXPathDistinctSorted (xmlNodeSetPtr nodes) { |
|
2515 xmlNodeSetPtr ret; |
|
2516 xmlHashTablePtr hash; |
|
2517 int i, l; |
|
2518 xmlChar * strval; |
|
2519 xmlNodePtr cur; |
|
2520 |
|
2521 if (xmlXPathNodeSetIsEmpty(nodes)) |
|
2522 return(nodes); |
|
2523 |
|
2524 ret = xmlXPathNodeSetCreate(NULL); |
|
2525 l = xmlXPathNodeSetGetLength(nodes); |
|
2526 hash = xmlHashCreate (l); |
|
2527 for (i = 0; i < l; i++) { |
|
2528 cur = xmlXPathNodeSetItem(nodes, i); |
|
2529 strval = xmlXPathCastNodeToString(cur); |
|
2530 if (xmlHashLookup(hash, strval) == NULL) { |
|
2531 xmlHashAddEntry(hash, strval, strval); |
|
2532 xmlXPathNodeSetAddUnique(ret, cur); |
|
2533 } else { |
|
2534 xmlFree(strval); |
|
2535 } |
|
2536 } |
|
2537 xmlHashFree(hash, (xmlHashDeallocator) xmlFree); |
|
2538 return(ret); |
|
2539 } |
|
2540 |
|
2541 /** |
|
2542 * xmlXPathDistinct: |
|
2543 * @nodes: a node-set |
|
2544 * |
|
2545 * Implements the EXSLT - Sets distinct() function: |
|
2546 * node-set set:distinct (node-set) |
|
2547 * @nodes is sorted by document order, then #exslSetsDistinctSorted |
|
2548 * is called with the sorted node-set |
|
2549 * |
|
2550 * Returns a subset of the nodes contained in @nodes, or @nodes if |
|
2551 * it is empty |
|
2552 */ |
|
2553 xmlNodeSetPtr |
|
2554 xmlXPathDistinct (xmlNodeSetPtr nodes) { |
|
2555 if (xmlXPathNodeSetIsEmpty(nodes)) |
|
2556 return(nodes); |
|
2557 |
|
2558 xmlXPathNodeSetSort(nodes); |
|
2559 return(xmlXPathDistinctSorted(nodes)); |
|
2560 } |
|
2561 |
|
2562 /** |
|
2563 * xmlXPathHasSameNodes: |
|
2564 * @nodes1: a node-set |
|
2565 * @nodes2: a node-set |
|
2566 * |
|
2567 * Implements the EXSLT - Sets has-same-nodes function: |
|
2568 * boolean set:has-same-node(node-set, node-set) |
|
2569 * |
|
2570 * Returns true (1) if @nodes1 shares any node with @nodes2, false (0) |
|
2571 * otherwise |
|
2572 */ |
|
2573 int |
|
2574 xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2575 int i, l; |
|
2576 xmlNodePtr cur; |
|
2577 |
|
2578 if (xmlXPathNodeSetIsEmpty(nodes1) || |
|
2579 xmlXPathNodeSetIsEmpty(nodes2)) |
|
2580 return(0); |
|
2581 |
|
2582 l = xmlXPathNodeSetGetLength(nodes1); |
|
2583 for (i = 0; i < l; i++) { |
|
2584 cur = xmlXPathNodeSetItem(nodes1, i); |
|
2585 if (xmlXPathNodeSetContains(nodes2, cur)) |
|
2586 return(1); |
|
2587 } |
|
2588 return(0); |
|
2589 } |
|
2590 |
|
2591 /** |
|
2592 * xmlXPathNodeLeadingSorted: |
|
2593 * @nodes: a node-set, sorted by document order |
|
2594 * @node: a node |
|
2595 * |
|
2596 * Implements the EXSLT - Sets leading() function: |
|
2597 * node-set set:leading (node-set, node-set) |
|
2598 * |
|
2599 * Returns the nodes in @nodes that precede @node in document order, |
|
2600 * @nodes if @node is NULL or an empty node-set if @nodes |
|
2601 * doesn't contain @node |
|
2602 */ |
|
2603 xmlNodeSetPtr |
|
2604 xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2605 int i, l; |
|
2606 xmlNodePtr cur; |
|
2607 xmlNodeSetPtr ret; |
|
2608 |
|
2609 if (node == NULL) |
|
2610 return(nodes); |
|
2611 |
|
2612 ret = xmlXPathNodeSetCreate(NULL); |
|
2613 if (xmlXPathNodeSetIsEmpty(nodes) || |
|
2614 (!xmlXPathNodeSetContains(nodes, node))) |
|
2615 return(ret); |
|
2616 |
|
2617 l = xmlXPathNodeSetGetLength(nodes); |
|
2618 for (i = 0; i < l; i++) { |
|
2619 cur = xmlXPathNodeSetItem(nodes, i); |
|
2620 if (cur == node) |
|
2621 break; |
|
2622 xmlXPathNodeSetAddUnique(ret, cur); |
|
2623 } |
|
2624 return(ret); |
|
2625 } |
|
2626 |
|
2627 /** |
|
2628 * xmlXPathNodeLeading: |
|
2629 * @nodes: a node-set |
|
2630 * @node: a node |
|
2631 * |
|
2632 * Implements the EXSLT - Sets leading() function: |
|
2633 * node-set set:leading (node-set, node-set) |
|
2634 * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted |
|
2635 * is called. |
|
2636 * |
|
2637 * Returns the nodes in @nodes that precede @node in document order, |
|
2638 * @nodes if @node is NULL or an empty node-set if @nodes |
|
2639 * doesn't contain @node |
|
2640 */ |
|
2641 xmlNodeSetPtr |
|
2642 xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2643 xmlXPathNodeSetSort(nodes); |
|
2644 return(xmlXPathNodeLeadingSorted(nodes, node)); |
|
2645 } |
|
2646 |
|
2647 /** |
|
2648 * xmlXPathLeadingSorted: |
|
2649 * @nodes1: a node-set, sorted by document order |
|
2650 * @nodes2: a node-set, sorted by document order |
|
2651 * |
|
2652 * Implements the EXSLT - Sets leading() function: |
|
2653 * node-set set:leading (node-set, node-set) |
|
2654 * |
|
2655 * Returns the nodes in @nodes1 that precede the first node in @nodes2 |
|
2656 * in document order, @nodes1 if @nodes2 is NULL or empty or |
|
2657 * an empty node-set if @nodes1 doesn't contain @nodes2 |
|
2658 */ |
|
2659 xmlNodeSetPtr |
|
2660 xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2661 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2662 return(nodes1); |
|
2663 return(xmlXPathNodeLeadingSorted(nodes1, |
|
2664 xmlXPathNodeSetItem(nodes2, 1))); |
|
2665 } |
|
2666 |
|
2667 /** |
|
2668 * xmlXPathLeading: |
|
2669 * @nodes1: a node-set |
|
2670 * @nodes2: a node-set |
|
2671 * |
|
2672 * Implements the EXSLT - Sets leading() function: |
|
2673 * node-set set:leading (node-set, node-set) |
|
2674 * @nodes1 and @nodes2 are sorted by document order, then |
|
2675 * #exslSetsLeadingSorted is called. |
|
2676 * |
|
2677 * Returns the nodes in @nodes1 that precede the first node in @nodes2 |
|
2678 * in document order, @nodes1 if @nodes2 is NULL or empty or |
|
2679 * an empty node-set if @nodes1 doesn't contain @nodes2 |
|
2680 */ |
|
2681 xmlNodeSetPtr |
|
2682 xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2683 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2684 return(nodes1); |
|
2685 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2686 return(xmlXPathNodeSetCreate(NULL)); |
|
2687 xmlXPathNodeSetSort(nodes1); |
|
2688 xmlXPathNodeSetSort(nodes2); |
|
2689 return(xmlXPathNodeLeadingSorted(nodes1, |
|
2690 xmlXPathNodeSetItem(nodes2, 1))); |
|
2691 } |
|
2692 |
|
2693 /** |
|
2694 * xmlXPathNodeTrailingSorted: |
|
2695 * @nodes: a node-set, sorted by document order |
|
2696 * @node: a node |
|
2697 * |
|
2698 * Implements the EXSLT - Sets trailing() function: |
|
2699 * node-set set:trailing (node-set, node-set) |
|
2700 * |
|
2701 * Returns the nodes in @nodes that follow @node in document order, |
|
2702 * @nodes if @node is NULL or an empty node-set if @nodes |
|
2703 * doesn't contain @node |
|
2704 */ |
|
2705 xmlNodeSetPtr |
|
2706 xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2707 int i, l; |
|
2708 xmlNodePtr cur; |
|
2709 xmlNodeSetPtr ret; |
|
2710 |
|
2711 if (node == NULL) |
|
2712 return(nodes); |
|
2713 |
|
2714 ret = xmlXPathNodeSetCreate(NULL); |
|
2715 if (xmlXPathNodeSetIsEmpty(nodes) || |
|
2716 (!xmlXPathNodeSetContains(nodes, node))) |
|
2717 return(ret); |
|
2718 |
|
2719 l = xmlXPathNodeSetGetLength(nodes); |
|
2720 for (i = l; i > 0; i--) { |
|
2721 cur = xmlXPathNodeSetItem(nodes, i); |
|
2722 if (cur == node) |
|
2723 break; |
|
2724 xmlXPathNodeSetAddUnique(ret, cur); |
|
2725 } |
|
2726 return(ret); |
|
2727 } |
|
2728 |
|
2729 /** |
|
2730 * xmlXPathNodeTrailing: |
|
2731 * @nodes: a node-set |
|
2732 * @node: a node |
|
2733 * |
|
2734 * Implements the EXSLT - Sets trailing() function: |
|
2735 * node-set set:trailing (node-set, node-set) |
|
2736 * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted |
|
2737 * is called. |
|
2738 * |
|
2739 * Returns the nodes in @nodes that follow @node in document order, |
|
2740 * @nodes if @node is NULL or an empty node-set if @nodes |
|
2741 * doesn't contain @node |
|
2742 */ |
|
2743 xmlNodeSetPtr |
|
2744 xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) { |
|
2745 xmlXPathNodeSetSort(nodes); |
|
2746 return(xmlXPathNodeTrailingSorted(nodes, node)); |
|
2747 } |
|
2748 |
|
2749 /** |
|
2750 * xmlXPathTrailingSorted: |
|
2751 * @nodes1: a node-set, sorted by document order |
|
2752 * @nodes2: a node-set, sorted by document order |
|
2753 * |
|
2754 * Implements the EXSLT - Sets trailing() function: |
|
2755 * node-set set:trailing (node-set, node-set) |
|
2756 * |
|
2757 * Returns the nodes in @nodes1 that follow the first node in @nodes2 |
|
2758 * in document order, @nodes1 if @nodes2 is NULL or empty or |
|
2759 * an empty node-set if @nodes1 doesn't contain @nodes2 |
|
2760 */ |
|
2761 xmlNodeSetPtr |
|
2762 xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2763 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2764 return(nodes1); |
|
2765 return(xmlXPathNodeTrailingSorted(nodes1, |
|
2766 xmlXPathNodeSetItem(nodes2, 0))); |
|
2767 } |
|
2768 |
|
2769 /** |
|
2770 * xmlXPathTrailing: |
|
2771 * @nodes1: a node-set |
|
2772 * @nodes2: a node-set |
|
2773 * |
|
2774 * Implements the EXSLT - Sets trailing() function: |
|
2775 * node-set set:trailing (node-set, node-set) |
|
2776 * @nodes1 and @nodes2 are sorted by document order, then |
|
2777 * #xmlXPathTrailingSorted is called. |
|
2778 * |
|
2779 * Returns the nodes in @nodes1 that follow the first node in @nodes2 |
|
2780 * in document order, @nodes1 if @nodes2 is NULL or empty or |
|
2781 * an empty node-set if @nodes1 doesn't contain @nodes2 |
|
2782 */ |
|
2783 xmlNodeSetPtr |
|
2784 xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
|
2785 if (xmlXPathNodeSetIsEmpty(nodes2)) |
|
2786 return(nodes1); |
|
2787 if (xmlXPathNodeSetIsEmpty(nodes1)) |
|
2788 return(xmlXPathNodeSetCreate(NULL)); |
|
2789 xmlXPathNodeSetSort(nodes1); |
|
2790 xmlXPathNodeSetSort(nodes2); |
|
2791 return(xmlXPathNodeTrailingSorted(nodes1, |
|
2792 xmlXPathNodeSetItem(nodes2, 0))); |
|
2793 } |
|
2794 |
|
2795 /************************************************************************ |
|
2796 * * |
|
2797 * Routines to handle extra functions * |
|
2798 * * |
|
2799 ************************************************************************/ |
|
2800 |
|
2801 /** |
|
2802 * xmlXPathRegisterFunc: |
|
2803 * @ctxt: the XPath context |
|
2804 * @name: the function name |
|
2805 * @f: the function implementation or NULL |
|
2806 * |
|
2807 * Register a new function. If @f is NULL it unregisters the function |
|
2808 * |
|
2809 * Returns 0 in case of success, -1 in case of error |
|
2810 */ |
|
2811 int |
|
2812 xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
2813 xmlXPathFunction f) { |
|
2814 return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f)); |
|
2815 } |
|
2816 |
|
2817 /** |
|
2818 * xmlXPathRegisterFuncNS: |
|
2819 * @ctxt: the XPath context |
|
2820 * @name: the function name |
|
2821 * @ns_uri: the function namespace URI |
|
2822 * @f: the function implementation or NULL |
|
2823 * |
|
2824 * Register a new function. If @f is NULL it unregisters the function |
|
2825 * |
|
2826 * Returns 0 in case of success, -1 in case of error |
|
2827 */ |
|
2828 int |
|
2829 xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
2830 const xmlChar *ns_uri, xmlXPathFunction f) { |
|
2831 if (ctxt == NULL || name == NULL) |
|
2832 return(-1); |
|
2833 |
|
2834 if (ctxt->funcHash == NULL) |
|
2835 ctxt->funcHash = xmlHashCreate(0); |
|
2836 if (ctxt->funcHash == NULL) |
|
2837 return(-1); |
|
2838 |
|
2839 if (f == NULL) |
|
2840 return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL)); |
|
2841 if( xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *)f) < 0) |
|
2842 { |
|
2843 xmlHashFree(ctxt->funcHash, NULL); |
|
2844 return(-1); |
|
2845 } |
|
2846 return(0); |
|
2847 } |
|
2848 |
|
2849 /** |
|
2850 * xmlXPathRegisterFuncLookup: |
|
2851 * @ctxt: the XPath context |
|
2852 * @f: the lookup function |
|
2853 * @funcCtxt: the lookup data |
|
2854 * |
|
2855 * Registers an external mechanism to do function lookup. |
|
2856 */ |
|
2857 void |
|
2858 xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt, |
|
2859 xmlXPathFuncLookupFunc f, |
|
2860 void *funcCtxt) { |
|
2861 if (ctxt == NULL) |
|
2862 return; |
|
2863 ctxt->funcLookupFunc = (void *) f; |
|
2864 ctxt->funcLookupData = funcCtxt; |
|
2865 } |
|
2866 |
|
2867 /** |
|
2868 * xmlXPathFunctionLookup: |
|
2869 * @ctxt: the XPath context |
|
2870 * @name: the function name |
|
2871 * |
|
2872 * Search in the Function array of the context for the given |
|
2873 * function. |
|
2874 * |
|
2875 * Returns the xmlXPathFunction or NULL if not found |
|
2876 */ |
|
2877 xmlXPathFunction |
|
2878 xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { |
|
2879 if (ctxt == NULL) |
|
2880 return (NULL); |
|
2881 |
|
2882 if (ctxt->funcLookupFunc != NULL) { |
|
2883 xmlXPathFunction ret; |
|
2884 xmlXPathFuncLookupFunc f; |
|
2885 |
|
2886 f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc; |
|
2887 ret = f(ctxt->funcLookupData, name, NULL); |
|
2888 if (ret != NULL) |
|
2889 return(ret); |
|
2890 } |
|
2891 return(xmlXPathFunctionLookupNS(ctxt, name, NULL)); |
|
2892 } |
|
2893 |
|
2894 /** |
|
2895 * xmlXPathFunctionLookupNS: |
|
2896 * @ctxt: the XPath context |
|
2897 * @name: the function name |
|
2898 * @ns_uri: the function namespace URI |
|
2899 * |
|
2900 * Search in the Function array of the context for the given |
|
2901 * function. |
|
2902 * |
|
2903 * Returns the xmlXPathFunction or NULL if not found |
|
2904 */ |
|
2905 xmlXPathFunction |
|
2906 xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, const xmlChar *ns_uri) { |
|
2907 if (ctxt == NULL) |
|
2908 return(NULL); |
|
2909 if (name == NULL) |
|
2910 return(NULL); |
|
2911 |
|
2912 if (ctxt->funcLookupFunc != NULL) { |
|
2913 xmlXPathFunction ret; |
|
2914 xmlXPathFuncLookupFunc f; |
|
2915 |
|
2916 f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc; |
|
2917 ret = f(ctxt->funcLookupData, name, ns_uri); |
|
2918 if (ret != NULL) |
|
2919 return(ret); |
|
2920 } |
|
2921 |
|
2922 if (ctxt->funcHash == NULL) |
|
2923 return(NULL); |
|
2924 |
|
2925 return (xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri); |
|
2926 } |
|
2927 |
|
2928 /** |
|
2929 * xmlXPathRegisteredFuncsCleanup: |
|
2930 * @ctxt: the XPath context |
|
2931 * |
|
2932 * Cleanup the XPath context data associated to registered functions |
|
2933 */ |
|
2934 void |
|
2935 xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) { |
|
2936 if (ctxt == NULL) |
|
2937 return; |
|
2938 |
|
2939 xmlHashFree(ctxt->funcHash, NULL); |
|
2940 ctxt->funcHash = NULL; |
|
2941 } |
|
2942 |
|
2943 /************************************************************************ |
|
2944 * * |
|
2945 * Routines to handle Variables * |
|
2946 * * |
|
2947 ************************************************************************/ |
|
2948 |
|
2949 /** |
|
2950 * xmlXPathRegisterVariable: |
|
2951 * @ctxt: the XPath context |
|
2952 * @name: the variable name |
|
2953 * @value: the variable value or NULL |
|
2954 * |
|
2955 * Register a new variable value. If @value is NULL it unregisters |
|
2956 * the variable |
|
2957 * |
|
2958 * Returns 0 in case of success, -1 in case of error |
|
2959 */ |
|
2960 int |
|
2961 xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
2962 xmlXPathObjectPtr value) { |
|
2963 return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value)); |
|
2964 } |
|
2965 |
|
2966 /** |
|
2967 * xmlXPathRegisterVariableNS: |
|
2968 * @ctxt: the XPath context |
|
2969 * @name: the variable name |
|
2970 * @ns_uri: the variable namespace URI |
|
2971 * @value: the variable value or NULL |
|
2972 * |
|
2973 * Register a new variable value. If @value is NULL it unregisters |
|
2974 * the variable |
|
2975 * |
|
2976 * Returns 0 in case of success, -1 in case of error |
|
2977 */ |
|
2978 int |
|
2979 xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
2980 const xmlChar *ns_uri, |
|
2981 xmlXPathObjectPtr value) { |
|
2982 if (ctxt == NULL) |
|
2983 return(-1); |
|
2984 if (name == NULL) |
|
2985 return(-1); |
|
2986 |
|
2987 if (ctxt->varHash == NULL) |
|
2988 ctxt->varHash = xmlHashCreate(0); |
|
2989 if (ctxt->varHash == NULL) |
|
2990 return(-1); |
|
2991 if (value == NULL) |
|
2992 return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri, |
|
2993 (xmlHashDeallocator)xmlXPathFreeObject)); |
|
2994 return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri, |
|
2995 (void *) value, |
|
2996 (xmlHashDeallocator)xmlXPathFreeObject)); |
|
2997 } |
|
2998 |
|
2999 /** |
|
3000 * xmlXPathRegisterVariableLookup: |
|
3001 * @ctxt: the XPath context |
|
3002 * @f: the lookup function |
|
3003 * @data: the lookup data |
|
3004 * |
|
3005 * register an external mechanism to do variable lookup |
|
3006 */ |
|
3007 void |
|
3008 xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt, |
|
3009 xmlXPathVariableLookupFunc f, void *data) { |
|
3010 if (ctxt == NULL) |
|
3011 return; |
|
3012 ctxt->varLookupFunc = (void *) f; |
|
3013 ctxt->varLookupData = data; |
|
3014 } |
|
3015 |
|
3016 /** |
|
3017 * xmlXPathVariableLookup: |
|
3018 * @ctxt: the XPath context |
|
3019 * @name: the variable name |
|
3020 * |
|
3021 * Search in the Variable array of the context for the given |
|
3022 * variable value. |
|
3023 * |
|
3024 * Returns a copy of the value or NULL if not found |
|
3025 */ |
|
3026 xmlXPathObjectPtr |
|
3027 xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) { |
|
3028 if (ctxt == NULL) |
|
3029 return(NULL); |
|
3030 |
|
3031 if (ctxt->varLookupFunc != NULL) { |
|
3032 xmlXPathVariableLookupFunc func = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc; |
|
3033 xmlXPathObjectPtr ret = func(ctxt->varLookupData, name, NULL); |
|
3034 return(ret); |
|
3035 } |
|
3036 return(xmlXPathVariableLookupNS(ctxt, name, NULL)); |
|
3037 } |
|
3038 |
|
3039 /** |
|
3040 * xmlXPathVariableLookupNS: |
|
3041 * @ctxt: the XPath context |
|
3042 * @name: the variable name |
|
3043 * @ns_uri: the variable namespace URI |
|
3044 * |
|
3045 * Search in the Variable array of the context for the given |
|
3046 * variable value. |
|
3047 * |
|
3048 * Returns the a copy of the value or NULL if not found |
|
3049 */ |
|
3050 xmlXPathObjectPtr |
|
3051 xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name, |
|
3052 const xmlChar *ns_uri) { |
|
3053 if (ctxt == NULL) |
|
3054 return(NULL); |
|
3055 |
|
3056 if (ctxt->varLookupFunc != NULL) { |
|
3057 xmlXPathVariableLookupFunc lookupFunc = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc; |
|
3058 xmlXPathObjectPtr ret = lookupFunc(ctxt->varLookupData, name, ns_uri); |
|
3059 if (ret != NULL) |
|
3060 return(ret); |
|
3061 } |
|
3062 |
|
3063 if (ctxt->varHash == NULL) |
|
3064 return(NULL); |
|
3065 if (name == NULL) |
|
3066 return(NULL); |
|
3067 |
|
3068 return(xmlXPathObjectCopy((xmlXPathObjectPtr) |
|
3069 xmlHashLookup2(ctxt->varHash, name, ns_uri))); |
|
3070 } |
|
3071 |
|
3072 /** |
|
3073 * xmlXPathRegisteredVariablesCleanup: |
|
3074 * @ctxt: the XPath context |
|
3075 * |
|
3076 * Cleanup the XPath context data associated to registered variables |
|
3077 */ |
|
3078 void |
|
3079 xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) { |
|
3080 if (ctxt == NULL) |
|
3081 return; |
|
3082 |
|
3083 xmlHashFree(ctxt->varHash, (xmlHashDeallocator)xmlXPathFreeObject); |
|
3084 ctxt->varHash = NULL; |
|
3085 } |
|
3086 |
|
3087 /** |
|
3088 * xmlXPathRegisterNs: |
|
3089 * @ctxt: the XPath context |
|
3090 * @prefix: the namespace prefix |
|
3091 * @ns_uri: the namespace name |
|
3092 * |
|
3093 * Register a new namespace. If @ns_uri is NULL it unregisters |
|
3094 * the namespace |
|
3095 * |
|
3096 * Returns 0 in case of success, -1 in case of error |
|
3097 */ |
|
3098 int |
|
3099 xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix, |
|
3100 const xmlChar *ns_uri) { |
|
3101 int res = -1; |
|
3102 xmlChar* uri = NULL; |
|
3103 |
|
3104 if (ctxt == NULL) |
|
3105 return(-1); |
|
3106 if (prefix == NULL) |
|
3107 return(-1); |
|
3108 |
|
3109 if (ctxt->nsHash == NULL) |
|
3110 ctxt->nsHash = xmlHashCreate(10); |
|
3111 if (ctxt->nsHash == NULL) |
|
3112 return(-1); |
|
3113 if (!ns_uri) |
|
3114 return(xmlHashRemoveEntry(ctxt->nsHash, prefix,(xmlHashDeallocator)xmlFree)); |
|
3115 uri = xmlStrdup(ns_uri); |
|
3116 if (uri) |
|
3117 { |
|
3118 res = xmlHashUpdateEntry(ctxt->nsHash, prefix, (void*)uri, |
|
3119 (xmlHashDeallocator)xmlFree); |
|
3120 if ( res == -1) |
|
3121 xmlFree( uri ); |
|
3122 } |
|
3123 return( res ); |
|
3124 } |
|
3125 |
|
3126 /** |
|
3127 * xmlXPathNsLookup: |
|
3128 * @ctxt: the XPath context |
|
3129 * @prefix: the namespace prefix value |
|
3130 * |
|
3131 * Search in the namespace declaration array of the context for the given |
|
3132 * namespace name associated to the given prefix |
|
3133 * |
|
3134 * Returns the value or NULL if not found |
|
3135 */ |
|
3136 const xmlChar * |
|
3137 xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) { |
|
3138 if (ctxt == NULL) |
|
3139 return(NULL); |
|
3140 if (prefix == NULL) |
|
3141 return(NULL); |
|
3142 |
|
3143 #ifdef XML_XML_NAMESPACE |
|
3144 if (xmlStrEqual(prefix, (const xmlChar *) "xml")) |
|
3145 return(XML_XML_NAMESPACE); |
|
3146 #endif |
|
3147 |
|
3148 if (ctxt->namespaces != NULL) { |
|
3149 int i; |
|
3150 |
|
3151 for (i = 0;i < ctxt->nsNr;i++) { |
|
3152 if ((ctxt->namespaces[i] != NULL) && |
|
3153 (xmlStrEqual(ctxt->namespaces[i]->prefix, prefix))) |
|
3154 return(ctxt->namespaces[i]->href); |
|
3155 } |
|
3156 } |
|
3157 |
|
3158 return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix)); |
|
3159 } |
|
3160 |
|
3161 /** |
|
3162 * xmlXPathRegisteredNsCleanup: |
|
3163 * @ctxt: the XPath context |
|
3164 * |
|
3165 * Cleanup the XPath context data associated to registered variables |
|
3166 */ |
|
3167 void |
|
3168 xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) { |
|
3169 if (ctxt == NULL) |
|
3170 return; |
|
3171 |
|
3172 xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree); |
|
3173 ctxt->nsHash = NULL; |
|
3174 } |
|
3175 |
|
3176 /************************************************************************ |
|
3177 * * |
|
3178 * Routines to handle Values * |
|
3179 * * |
|
3180 ************************************************************************/ |
|
3181 |
|
3182 /* Allocations are terrible, one needs to optimize all this !!! */ |
|
3183 |
|
3184 /** |
|
3185 * xmlXPathNewFloat: |
|
3186 * @val: the double value |
|
3187 * |
|
3188 * Create a new xmlXPathObjectPtr of type double and of value @val |
|
3189 * |
|
3190 * Returns the newly created object. |
|
3191 */ |
|
3192 xmlXPathObjectPtr |
|
3193 xmlXPathNewFloat(double val) { |
|
3194 xmlXPathObjectPtr ret; |
|
3195 |
|
3196 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3197 if (ret == NULL) { |
|
3198 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating float object\n")); |
|
3199 return(NULL); |
|
3200 } |
|
3201 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3202 ret->type = XPATH_NUMBER; |
|
3203 ret->floatval = val; |
|
3204 return(ret); |
|
3205 } |
|
3206 |
|
3207 /** |
|
3208 * xmlXPathNewBoolean: |
|
3209 * @val: the boolean value |
|
3210 * |
|
3211 * Create a new xmlXPathObjectPtr of type boolean and of value @val |
|
3212 * |
|
3213 * Returns the newly created object. |
|
3214 */ |
|
3215 xmlXPathObjectPtr |
|
3216 xmlXPathNewBoolean(int val) { |
|
3217 xmlXPathObjectPtr ret; |
|
3218 |
|
3219 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3220 if (ret == NULL) { |
|
3221 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating boolean object\n")); |
|
3222 return(NULL); |
|
3223 } |
|
3224 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3225 ret->type = XPATH_BOOLEAN; |
|
3226 ret->boolval = (val != 0); |
|
3227 return(ret); |
|
3228 } |
|
3229 |
|
3230 /** |
|
3231 * xmlXPathNewString: |
|
3232 * @val: the xmlChar * value |
|
3233 * |
|
3234 * Create a new xmlXPathObjectPtr of type string and of value @val |
|
3235 * |
|
3236 * Returns the newly created object or NULL if OOM |
|
3237 * |
|
3238 * OOM: possible --> sets OOM flag |
|
3239 */ |
|
3240 xmlXPathObjectPtr |
|
3241 xmlXPathNewString(const xmlChar *val) { |
|
3242 xmlXPathObjectPtr ret; |
|
3243 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3244 if (ret == NULL) { |
|
3245 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n")); |
|
3246 return(NULL); |
|
3247 } |
|
3248 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3249 ret->type = XPATH_STRING; |
|
3250 ret->stringval = xmlStrdup(val ? val : (const xmlChar *)""); |
|
3251 if( OOM_FLAG ) |
|
3252 { |
|
3253 xmlXPathFreeObject(ret); |
|
3254 return(NULL); |
|
3255 } |
|
3256 return(ret); |
|
3257 } |
|
3258 |
|
3259 /** |
|
3260 * xmlXPathWrapString: |
|
3261 * @val: the xmlChar * value |
|
3262 * |
|
3263 * Wraps the @val string into an XPath object. |
|
3264 * |
|
3265 * Returns the newly created object. |
|
3266 */ |
|
3267 xmlXPathObjectPtr |
|
3268 xmlXPathWrapString (xmlChar *val) { |
|
3269 xmlXPathObjectPtr ret; |
|
3270 |
|
3271 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3272 if (ret == NULL) { |
|
3273 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n")); |
|
3274 return(NULL); |
|
3275 } |
|
3276 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3277 ret->type = XPATH_STRING; |
|
3278 ret->stringval = val; |
|
3279 return(ret); |
|
3280 } |
|
3281 |
|
3282 /** |
|
3283 * xmlXPathNewCString: |
|
3284 * @val: the char * value |
|
3285 * |
|
3286 * Create a new xmlXPathObjectPtr of type string and of value @val |
|
3287 * |
|
3288 * Returns the newly created object. |
|
3289 */ |
|
3290 xmlXPathObjectPtr |
|
3291 xmlXPathNewCString(const char *val) { |
|
3292 xmlXPathObjectPtr ret; |
|
3293 |
|
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(BAD_CAST val); |
|
3302 if(OOM_FLAG) |
|
3303 { |
|
3304 xmlXPathFreeObject(ret); |
|
3305 return(NULL); |
|
3306 } |
|
3307 return(ret); |
|
3308 } |
|
3309 |
|
3310 /** |
|
3311 * xmlXPathWrapCString: |
|
3312 * @val: the char * value |
|
3313 * |
|
3314 * Wraps a string into an XPath object. |
|
3315 * |
|
3316 * Returns the newly created object. |
|
3317 */ |
|
3318 xmlXPathObjectPtr |
|
3319 xmlXPathWrapCString (char * val) { |
|
3320 return(xmlXPathWrapString((xmlChar *)(val))); |
|
3321 } |
|
3322 |
|
3323 /** |
|
3324 * xmlXPathWrapExternal: |
|
3325 * @val: the user data |
|
3326 * |
|
3327 * Wraps the @val data into an XPath object. |
|
3328 * |
|
3329 * Returns the newly created object. |
|
3330 */ |
|
3331 xmlXPathObjectPtr |
|
3332 xmlXPathWrapExternal (void *val) { |
|
3333 xmlXPathObjectPtr ret; |
|
3334 |
|
3335 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3336 if (ret == NULL) { |
|
3337 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating user object\n")); |
|
3338 return(NULL); |
|
3339 } |
|
3340 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
3341 ret->type = XPATH_USERS; |
|
3342 ret->user = val; |
|
3343 return(ret); |
|
3344 } |
|
3345 |
|
3346 /** |
|
3347 * xmlXPathObjectCopy: |
|
3348 * @val: the original object |
|
3349 * |
|
3350 * allocate a new copy of a given object |
|
3351 * |
|
3352 * Returns the newly created object or NULL if OOM |
|
3353 */ |
|
3354 xmlXPathObjectPtr |
|
3355 xmlXPathObjectCopy(xmlXPathObjectPtr val) { |
|
3356 xmlXPathObjectPtr ret; |
|
3357 |
|
3358 if (val == NULL) |
|
3359 return(NULL); |
|
3360 |
|
3361 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
3362 if (ret == NULL) { |
|
3363 xmlXPathErrMemory(NULL, EMBED_ERRTXT("copying object\n")); |
|
3364 return(NULL); |
|
3365 } |
|
3366 memcpy(ret, val , (size_t) sizeof(xmlXPathObject)); |
|
3367 switch (val->type) { |
|
3368 case XPATH_BOOLEAN: |
|
3369 case XPATH_NUMBER: |
|
3370 case XPATH_POINT: |
|
3371 case XPATH_RANGE: |
|
3372 break; |
|
3373 case XPATH_STRING: |
|
3374 ret->stringval = xmlStrdup(val->stringval); |
|
3375 if( OOM_FLAG ) |
|
3376 { |
|
3377 xmlXPathFreeObject(ret); |
|
3378 return(NULL); |
|
3379 } |
|
3380 break; |
|
3381 case XPATH_XSLT_TREE: |
|
3382 if ((val->nodesetval != NULL) && |
|
3383 (val->nodesetval->nodeTab != NULL)) { |
|
3384 xmlNodePtr cur, tmp; |
|
3385 xmlDocPtr top; |
|
3386 |
|
3387 ret->boolval = 1; |
|
3388 top = xmlNewDoc(NULL); |
|
3389 top->name = (char *) |
|
3390 xmlStrdup(val->nodesetval->nodeTab[0]->name); |
|
3391 ret->user = top; |
|
3392 if (top != NULL) { |
|
3393 top->doc = top; |
|
3394 cur = val->nodesetval->nodeTab[0]->children; |
|
3395 while (cur != NULL) { |
|
3396 tmp = xmlDocCopyNode(cur, top, 1); |
|
3397 xmlAddChild((xmlNodePtr) top, tmp); |
|
3398 cur = cur->next; |
|
3399 } |
|
3400 } |
|
3401 ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top); |
|
3402 } else |
|
3403 ret->nodesetval = xmlXPathNodeSetCreate(NULL); |
|
3404 /* Deallocate the copied tree value */ |
|
3405 break; |
|
3406 case XPATH_NODESET: |
|
3407 ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval); |
|
3408 /* Do not deallocate the copied tree value */ |
|
3409 ret->boolval = 0; |
|
3410 break; |
|
3411 case XPATH_LOCATIONSET: |
|
3412 #ifdef LIBXML_XPTR_ENABLED |
|
3413 { |
|
3414 xmlLocationSetPtr loc = val->user; |
|
3415 ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc); |
|
3416 break; |
|
3417 } |
|
3418 #endif |
|
3419 case XPATH_USERS: |
|
3420 ret->user = val->user; |
|
3421 break; |
|
3422 case XPATH_UNDEFINED: |
|
3423 xmlGenericError(xmlGenericErrorContext, |
|
3424 EMBED_ERRTXT("xmlXPathObjectCopy: unsupported type %d\n"), |
|
3425 val->type); |
|
3426 break; |
|
3427 } |
|
3428 return(ret); |
|
3429 } |
|
3430 |
|
3431 /** |
|
3432 * xmlXPathFreeObject: |
|
3433 * @obj: the object to free |
|
3434 * |
|
3435 * Free up an xmlXPathObjectPtr object. |
|
3436 */ |
|
3437 // TODO: OPTIMIZE: Some reduction of function calls is possible... |
|
3438 // Consider using own version of this function |
|
3439 void |
|
3440 xmlXPathFreeObject(xmlXPathObjectPtr obj) { |
|
3441 if (obj == NULL) return; |
|
3442 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) { |
|
3443 if (obj->boolval) { |
|
3444 if (obj->user != NULL) { |
|
3445 xmlXPathFreeNodeSet(obj->nodesetval); |
|
3446 xmlFreeNodeList((xmlNodePtr) obj->user); |
|
3447 } else |
|
3448 if (obj->nodesetval != NULL) |
|
3449 xmlXPathFreeValueTree(obj->nodesetval); |
|
3450 } else { |
|
3451 if (obj->nodesetval != NULL) |
|
3452 xmlXPathFreeNodeSet(obj->nodesetval); |
|
3453 } |
|
3454 #ifdef LIBXML_XPTR_ENABLED |
|
3455 } else if (obj->type == XPATH_LOCATIONSET) { |
|
3456 if (obj->user != NULL) |
|
3457 xmlXPtrFreeLocationSet(obj->user); |
|
3458 #endif |
|
3459 } else if (obj->type == XPATH_STRING) { |
|
3460 if (obj->stringval != NULL) |
|
3461 xmlFree(obj->stringval); |
|
3462 } |
|
3463 |
|
3464 xmlFree(obj); |
|
3465 } |
|
3466 |
|
3467 |
|
3468 /************************************************************************ |
|
3469 * * |
|
3470 * Type Casting Routines * |
|
3471 * * |
|
3472 ************************************************************************/ |
|
3473 |
|
3474 /** |
|
3475 * xmlXPathCastBooleanToString: |
|
3476 * @val: a boolean |
|
3477 * |
|
3478 * Converts a boolean to its string value. |
|
3479 * |
|
3480 * Returns a newly allocated string. |
|
3481 */ |
|
3482 xmlChar * |
|
3483 xmlXPathCastBooleanToString (int val) { |
|
3484 xmlChar *ret; |
|
3485 if (val) |
|
3486 ret = xmlStrdup((const xmlChar*) "true"); |
|
3487 else |
|
3488 ret = xmlStrdup((const xmlChar*) "false"); |
|
3489 return(ret); |
|
3490 } |
|
3491 |
|
3492 /** |
|
3493 * xmlXPathCastNumberToString: |
|
3494 * @val: a number |
|
3495 * |
|
3496 * Converts a number to its string value. |
|
3497 * |
|
3498 * Returns a newly allocated string. |
|
3499 * |
|
3500 * OOM: possible --> returns NULL, OOM flag is set |
|
3501 */ |
|
3502 xmlChar* |
|
3503 xmlXPathCastNumberToString (double val) { |
|
3504 xmlChar *ret; |
|
3505 switch (xmlXPathIsInf(val)) { |
|
3506 case 1: |
|
3507 ret = xmlStrdup((const xmlChar*) "Infinity"); |
|
3508 break; |
|
3509 case -1: |
|
3510 ret = xmlStrdup((const xmlChar*) "-Infinity"); |
|
3511 break; |
|
3512 default: |
|
3513 if (xmlXPathIsNaN(val)) { |
|
3514 ret = xmlStrdup((const xmlChar*) "NaN"); |
|
3515 } else if (val == 0 && xmlXPathGetSign(val) != 0) { |
|
3516 ret = xmlStrdup((const xmlChar*) "0"); |
|
3517 } else { |
|
3518 /* TODO: could be improved */ |
|
3519 char buf[100]; |
|
3520 xmlXPathFormatNumber(val, buf, 100); |
|
3521 ret = xmlStrdup((const xmlChar *) buf); |
|
3522 } |
|
3523 } |
|
3524 return(ret); |
|
3525 } |
|
3526 |
|
3527 /** |
|
3528 * xmlXPathCastNodeToString: |
|
3529 * @node: a node |
|
3530 * |
|
3531 * Converts a node to its string value. |
|
3532 * |
|
3533 * Returns a newly allocated string. |
|
3534 * |
|
3535 * OOM: possible --> returns NULL; OOM flag must be checked ALWAYS |
|
3536 */ |
|
3537 xmlChar* |
|
3538 xmlXPathCastNodeToString (xmlNodePtr node) { |
|
3539 return(xmlNodeGetContent(node)); |
|
3540 } // TODO: OPTIMIZE: define this as macro |
|
3541 |
|
3542 /** |
|
3543 * xmlXPathCastNodeSetToString: |
|
3544 * @ns: a node-set |
|
3545 * |
|
3546 * Converts a node-set to its string value. |
|
3547 * |
|
3548 * Returns a newly allocated string. |
|
3549 * |
|
3550 * OOM: possible --> returns NULL, OOM flag is set |
|
3551 */ |
|
3552 xmlChar * |
|
3553 xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) { |
|
3554 if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL)) |
|
3555 return(xmlStrdup((const xmlChar *) "")); |
|
3556 |
|
3557 xmlXPathNodeSetSort(ns); |
|
3558 return(xmlXPathCastNodeToString(ns->nodeTab[0])); |
|
3559 } |
|
3560 |
|
3561 /** |
|
3562 * xmlXPathCastToString: |
|
3563 * @val: an XPath object |
|
3564 * |
|
3565 * Converts an existing object to its string() equivalent |
|
3566 * |
|
3567 * Returns the string value of the object, NULL in case of error. |
|
3568 * A new string is allocated only if needed (@val isn't a |
|
3569 * string object). |
|
3570 */ |
|
3571 xmlChar * |
|
3572 xmlXPathCastToString(xmlXPathObjectPtr val) { |
|
3573 xmlChar *ret = NULL; |
|
3574 |
|
3575 if (val == NULL) |
|
3576 return(xmlStrdup((const xmlChar *) "")); |
|
3577 |
|
3578 switch (val->type) { |
|
3579 case XPATH_UNDEFINED: |
|
3580 #ifdef DEBUG_EXPR |
|
3581 xmlGenericError(xmlGenericErrorContext, "String: undefined\n"); |
|
3582 #endif |
|
3583 ret = xmlStrdup((const xmlChar *) ""); |
|
3584 break; |
|
3585 case XPATH_NUMBER: |
|
3586 ret = xmlXPathCastNumberToString(val->floatval); |
|
3587 break; |
|
3588 |
|
3589 case XPATH_NODESET: |
|
3590 case XPATH_XSLT_TREE: |
|
3591 ret = xmlXPathCastNodeSetToString(val->nodesetval); |
|
3592 break; |
|
3593 |
|
3594 case XPATH_BOOLEAN: |
|
3595 ret = xmlXPathCastBooleanToString(val->boolval); |
|
3596 break; |
|
3597 |
|
3598 case XPATH_STRING: |
|
3599 return(xmlStrdup(val->stringval)); |
|
3600 |
|
3601 case XPATH_USERS: |
|
3602 case XPATH_POINT: |
|
3603 case XPATH_RANGE: |
|
3604 case XPATH_LOCATIONSET: |
|
3605 // TODO |
|
3606 ret = xmlStrdup((const xmlChar *) ""); |
|
3607 break; |
|
3608 } |
|
3609 return(ret); |
|
3610 } |
|
3611 |
|
3612 /** |
|
3613 * xmlXPathConvertString: |
|
3614 * @val: an XPath object |
|
3615 * |
|
3616 * Converts an existing object to its string() equivalent |
|
3617 * |
|
3618 * Returns the new object, the old one is freed (or the operation |
|
3619 * is done directly on @val) or NULL if OOM |
|
3620 */ |
|
3621 xmlXPathObjectPtr |
|
3622 xmlXPathConvertString(xmlXPathObjectPtr val) { |
|
3623 xmlChar *res = NULL; |
|
3624 xmlXPathObjectPtr ret; |
|
3625 |
|
3626 if (val == NULL) |
|
3627 return(xmlXPathNewCString("")); |
|
3628 |
|
3629 switch (val->type) { |
|
3630 case XPATH_UNDEFINED: |
|
3631 #ifdef DEBUG_EXPR |
|
3632 xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n"); |
|
3633 #endif |
|
3634 break; |
|
3635 case XPATH_NODESET: |
|
3636 case XPATH_XSLT_TREE: |
|
3637 res = xmlXPathCastNodeSetToString(val->nodesetval); |
|
3638 break; |
|
3639 case XPATH_STRING: |
|
3640 return(val); |
|
3641 case XPATH_BOOLEAN: |
|
3642 res = xmlXPathCastBooleanToString(val->boolval); |
|
3643 break; |
|
3644 case XPATH_NUMBER: |
|
3645 res = xmlXPathCastNumberToString(val->floatval); |
|
3646 break; |
|
3647 case XPATH_USERS: |
|
3648 case XPATH_POINT: |
|
3649 case XPATH_RANGE: |
|
3650 case XPATH_LOCATIONSET: |
|
3651 TODO; |
|
3652 break; |
|
3653 } |
|
3654 xmlXPathFreeObject(val); |
|
3655 if(OOM_FLAG) return(NULL); |
|
3656 if (res == NULL) |
|
3657 return(xmlXPathNewCString("")); |
|
3658 ret = xmlXPathWrapString(res); |
|
3659 if(OOM_FLAG) |
|
3660 { |
|
3661 xmlFree(res); |
|
3662 return(NULL); |
|
3663 } |
|
3664 return(ret); |
|
3665 } |
|
3666 |
|
3667 /** |
|
3668 * xmlXPathCastBooleanToNumber: |
|
3669 * @val: a boolean |
|
3670 * |
|
3671 * Converts a boolean to its number value |
|
3672 * |
|
3673 * Returns the number value |
|
3674 */ |
|
3675 double |
|
3676 xmlXPathCastBooleanToNumber(int val) { |
|
3677 return val ? (1.0) :(0.0); |
|
3678 } |
|
3679 |
|
3680 /** |
|
3681 * xmlXPathCastStringToNumber: |
|
3682 * @val: a string |
|
3683 * |
|
3684 * Converts a string to its number value |
|
3685 * |
|
3686 * Returns the number value |
|
3687 * |
|
3688 * OOM: Never |
|
3689 */ |
|
3690 double |
|
3691 xmlXPathCastStringToNumber(const xmlChar * val) { |
|
3692 return(xmlXPathStringEvalNumber(val)); |
|
3693 } |
|
3694 |
|
3695 /** |
|
3696 * xmlXPathCastNodeToNumber: |
|
3697 * @node: a node |
|
3698 * |
|
3699 * Converts a node to its number value |
|
3700 * |
|
3701 * Returns the number value |
|
3702 */ |
|
3703 double |
|
3704 xmlXPathCastNodeToNumber (xmlNodePtr node) { |
|
3705 xmlChar *strval; |
|
3706 double ret; |
|
3707 |
|
3708 if (node == NULL) |
|
3709 return(xmlXPathNAN); |
|
3710 strval = xmlXPathCastNodeToString(node); |
|
3711 if (strval == NULL) |
|
3712 return(xmlXPathNAN); |
|
3713 |
|
3714 ret = xmlXPathCastStringToNumber(strval); |
|
3715 xmlFree(strval); |
|
3716 |
|
3717 return(ret); |
|
3718 } |
|
3719 |
|
3720 /** |
|
3721 * xmlXPathCastNodeSetToNumber: |
|
3722 * @ns: a node-set |
|
3723 * |
|
3724 * Converts a node-set to its number value |
|
3725 * |
|
3726 * Returns the number value |
|
3727 * |
|
3728 * OOM: possible --> OOM flag must be checked |
|
3729 */ |
|
3730 double |
|
3731 xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) { |
|
3732 xmlChar *str; |
|
3733 double ret; |
|
3734 |
|
3735 if (ns == NULL) |
|
3736 return(xmlXPathNAN); |
|
3737 str = xmlXPathCastNodeSetToString(ns); // OOM is possible |
|
3738 if(str){ |
|
3739 ret = xmlXPathCastStringToNumber(str); |
|
3740 xmlFree(str); |
|
3741 return(ret); |
|
3742 } |
|
3743 return 0; |
|
3744 } |
|
3745 |
|
3746 /** |
|
3747 * xmlXPathCastToNumber: |
|
3748 * @val: an XPath object |
|
3749 * |
|
3750 * Converts an XPath object to its number value |
|
3751 * |
|
3752 * Returns the number value |
|
3753 */ |
|
3754 double |
|
3755 xmlXPathCastToNumber(xmlXPathObjectPtr val) { |
|
3756 double ret = 0.0; |
|
3757 |
|
3758 if (val == NULL) |
|
3759 return(xmlXPathNAN); |
|
3760 switch (val->type) { |
|
3761 case XPATH_UNDEFINED: |
|
3762 #ifdef DEGUB_EXPR |
|
3763 xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n"); |
|
3764 #endif |
|
3765 ret = xmlXPathNAN; |
|
3766 break; |
|
3767 case XPATH_NODESET: |
|
3768 case XPATH_XSLT_TREE: |
|
3769 ret = xmlXPathCastNodeSetToNumber(val->nodesetval); |
|
3770 break; |
|
3771 case XPATH_STRING: |
|
3772 ret = xmlXPathCastStringToNumber(val->stringval); |
|
3773 break; |
|
3774 case XPATH_NUMBER: |
|
3775 ret = val->floatval; |
|
3776 break; |
|
3777 case XPATH_BOOLEAN: |
|
3778 ret = xmlXPathCastBooleanToNumber(val->boolval); |
|
3779 break; |
|
3780 case XPATH_USERS: |
|
3781 case XPATH_POINT: |
|
3782 case XPATH_RANGE: |
|
3783 case XPATH_LOCATIONSET: |
|
3784 TODO; |
|
3785 ret = xmlXPathNAN; |
|
3786 break; |
|
3787 } |
|
3788 return(ret); |
|
3789 } |
|
3790 |
|
3791 /** |
|
3792 * xmlXPathConvertNumber: |
|
3793 * @val: an XPath object |
|
3794 * |
|
3795 * Converts an existing object to its number() equivalent |
|
3796 * |
|
3797 * Returns the new object, the old one is freed (or the operation |
|
3798 * is done directly on @val) |
|
3799 */ |
|
3800 xmlXPathObjectPtr |
|
3801 xmlXPathConvertNumber(xmlXPathObjectPtr val) { |
|
3802 xmlXPathObjectPtr ret; |
|
3803 |
|
3804 if (val == NULL) |
|
3805 return(xmlXPathNewFloat(0.0)); |
|
3806 if (val->type == XPATH_NUMBER) |
|
3807 return(val); |
|
3808 ret = xmlXPathNewFloat(xmlXPathCastToNumber(val)); |
|
3809 xmlXPathFreeObject(val); |
|
3810 return(ret); |
|
3811 } |
|
3812 |
|
3813 /** |
|
3814 * xmlXPathCastNumberToBoolean: |
|
3815 * @val: a number |
|
3816 * |
|
3817 * Converts a number to its boolean value |
|
3818 * |
|
3819 * Returns the boolean value |
|
3820 */ |
|
3821 int |
|
3822 xmlXPathCastNumberToBoolean (double val) { |
|
3823 if (xmlXPathIsNaN(val) || (val == 0.0)) |
|
3824 return(0); |
|
3825 return(1); |
|
3826 } |
|
3827 |
|
3828 /** |
|
3829 * xmlXPathCastStringToBoolean: |
|
3830 * @val: a string |
|
3831 * |
|
3832 * Converts a string to its boolean value |
|
3833 * |
|
3834 * Returns the boolean value |
|
3835 * |
|
3836 * OOM: never |
|
3837 */ |
|
3838 int |
|
3839 xmlXPathCastStringToBoolean (const xmlChar *val) { |
|
3840 if ((val == NULL) || (xmlStrlen(val) == 0)) |
|
3841 return(0); |
|
3842 return(1); |
|
3843 } |
|
3844 |
|
3845 /** |
|
3846 * xmlXPathCastNodeSetToBoolean: |
|
3847 * @ns: a node-set |
|
3848 * |
|
3849 * Converts a node-set to its boolean value |
|
3850 * |
|
3851 * Returns the boolean value |
|
3852 * |
|
3853 * OOM: never |
|
3854 */ |
|
3855 int |
|
3856 xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) { |
|
3857 if ((ns == NULL) || (ns->nodeNr == 0)) |
|
3858 return(0); |
|
3859 return(1); |
|
3860 } |
|
3861 |
|
3862 /** |
|
3863 * xmlXPathCastToBoolean: |
|
3864 * @val: an XPath object |
|
3865 * |
|
3866 * Converts an XPath object to its boolean value |
|
3867 * |
|
3868 * Returns the boolean value |
|
3869 */ |
|
3870 int |
|
3871 xmlXPathCastToBoolean (xmlXPathObjectPtr val) { |
|
3872 int ret = 0; |
|
3873 |
|
3874 if (val == NULL) |
|
3875 return(0); |
|
3876 switch (val->type) { |
|
3877 case XPATH_UNDEFINED: |
|
3878 #ifdef DEBUG_EXPR |
|
3879 xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n"); |
|
3880 #endif |
|
3881 ret = 0; |
|
3882 break; |
|
3883 case XPATH_NODESET: |
|
3884 case XPATH_XSLT_TREE: |
|
3885 ret = xmlXPathCastNodeSetToBoolean(val->nodesetval); |
|
3886 break; |
|
3887 case XPATH_STRING: |
|
3888 ret = xmlXPathCastStringToBoolean(val->stringval); |
|
3889 break; |
|
3890 case XPATH_NUMBER: |
|
3891 ret = xmlXPathCastNumberToBoolean(val->floatval); |
|
3892 break; |
|
3893 case XPATH_BOOLEAN: |
|
3894 ret = val->boolval; |
|
3895 break; |
|
3896 case XPATH_USERS: |
|
3897 case XPATH_POINT: |
|
3898 case XPATH_RANGE: |
|
3899 case XPATH_LOCATIONSET: |
|
3900 TODO; |
|
3901 ret = 0; |
|
3902 break; |
|
3903 } |
|
3904 return(ret); |
|
3905 } |
|
3906 |
|
3907 |
|
3908 /** |
|
3909 * xmlXPathConvertBoolean: |
|
3910 * @val: an XPath object |
|
3911 * |
|
3912 * Converts an existing object to its boolean() equivalent |
|
3913 * |
|
3914 * Returns the new object, the old one is freed (or the operation |
|
3915 * is done directly on @val) |
|
3916 */ |
|
3917 xmlXPathObjectPtr |
|
3918 xmlXPathConvertBoolean(xmlXPathObjectPtr val) { |
|
3919 xmlXPathObjectPtr ret; |
|
3920 |
|
3921 if (val == NULL) |
|
3922 return(xmlXPathNewBoolean(0)); |
|
3923 if (val->type == XPATH_BOOLEAN) |
|
3924 return(val); |
|
3925 ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val)); |
|
3926 xmlXPathFreeObject(val); |
|
3927 return(ret); |
|
3928 } |
|
3929 |
|
3930 /************************************************************************ |
|
3931 * * |
|
3932 * Routines to handle XPath contexts * |
|
3933 * * |
|
3934 ************************************************************************/ |
|
3935 |
|
3936 #ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED |
|
3937 /** |
|
3938 * xmlHashCopier: |
|
3939 * typedef void *(*xmlHashCopier)(void *payload, xmlChar *name); |
|
3940 */ |
|
3941 void* xeSimpleHashEntryCopier(void *payload, xmlChar* name ) |
|
3942 { |
|
3943 return payload; // just return the same value |
|
3944 } |
|
3945 #endif |
|
3946 |
|
3947 /** |
|
3948 * xmlXPathNewContext: |
|
3949 * @doc: the XML document |
|
3950 * |
|
3951 * Create a new xmlXPathContext |
|
3952 * |
|
3953 * Returns the xmlXPathContext just allocated. The caller will need to free it. |
|
3954 */ |
|
3955 xmlXPathContextPtr |
|
3956 xmlXPathNewContext(xmlDocPtr doc) { |
|
3957 xmlXPathContextPtr ret; |
|
3958 xmlHashTablePtr gFuncHash; |
|
3959 |
|
3960 ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext)); |
|
3961 if (ret == NULL) { |
|
3962 xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating context\n")); |
|
3963 return(NULL); |
|
3964 } |
|
3965 memset(ret, 0 , (size_t) sizeof(xmlXPathContext)); |
|
3966 |
|
3967 ret->doc = doc; // this is a context node in general, not always a document node |
|
3968 |
|
3969 // XMLENGINE: these are nullified already |
|
3970 //ret->node = NULL; |
|
3971 //ret->varHash = NULL; |
|
3972 //ret->nb_types = 0; |
|
3973 //ret->max_types = 0; |
|
3974 //ret->types = NULL; |
|
3975 |
|
3976 #ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED |
|
3977 // XMLENGINE: BEGIN NEW CODE |
|
3978 // xmlXPathDefaultFunctionsHash is the new field in xmlGlobalState structure |
|
3979 gFuncHash = xmlXPathDefaultFunctionsHash; |
|
3980 if(!gFuncHash) |
|
3981 { // global reusable hash table was not initialized yet |
|
3982 if( xmlXPathRegisterAllFunctions(ret) < 0 ) |
|
3983 { |
|
3984 xmlFree(ret); |
|
3985 return(NULL); |
|
3986 } |
|
3987 if(xmlXPathDefineExtensionFunctionsGlobally) |
|
3988 { |
|
3989 xmlXPathDefaultFunctionsHash = ret->funcHash; |
|
3990 } |
|
3991 else |
|
3992 { |
|
3993 xmlXPathDefaultFunctionsHash = xmlHashCopy(ret->funcHash, xeSimpleHashEntryCopier); |
|
3994 } |
|
3995 } |
|
3996 else |
|
3997 { // copy previously initialized hash table OR |
|
3998 // reuse the global hash, then, no cleanup is needed when |
|
3999 // XPath context is freed |
|
4000 if(xmlXPathDefineExtensionFunctionsGlobally) |
|
4001 { |
|
4002 ret->funcHash = gFuncHash; |
|
4003 } |
|
4004 else |
|
4005 { |
|
4006 ret->funcHash = xmlHashCopy( gFuncHash, xeSimpleHashEntryCopier ); |
|
4007 } |
|
4008 } |
|
4009 // XMLENGINE: END NEW CODE |
|
4010 #else |
|
4011 /* original code*/ |
|
4012 ret->funcHash = xmlHashCreate(0); |
|
4013 xmlXPathRegisterAllFunctions(ret); //TODO: check OOM |
|
4014 #endif /* else !XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED */ |
|
4015 |
|
4016 // XMLENGINE: these are nullified already |
|
4017 //ret->nb_axis = 0; |
|
4018 //ret->max_axis = 0; |
|
4019 //ret->axis = NULL; |
|
4020 //ret->nsHash = NULL; |
|
4021 //ret->user = NULL; |
|
4022 |
|
4023 ret->contextSize = -1; |
|
4024 ret->proximityPosition = -1; |
|
4025 |
|
4026 return(ret); |
|
4027 } |
|
4028 |
|
4029 /** |
|
4030 * xmlXPathFreeContext: |
|
4031 * @ctxt: the context to free |
|
4032 * |
|
4033 * Free up an xmlXPathContext |
|
4034 */ |
|
4035 void |
|
4036 xmlXPathFreeContext(xmlXPathContextPtr ctxt) { |
|
4037 // TODO: Check will this erase useful error info during ERR cleanup |
|
4038 // merge: i'm not sure if this is useful anymore... |
|
4039 if (&ctxt->lastError) |
|
4040 xmlResetError(&ctxt->lastError); |
|
4041 |
|
4042 xmlXPathRegisteredNsCleanup(ctxt); |
|
4043 #ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED |
|
4044 // DO NOTHING HERE if global hash table is reused. |
|
4045 // The function hash is destroyed only if it is not reused, |
|
4046 // otherwise the reference to it is stored in global: |
|
4047 // xmlXPathDefaultFunctionsHash variable |
|
4048 // and the hash table is freed during XML Engine cleanup with |
|
4049 // xeXPathCleanup() |
|
4050 if(!xmlXPathDefineExtensionFunctionsGlobally) |
|
4051 { |
|
4052 xmlXPathRegisteredFuncsCleanup(ctxt); |
|
4053 } |
|
4054 #else |
|
4055 xmlXPathRegisteredFuncsCleanup(ctxt); |
|
4056 #endif |
|
4057 xmlXPathRegisteredVariablesCleanup(ctxt); |
|
4058 xmlFree(ctxt); |
|
4059 } |
|
4060 |
|
4061 /************************************************************************ |
|
4062 * * |
|
4063 * Routines to handle XPath parser contexts * |
|
4064 * * |
|
4065 ************************************************************************/ |
|
4066 |
|
4067 #define CHECK_CTXT(ctxt) \ |
|
4068 if (ctxt == NULL) { \ |
|
4069 xmlGenericError(xmlGenericErrorContext, \ |
|
4070 EMBED_ERRTXT("%s:%d Internal error: ctxt == NULL\n"), \ |
|
4071 __FILE__, __LINE__); \ |
|
4072 } \ |
|
4073 |
|
4074 |
|
4075 // TODO: Quite big macro -- for release build it could be reduced by xmlGenericError calls |
|
4076 #define CHECK_CONTEXT(ctxt) \ |
|
4077 if (ctxt == NULL) { \ |
|
4078 xmlGenericError(xmlGenericErrorContext, \ |
|
4079 EMBED_ERRTXT("%s:%d Internal error: no context\n"), \ |
|
4080 __FILE__, __LINE__); \ |
|
4081 } \ |
|
4082 else if (ctxt->doc == NULL) { \ |
|
4083 xmlGenericError(xmlGenericErrorContext, \ |
|
4084 EMBED_ERRTXT("%s:%d Internal error: no document\n"), \ |
|
4085 __FILE__, __LINE__); \ |
|
4086 } \ |
|
4087 else if (ctxt->doc->children == NULL) { \ |
|
4088 xmlGenericError(xmlGenericErrorContext, \ |
|
4089 EMBED_ERRTXT("%s:%d Internal error: document without root\n"), \ |
|
4090 __FILE__, __LINE__); \ |
|
4091 } \ |
|
4092 |
|
4093 |
|
4094 /** |
|
4095 * xmlXPathNewParserContext: |
|
4096 * @str: the XPath expression |
|
4097 * @ctxt: the XPath context |
|
4098 * |
|
4099 * Create a new xmlXPathParserContext |
|
4100 * |
|
4101 * Returns the xmlXPathParserContext just allocated. |
|
4102 */ |
|
4103 xmlXPathParserContextPtr |
|
4104 xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) { |
|
4105 xmlXPathParserContextPtr ret; |
|
4106 |
|
4107 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); |
|
4108 if (ret == NULL) { |
|
4109 xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating parser context\n")); |
|
4110 return(NULL); |
|
4111 } |
|
4112 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); |
|
4113 ret->cur = ret->base = str; |
|
4114 ret->context = ctxt; |
|
4115 |
|
4116 ret->comp = xmlXPathNewCompExpr(); |
|
4117 if (ret->comp == NULL) { |
|
4118 xmlFree(ret->valueTab); |
|
4119 xmlFree(ret); |
|
4120 return(NULL); |
|
4121 } |
|
4122 if ((ctxt != NULL) && (ctxt->dict != NULL)) { |
|
4123 ret->comp->dict = ctxt->dict; |
|
4124 xmlDictReference(ret->comp->dict); |
|
4125 } |
|
4126 |
|
4127 return(ret); |
|
4128 } |
|
4129 |
|
4130 /** |
|
4131 * xmlXPathCompParserContext: |
|
4132 * @comp: the XPath compiled expression |
|
4133 * @ctxt: the XPath context |
|
4134 * |
|
4135 * Create a new xmlXPathParserContext when processing a compiled expression |
|
4136 * |
|
4137 * Returns the xmlXPathParserContext just allocated. |
|
4138 */ |
|
4139 static xmlXPathParserContextPtr |
|
4140 xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) { |
|
4141 xmlXPathParserContextPtr ret; |
|
4142 |
|
4143 ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext)); |
|
4144 if (ret == NULL) { |
|
4145 xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n")); |
|
4146 return(NULL); |
|
4147 } |
|
4148 memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext)); |
|
4149 |
|
4150 /* Allocate the value stack */ |
|
4151 ret->valueTab = (xmlXPathObjectPtr *) |
|
4152 xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); |
|
4153 if (ret->valueTab == NULL) { |
|
4154 xmlFree(ret); |
|
4155 xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n")); |
|
4156 return(NULL); |
|
4157 } |
|
4158 ret->valueNr = 0; |
|
4159 ret->valueMax = 10; |
|
4160 ret->value = NULL; |
|
4161 |
|
4162 ret->context = ctxt; |
|
4163 ret->comp = comp; |
|
4164 |
|
4165 return(ret); |
|
4166 } |
|
4167 |
|
4168 /** |
|
4169 * xmlXPathFreeParserContext: |
|
4170 * @ctxt: the context to free |
|
4171 * |
|
4172 * Free up an xmlXPathParserContext |
|
4173 */ |
|
4174 void |
|
4175 xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) { |
|
4176 if (ctxt->valueTab != NULL) { |
|
4177 xmlFree(ctxt->valueTab); |
|
4178 } |
|
4179 if (ctxt->comp) |
|
4180 xmlXPathFreeCompExpr(ctxt->comp); |
|
4181 xmlFree(ctxt); |
|
4182 } |
|
4183 |
|
4184 /************************************************************************ |
|
4185 * * |
|
4186 * The implicit core function library * |
|
4187 * * |
|
4188 ************************************************************************/ |
|
4189 |
|
4190 /** |
|
4191 * xmlXPathNodeValHash: |
|
4192 * @node: a node pointer |
|
4193 * |
|
4194 * Function computing the beginning of the string value of the node, |
|
4195 * used to speed up comparisons |
|
4196 * |
|
4197 * Returns an int usable as a hash |
|
4198 */ |
|
4199 static unsigned int |
|
4200 xmlXPathNodeValHash(xmlNodePtr node) { |
|
4201 int len = 2; |
|
4202 const xmlChar * string = NULL; |
|
4203 xmlNodePtr tmp = NULL; |
|
4204 unsigned int ret = 0; |
|
4205 |
|
4206 if (node == NULL) |
|
4207 return(0); |
|
4208 |
|
4209 if (node->type == XML_DOCUMENT_NODE) { |
|
4210 tmp = xmlDocGetRootElement((xmlDocPtr) node); |
|
4211 if (tmp == NULL) |
|
4212 node = node->children; |
|
4213 else |
|
4214 node = tmp; |
|
4215 |
|
4216 if (node == NULL) |
|
4217 return(0); |
|
4218 } |
|
4219 |
|
4220 switch (node->type) { |
|
4221 case XML_COMMENT_NODE: |
|
4222 case XML_PI_NODE: |
|
4223 case XML_CDATA_SECTION_NODE: |
|
4224 case XML_TEXT_NODE: |
|
4225 string = node->content; |
|
4226 if (string == NULL) |
|
4227 return(0); |
|
4228 if (string[0] == 0) |
|
4229 return(0); |
|
4230 return(((unsigned int) string[0]) + |
|
4231 (((unsigned int) string[1]) << 8)); |
|
4232 case XML_NAMESPACE_DECL: |
|
4233 string = ((xmlNsPtr)node)->href; |
|
4234 if (string == NULL) |
|
4235 return(0); |
|
4236 if (string[0] == 0) |
|
4237 return(0); |
|
4238 return(((unsigned int) string[0]) + |
|
4239 (((unsigned int) string[1]) << 8)); |
|
4240 case XML_ATTRIBUTE_NODE: |
|
4241 tmp = ((xmlAttrPtr) node)->children; |
|
4242 break; |
|
4243 case XML_ELEMENT_NODE: |
|
4244 tmp = node->children; |
|
4245 break; |
|
4246 default: |
|
4247 return(0); |
|
4248 } |
|
4249 while (tmp != NULL) { |
|
4250 switch (tmp->type) { |
|
4251 case XML_COMMENT_NODE: |
|
4252 case XML_PI_NODE: |
|
4253 case XML_CDATA_SECTION_NODE: |
|
4254 case XML_TEXT_NODE: |
|
4255 string = tmp->content; |
|
4256 break; |
|
4257 case XML_NAMESPACE_DECL: |
|
4258 string = ((xmlNsPtr)tmp)->href; |
|
4259 break; |
|
4260 default: |
|
4261 break; |
|
4262 } |
|
4263 if ((string != NULL) && (string[0] != 0)) { |
|
4264 if (len == 1) { |
|
4265 return(ret + (((unsigned int) string[0]) << 8)); |
|
4266 } |
|
4267 if (string[1] == 0) { |
|
4268 len = 1; |
|
4269 ret = (unsigned int) string[0]; |
|
4270 } else { |
|
4271 return(((unsigned int) string[0]) + |
|
4272 (((unsigned int) string[1]) << 8)); |
|
4273 } |
|
4274 } |
|
4275 /* |
|
4276 * Skip to next node |
|
4277 */ |
|
4278 if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) { |
|
4279 if (tmp->children->type != XML_ENTITY_DECL) { |
|
4280 tmp = tmp->children; |
|
4281 continue; |
|
4282 } |
|
4283 } |
|
4284 if (tmp == node) |
|
4285 break; |
|
4286 |
|
4287 if (tmp->next != NULL) { |
|
4288 tmp = tmp->next; |
|
4289 continue; |
|
4290 } |
|
4291 |
|
4292 do { |
|
4293 tmp = tmp->parent; |
|
4294 if (tmp == NULL) |
|
4295 break; |
|
4296 if (tmp == node) { |
|
4297 tmp = NULL; |
|
4298 break; |
|
4299 } |
|
4300 if (tmp->next != NULL) { |
|
4301 tmp = tmp->next; |
|
4302 break; |
|
4303 } |
|
4304 } while (tmp != NULL); |
|
4305 } |
|
4306 return(ret); |
|
4307 } |
|
4308 |
|
4309 /** |
|
4310 * xmlXPathStringHash: |
|
4311 * @string: a string |
|
4312 * |
|
4313 * Function computing the beginning of the string value of the node, |
|
4314 * used to speed up comparisons |
|
4315 * |
|
4316 * Returns an int usable as a hash |
|
4317 */ |
|
4318 static unsigned int |
|
4319 xmlXPathStringHash(const xmlChar * string) { |
|
4320 if (string == NULL) |
|
4321 return((unsigned int) 0); |
|
4322 if (string[0] == 0) |
|
4323 return(0); |
|
4324 return(((unsigned int) string[0]) + |
|
4325 (((unsigned int) string[1]) << 8)); |
|
4326 } |
|
4327 |
|
4328 /** |
|
4329 * xmlXPathCompareNodeSetFloat: |
|
4330 * @ctxt: the XPath Parser context |
|
4331 * @inf: less than (1) or greater than (0) |
|
4332 * @strict: is the comparison strict |
|
4333 * @arg: the node set |
|
4334 * @f: the value |
|
4335 * |
|
4336 * Implement the compare operation between a nodeset and a number |
|
4337 * @ns < @val (1, 1, ... |
|
4338 * @ns <= @val (1, 0, ... |
|
4339 * @ns > @val (0, 1, ... |
|
4340 * @ns >= @val (0, 0, ... |
|
4341 * |
|
4342 * If one object to be compared is a node-set and the other is a number, |
|
4343 * then the comparison will be true if and only if there is a node in the |
|
4344 * node-set such that the result of performing the comparison on the number |
|
4345 * to be compared and on the result of converting the string-value of that |
|
4346 * node to a number using the number function is true. |
|
4347 * |
|
4348 * Returns 0 or 1 depending on the results of the test. |
|
4349 */ |
|
4350 static int |
|
4351 xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict, |
|
4352 xmlXPathObjectPtr arg, xmlXPathObjectPtr f) { |
|
4353 int i, ret = 0; |
|
4354 xmlNodeSetPtr ns; |
|
4355 xmlChar *str2; |
|
4356 |
|
4357 if ((f == NULL) || (arg == NULL) || |
|
4358 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { |
|
4359 xmlXPathFreeObject(arg); |
|
4360 xmlXPathFreeObject(f); |
|
4361 return(0); |
|
4362 } |
|
4363 ns = arg->nodesetval; |
|
4364 if (ns != NULL) { |
|
4365 for (i = 0;i < ns->nodeNr;i++) { |
|
4366 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); |
|
4367 if (str2 != NULL) { |
|
4368 valuePush(ctxt, |
|
4369 xmlXPathNewString(str2)); |
|
4370 xmlFree(str2); |
|
4371 xmlXPathNumberFunction(ctxt, 1); |
|
4372 valuePush(ctxt, xmlXPathObjectCopy(f)); |
|
4373 ret = xmlXPathCompareValues(ctxt, inf, strict); |
|
4374 if (ret) |
|
4375 break; |
|
4376 } |
|
4377 } |
|
4378 } |
|
4379 xmlXPathFreeObject(arg); |
|
4380 xmlXPathFreeObject(f); |
|
4381 return(ret); |
|
4382 } |
|
4383 |
|
4384 /** |
|
4385 * xmlXPathCompareNodeSetString: |
|
4386 * @ctxt: the XPath Parser context |
|
4387 * @inf: less than (1) or greater than (0) |
|
4388 * @strict: is the comparison strict |
|
4389 * @arg: the node set |
|
4390 * @s: the value |
|
4391 * |
|
4392 * Implement the compare operation between a nodeset and a string |
|
4393 * @ns < @val (1, 1, ... |
|
4394 * @ns <= @val (1, 0, ... |
|
4395 * @ns > @val (0, 1, ... |
|
4396 * @ns >= @val (0, 0, ... |
|
4397 * |
|
4398 * If one object to be compared is a node-set and the other is a string, |
|
4399 * then the comparison will be true if and only if there is a node in |
|
4400 * the node-set such that the result of performing the comparison on the |
|
4401 * string-value of the node and the other string is true. |
|
4402 * |
|
4403 * Returns 0 or 1 depending on the results of the test. |
|
4404 */ |
|
4405 static int |
|
4406 xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict, |
|
4407 xmlXPathObjectPtr arg, xmlXPathObjectPtr s) { |
|
4408 int i, ret = 0; |
|
4409 xmlNodeSetPtr ns; |
|
4410 xmlChar *str2; |
|
4411 |
|
4412 if ((s == NULL) || (arg == NULL) || |
|
4413 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) { |
|
4414 xmlXPathFreeObject(arg); |
|
4415 xmlXPathFreeObject(s); |
|
4416 return(0); |
|
4417 } |
|
4418 ns = arg->nodesetval; |
|
4419 if (ns != NULL) { |
|
4420 for (i = 0;i < ns->nodeNr;i++) { |
|
4421 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); |
|
4422 if (str2 != NULL) { |
|
4423 valuePush(ctxt, |
|
4424 xmlXPathNewString(str2)); |
|
4425 xmlFree(str2); |
|
4426 valuePush(ctxt, xmlXPathObjectCopy(s)); |
|
4427 ret = xmlXPathCompareValues(ctxt, inf, strict); |
|
4428 if (ret) |
|
4429 break; |
|
4430 } |
|
4431 } |
|
4432 } |
|
4433 xmlXPathFreeObject(arg); |
|
4434 xmlXPathFreeObject(s); |
|
4435 return(ret); |
|
4436 } |
|
4437 |
|
4438 /** |
|
4439 * xmlXPathCompareNodeSets: |
|
4440 * @inf: less than (1) or greater than (0) |
|
4441 * @strict: is the comparison strict |
|
4442 * @arg1: the first node set object |
|
4443 * @arg2: the second node set object |
|
4444 * |
|
4445 * Implement the compare operation on nodesets: |
|
4446 * |
|
4447 * If both objects to be compared are node-sets, then the comparison |
|
4448 * will be true if and only if there is a node in the first node-set |
|
4449 * and a node in the second node-set such that the result of performing |
|
4450 * the comparison on the string-values of the two nodes is true. |
|
4451 * .... |
|
4452 * When neither object to be compared is a node-set and the operator |
|
4453 * is <=, <, >= or >, then the objects are compared by converting both |
|
4454 * objects to numbers and comparing the numbers according to IEEE 754. |
|
4455 * .... |
|
4456 * The number function converts its argument to a number as follows: |
|
4457 * - a string that consists of optional whitespace followed by an |
|
4458 * optional minus sign followed by a Number followed by whitespace |
|
4459 * is converted to the IEEE 754 number that is nearest (according |
|
4460 * to the IEEE 754 round-to-nearest rule) to the mathematical value |
|
4461 * represented by the string; any other string is converted to NaN |
|
4462 * |
|
4463 * Conclusion all nodes need to be converted first to their string value |
|
4464 * and then the comparison must be done when possible |
|
4465 */ |
|
4466 static int |
|
4467 xmlXPathCompareNodeSets(int inf, int strict, |
|
4468 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { |
|
4469 int i, j, init = 0; |
|
4470 double val1; |
|
4471 double *values2; |
|
4472 int ret = 0; |
|
4473 xmlNodeSetPtr ns1; |
|
4474 xmlNodeSetPtr ns2; |
|
4475 |
|
4476 if ((arg1 == NULL) || |
|
4477 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) { |
|
4478 xmlXPathFreeObject(arg2); |
|
4479 return(0); |
|
4480 } |
|
4481 if ((arg2 == NULL) || |
|
4482 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) { |
|
4483 xmlXPathFreeObject(arg1); |
|
4484 xmlXPathFreeObject(arg2); |
|
4485 return(0); |
|
4486 } |
|
4487 |
|
4488 ns1 = arg1->nodesetval; |
|
4489 ns2 = arg2->nodesetval; |
|
4490 |
|
4491 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) { |
|
4492 xmlXPathFreeObject(arg1); |
|
4493 xmlXPathFreeObject(arg2); |
|
4494 return(0); |
|
4495 } |
|
4496 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) { |
|
4497 xmlXPathFreeObject(arg1); |
|
4498 xmlXPathFreeObject(arg2); |
|
4499 return(0); |
|
4500 } |
|
4501 |
|
4502 values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double)); |
|
4503 if (values2 == NULL) { |
|
4504 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4505 xmlXPathFreeObject(arg1); |
|
4506 xmlXPathFreeObject(arg2); |
|
4507 return(0); |
|
4508 } |
|
4509 for (i = 0;i < ns1->nodeNr;i++) { |
|
4510 val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]); |
|
4511 if (xmlXPathIsNaN(val1)) |
|
4512 continue; |
|
4513 for (j = 0;j < ns2->nodeNr;j++) { |
|
4514 if (init == 0) { |
|
4515 values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]); |
|
4516 } |
|
4517 if (xmlXPathIsNaN(values2[j])) |
|
4518 continue; |
|
4519 if (inf && strict) |
|
4520 ret = (val1 < values2[j]); |
|
4521 else if (inf && !strict) |
|
4522 ret = (val1 <= values2[j]); |
|
4523 else if (!inf && strict) |
|
4524 ret = (val1 > values2[j]); |
|
4525 else if (!inf && !strict) |
|
4526 ret = (val1 >= values2[j]); |
|
4527 if (ret) |
|
4528 break; |
|
4529 } |
|
4530 if (ret) |
|
4531 break; |
|
4532 init = 1; |
|
4533 } |
|
4534 xmlFree(values2); |
|
4535 xmlXPathFreeObject(arg1); |
|
4536 xmlXPathFreeObject(arg2); |
|
4537 return(ret); |
|
4538 } |
|
4539 |
|
4540 /** |
|
4541 * xmlXPathCompareNodeSetValue: |
|
4542 * @ctxt: the XPath Parser context |
|
4543 * @inf: less than (1) or greater than (0) |
|
4544 * @strict: is the comparison strict |
|
4545 * @arg: the node set |
|
4546 * @val: the value |
|
4547 * |
|
4548 * Implement the compare operation between a nodeset and a value |
|
4549 * @ns < @val (1, 1, ... |
|
4550 * @ns <= @val (1, 0, ... |
|
4551 * @ns > @val (0, 1, ... |
|
4552 * @ns >= @val (0, 0, ... |
|
4553 * |
|
4554 * If one object to be compared is a node-set and the other is a boolean, |
|
4555 * then the comparison will be true if and only if the result of performing |
|
4556 * the comparison on the boolean and on the result of converting |
|
4557 * the node-set to a boolean using the boolean function is true. |
|
4558 * |
|
4559 * Returns 0 or 1 depending on the results of the test. |
|
4560 */ |
|
4561 static int |
|
4562 xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict, |
|
4563 xmlXPathObjectPtr arg, xmlXPathObjectPtr val) { |
|
4564 if ((val == NULL) || (arg == NULL) || |
|
4565 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) |
|
4566 return(0); |
|
4567 |
|
4568 switch(val->type) { |
|
4569 case XPATH_NUMBER: |
|
4570 return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val)); |
|
4571 case XPATH_NODESET: |
|
4572 case XPATH_XSLT_TREE: |
|
4573 return(xmlXPathCompareNodeSets(inf, strict, arg, val)); |
|
4574 case XPATH_STRING: |
|
4575 return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val)); |
|
4576 case XPATH_BOOLEAN: |
|
4577 valuePush(ctxt, arg); |
|
4578 xmlXPathBooleanFunction(ctxt, 1); |
|
4579 valuePush(ctxt, val); |
|
4580 return(xmlXPathCompareValues(ctxt, inf, strict)); |
|
4581 default: |
|
4582 TODO |
|
4583 } |
|
4584 return(0); |
|
4585 } |
|
4586 |
|
4587 /** |
|
4588 * xmlXPathEqualNodeSetString: |
|
4589 * @arg: the nodeset object argument |
|
4590 * @str: the string to compare to. |
|
4591 * @neq: flag to show whether for '=' (0) or '!=' (1) |
|
4592 * |
|
4593 * Implement the equal operation on XPath objects content: @arg1 == @arg2 |
|
4594 * If one object to be compared is a node-set and the other is a string, |
|
4595 * then the comparison will be true if and only if there is a node in |
|
4596 * the node-set such that the result of performing the comparison on the |
|
4597 * string-value of the node and the other string is true. |
|
4598 * |
|
4599 * Returns 0 or 1 depending on the results of the test. |
|
4600 */ |
|
4601 static int |
|
4602 xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar * str, int neq) |
|
4603 { |
|
4604 int i; |
|
4605 xmlNodeSetPtr ns; |
|
4606 xmlChar *str2; |
|
4607 unsigned int hash; |
|
4608 |
|
4609 if ((str == NULL) || (arg == NULL) || |
|
4610 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) |
|
4611 return (0); |
|
4612 ns = arg->nodesetval; |
|
4613 /* |
|
4614 * A NULL nodeset compared with a string is always false |
|
4615 * (since there is no node equal, and no node not equal) |
|
4616 */ |
|
4617 if ((ns == NULL) || (ns->nodeNr <= 0) ) |
|
4618 return (0); |
|
4619 hash = xmlXPathStringHash(str); |
|
4620 for (i = 0; i < ns->nodeNr; i++) { |
|
4621 if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) { |
|
4622 str2 = xmlNodeGetContent(ns->nodeTab[i]); |
|
4623 if ((str2 != NULL) && (xmlStrEqual(str, str2))) { |
|
4624 xmlFree(str2); |
|
4625 if (neq) |
|
4626 continue; |
|
4627 return (1); |
|
4628 } else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) { |
|
4629 if (neq) |
|
4630 continue; |
|
4631 return (1); |
|
4632 } else if (neq) { |
|
4633 if (str2 != NULL) |
|
4634 xmlFree(str2); |
|
4635 return (1); |
|
4636 } |
|
4637 if (str2 != NULL) |
|
4638 xmlFree(str2); |
|
4639 } else if (neq) |
|
4640 return (1); |
|
4641 } |
|
4642 return (0); |
|
4643 } |
|
4644 |
|
4645 /** |
|
4646 * xmlXPathEqualNodeSetFloat: |
|
4647 * @arg: the nodeset object argument |
|
4648 * @f: the float to compare to |
|
4649 * @neq: flag to show whether to compare '=' (0) or '!=' (1) |
|
4650 * |
|
4651 * Implement the equal operation on XPath objects content: @arg1 == @arg2 |
|
4652 * If one object to be compared is a node-set and the other is a number, |
|
4653 * then the comparison will be true if and only if there is a node in |
|
4654 * the node-set such that the result of performing the comparison on the |
|
4655 * number to be compared and on the result of converting the string-value |
|
4656 * of that node to a number using the number function is true. |
|
4657 * |
|
4658 * Returns 0 or 1 depending on the results of the test. |
|
4659 */ |
|
4660 static int |
|
4661 xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt, |
|
4662 xmlXPathObjectPtr arg, double f, int neq) { |
|
4663 int i, ret=0; |
|
4664 xmlNodeSetPtr ns; |
|
4665 xmlChar *str2; |
|
4666 xmlXPathObjectPtr val; |
|
4667 double v; |
|
4668 |
|
4669 if ((arg == NULL) || |
|
4670 ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) |
|
4671 return(0); |
|
4672 |
|
4673 ns = arg->nodesetval; |
|
4674 if (ns != NULL) { |
|
4675 for (i=0;i<ns->nodeNr;i++) { |
|
4676 str2 = xmlXPathCastNodeToString(ns->nodeTab[i]); |
|
4677 if (str2 != NULL) { |
|
4678 valuePush(ctxt, xmlXPathNewString(str2)); |
|
4679 xmlFree(str2); |
|
4680 xmlXPathNumberFunction(ctxt, 1); |
|
4681 val = valuePop(ctxt); |
|
4682 v = val->floatval; |
|
4683 xmlXPathFreeObject(val); |
|
4684 if (!xmlXPathIsNaN(v)) { |
|
4685 if ((!neq) && (v==f)) { |
|
4686 ret = 1; |
|
4687 break; |
|
4688 } else if ((neq) && (v!=f)) { |
|
4689 ret = 1; |
|
4690 break; |
|
4691 } |
|
4692 } |
|
4693 } |
|
4694 } |
|
4695 } |
|
4696 |
|
4697 return(ret); |
|
4698 } |
|
4699 |
|
4700 |
|
4701 /** |
|
4702 * xmlXPathEqualNodeSets: |
|
4703 * @arg1: first nodeset object argument |
|
4704 * @arg2: second nodeset object argument |
|
4705 * @neq: flag to show whether to test '=' (0) or '!=' (1) |
|
4706 * |
|
4707 * Implement the equal / not equal operation on XPath nodesets: |
|
4708 * @arg1 == @arg2 or @arg1 != @arg2 |
|
4709 * If both objects to be compared are node-sets, then the comparison |
|
4710 * will be true if and only if there is a node in the first node-set and |
|
4711 * a node in the second node-set such that the result of performing the |
|
4712 * comparison on the string-values of the two nodes is true. |
|
4713 * |
|
4714 * (needless to say, this is a costly operation) |
|
4715 * |
|
4716 * Returns 0 or 1 depending on the results of the test. |
|
4717 */ |
|
4718 static int |
|
4719 xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2, int neq) { |
|
4720 int i, j; |
|
4721 unsigned int *hashs1; |
|
4722 unsigned int *hashs2; |
|
4723 xmlChar **values1; |
|
4724 xmlChar **values2; |
|
4725 int ret = 0; |
|
4726 xmlNodeSetPtr ns1; |
|
4727 xmlNodeSetPtr ns2; |
|
4728 |
|
4729 if ((arg1 == NULL) || |
|
4730 ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) |
|
4731 return(0); |
|
4732 if ((arg2 == NULL) || |
|
4733 ((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) |
|
4734 return(0); |
|
4735 |
|
4736 ns1 = arg1->nodesetval; |
|
4737 ns2 = arg2->nodesetval; |
|
4738 |
|
4739 if ((ns1 == NULL) || (ns1->nodeNr <= 0)) |
|
4740 return(0); |
|
4741 if ((ns2 == NULL) || (ns2->nodeNr <= 0)) |
|
4742 return(0); |
|
4743 |
|
4744 /* |
|
4745 * for equal, check if there is a node pertaining to both sets |
|
4746 */ |
|
4747 if (neq == 0) |
|
4748 for (i = 0;i < ns1->nodeNr;i++) |
|
4749 for (j = 0;j < ns2->nodeNr;j++) |
|
4750 if (ns1->nodeTab[i] == ns2->nodeTab[j]) |
|
4751 return(1); |
|
4752 |
|
4753 values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *)); |
|
4754 if (values1 == NULL) { |
|
4755 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4756 return(0); |
|
4757 } |
|
4758 hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int)); |
|
4759 if (hashs1 == NULL) { |
|
4760 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4761 xmlFree(values1); |
|
4762 return(0); |
|
4763 } |
|
4764 memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *)); |
|
4765 values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *)); |
|
4766 if (values2 == NULL) { |
|
4767 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4768 xmlFree(hashs1); |
|
4769 xmlFree(values1); |
|
4770 return(0); |
|
4771 } |
|
4772 hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int)); |
|
4773 if (hashs2 == NULL) { |
|
4774 xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n")); |
|
4775 xmlFree(hashs1); |
|
4776 xmlFree(values1); |
|
4777 xmlFree(values2); |
|
4778 return(0); |
|
4779 } |
|
4780 memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *)); |
|
4781 for (i = 0;i < ns1->nodeNr;i++) { |
|
4782 hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]); |
|
4783 for (j = 0;j < ns2->nodeNr;j++) { |
|
4784 if (i == 0) |
|
4785 hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]); |
|
4786 if (hashs1[i] != hashs2[j]) { |
|
4787 if (neq) { |
|
4788 ret = 1; |
|
4789 break; |
|
4790 } |
|
4791 } |
|
4792 else { |
|
4793 if (values1[i] == NULL) |
|
4794 values1[i] = xmlNodeGetContent(ns1->nodeTab[i]); |
|
4795 if (values2[j] == NULL) |
|
4796 values2[j] = xmlNodeGetContent(ns2->nodeTab[j]); |
|
4797 ret = xmlStrEqual(values1[i], values2[j]) ^ neq; |
|
4798 if (ret) |
|
4799 break; |
|
4800 } |
|
4801 } |
|
4802 if (ret) |
|
4803 break; |
|
4804 } |
|
4805 for (i = 0;i < ns1->nodeNr;i++) |
|
4806 if (values1[i] != NULL) |
|
4807 xmlFree(values1[i]); |
|
4808 for (j = 0;j < ns2->nodeNr;j++) |
|
4809 if (values2[j] != NULL) |
|
4810 xmlFree(values2[j]); |
|
4811 xmlFree(values1); |
|
4812 xmlFree(values2); |
|
4813 xmlFree(hashs1); |
|
4814 xmlFree(hashs2); |
|
4815 return(ret); |
|
4816 } |
|
4817 |
|
4818 static int |
|
4819 xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt, |
|
4820 xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) { |
|
4821 int ret = 0; |
|
4822 /* |
|
4823 *At this point we are assured neither arg1 nor arg2 |
|
4824 *is a nodeset, so we can just pick the appropriate routine. |
|
4825 */ |
|
4826 switch (arg1->type) { |
|
4827 case XPATH_UNDEFINED: |
|
4828 #ifdef DEBUG_EXPR |
|
4829 xmlGenericError(xmlGenericErrorContext, |
|
4830 "Equal: undefined\n"); |
|
4831 #endif |
|
4832 break; |
|
4833 case XPATH_BOOLEAN: |
|
4834 switch (arg2->type) { |
|
4835 case XPATH_UNDEFINED: |
|
4836 #ifdef DEBUG_EXPR |
|
4837 xmlGenericError(xmlGenericErrorContext, |
|
4838 "Equal: undefined\n"); |
|
4839 #endif |
|
4840 break; |
|
4841 case XPATH_BOOLEAN: |
|
4842 #ifdef DEBUG_EXPR |
|
4843 xmlGenericError(xmlGenericErrorContext, |
|
4844 "Equal: %d boolean %d \n", |
|
4845 arg1->boolval, arg2->boolval); |
|
4846 #endif |
|
4847 ret = (arg1->boolval == arg2->boolval); |
|
4848 break; |
|
4849 case XPATH_NUMBER: |
|
4850 ret = (arg1->boolval == |
|
4851 xmlXPathCastNumberToBoolean(arg2->floatval)); |
|
4852 break; |
|
4853 case XPATH_STRING: |
|
4854 if ((arg2->stringval == NULL) || |
|
4855 (arg2->stringval[0] == 0)) ret = 0; |
|
4856 else |
|
4857 ret = 1; |
|
4858 ret = (arg1->boolval == ret); |
|
4859 break; |
|
4860 case XPATH_USERS: |
|
4861 case XPATH_POINT: |
|
4862 case XPATH_RANGE: |
|
4863 case XPATH_LOCATIONSET: |
|
4864 TODO |
|
4865 break; |
|
4866 case XPATH_NODESET: |
|
4867 case XPATH_XSLT_TREE: |
|
4868 break; |
|
4869 } |
|
4870 break; |
|
4871 case XPATH_NUMBER: |
|
4872 switch (arg2->type) { |
|
4873 case XPATH_UNDEFINED: |
|
4874 #ifdef DEBUG_EXPR |
|
4875 xmlGenericError(xmlGenericErrorContext, |
|
4876 "Equal: undefined\n"); |
|
4877 #endif |
|
4878 break; |
|
4879 case XPATH_BOOLEAN: |
|
4880 ret = (arg2->boolval== |
|
4881 xmlXPathCastNumberToBoolean(arg1->floatval)); |
|
4882 break; |
|
4883 case XPATH_STRING: |
|
4884 valuePush(ctxt, arg2); |
|
4885 xmlXPathNumberFunction(ctxt, 1); |
|
4886 arg2 = valuePop(ctxt); |
|
4887 /* no break on purpose */ |
|
4888 case XPATH_NUMBER: |
|
4889 /* Hand check NaN and Infinity equalities */ |
|
4890 if (xmlXPathIsNaN(arg1->floatval) || |
|
4891 xmlXPathIsNaN(arg2->floatval)) { |
|
4892 ret = 0; |
|
4893 } else if (xmlXPathIsInf(arg1->floatval) == 1) { |
|
4894 if (xmlXPathIsInf(arg2->floatval) == 1) |
|
4895 ret = 1; |
|
4896 else |
|
4897 ret = 0; |
|
4898 } else if (xmlXPathIsInf(arg1->floatval) == -1) { |
|
4899 if (xmlXPathIsInf(arg2->floatval) == -1) |
|
4900 ret = 1; |
|
4901 else |
|
4902 ret = 0; |
|
4903 } else if (xmlXPathIsInf(arg2->floatval) == 1) { |
|
4904 if (xmlXPathIsInf(arg1->floatval) == 1) |
|
4905 ret = 1; |
|
4906 else |
|
4907 ret = 0; |
|
4908 } else if (xmlXPathIsInf(arg2->floatval) == -1) { |
|
4909 if (xmlXPathIsInf(arg1->floatval) == -1) |
|
4910 ret = 1; |
|
4911 else |
|
4912 ret = 0; |
|
4913 } else { |
|
4914 ret = (arg1->floatval == arg2->floatval); |
|
4915 } |
|
4916 break; |
|
4917 case XPATH_USERS: |
|
4918 case XPATH_POINT: |
|
4919 case XPATH_RANGE: |
|
4920 case XPATH_LOCATIONSET: |
|
4921 TODO |
|
4922 break; |
|
4923 case XPATH_NODESET: |
|
4924 case XPATH_XSLT_TREE: |
|
4925 break; |
|
4926 } |
|
4927 break; |
|
4928 case XPATH_STRING: |
|
4929 switch (arg2->type) { |
|
4930 case XPATH_UNDEFINED: |
|
4931 #ifdef DEBUG_EXPR |
|
4932 xmlGenericError(xmlGenericErrorContext, |
|
4933 "Equal: undefined\n"); |
|
4934 #endif |
|
4935 break; |
|
4936 case XPATH_BOOLEAN: |
|
4937 if ((arg1->stringval == NULL) || |
|
4938 (arg1->stringval[0] == 0)) ret = 0; |
|
4939 else |
|
4940 ret = 1; |
|
4941 ret = (arg2->boolval == ret); |
|
4942 break; |
|
4943 case XPATH_STRING: |
|
4944 ret = xmlStrEqual(arg1->stringval, arg2->stringval); |
|
4945 break; |
|
4946 case XPATH_NUMBER: |
|
4947 valuePush(ctxt, arg1); |
|
4948 xmlXPathNumberFunction(ctxt, 1); |
|
4949 arg1 = valuePop(ctxt); |
|
4950 /* Hand check NaN and Infinity equalities */ |
|
4951 if (xmlXPathIsNaN(arg1->floatval) || |
|
4952 xmlXPathIsNaN(arg2->floatval)) { |
|
4953 ret = 0; |
|
4954 } else if (xmlXPathIsInf(arg1->floatval) == 1) { |
|
4955 if (xmlXPathIsInf(arg2->floatval) == 1) |
|
4956 ret = 1; |
|
4957 else |
|
4958 ret = 0; |
|
4959 } else if (xmlXPathIsInf(arg1->floatval) == -1) { |
|
4960 if (xmlXPathIsInf(arg2->floatval) == -1) |
|
4961 ret = 1; |
|
4962 else |
|
4963 ret = 0; |
|
4964 } else if (xmlXPathIsInf(arg2->floatval) == 1) { |
|
4965 if (xmlXPathIsInf(arg1->floatval) == 1) |
|
4966 ret = 1; |
|
4967 else |
|
4968 ret = 0; |
|
4969 } else if (xmlXPathIsInf(arg2->floatval) == -1) { |
|
4970 if (xmlXPathIsInf(arg1->floatval) == -1) |
|
4971 ret = 1; |
|
4972 else |
|
4973 ret = 0; |
|
4974 } else { |
|
4975 ret = (arg1->floatval == arg2->floatval); |
|
4976 } |
|
4977 break; |
|
4978 case XPATH_USERS: |
|
4979 case XPATH_POINT: |
|
4980 case XPATH_RANGE: |
|
4981 case XPATH_LOCATIONSET: |
|
4982 TODO |
|
4983 break; |
|
4984 case XPATH_NODESET: |
|
4985 case XPATH_XSLT_TREE: |
|
4986 break; |
|
4987 } |
|
4988 break; |
|
4989 case XPATH_USERS: |
|
4990 case XPATH_POINT: |
|
4991 case XPATH_RANGE: |
|
4992 case XPATH_LOCATIONSET: |
|
4993 TODO |
|
4994 break; |
|
4995 case XPATH_NODESET: |
|
4996 case XPATH_XSLT_TREE: |
|
4997 break; |
|
4998 } |
|
4999 xmlXPathFreeObject(arg1); |
|
5000 xmlXPathFreeObject(arg2); |
|
5001 return(ret); |
|
5002 } |
|
5003 |
|
5004 /** |
|
5005 * xmlXPathEqualValues: |
|
5006 * @ctxt: the XPath Parser context |
|
5007 * |
|
5008 * Implement the equal operation on XPath objects content: @arg1 == @arg2 |
|
5009 * |
|
5010 * Returns 0 or 1 depending on the results of the test. |
|
5011 */ |
|
5012 int |
|
5013 xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) { |
|
5014 xmlXPathObjectPtr arg1, arg2, argtmp; |
|
5015 int ret = 0; |
|
5016 |
|
5017 arg2 = valuePop(ctxt); |
|
5018 arg1 = valuePop(ctxt); |
|
5019 if ((arg1 == NULL) || (arg2 == NULL)) { |
|
5020 if (arg1 != NULL) |
|
5021 xmlXPathFreeObject(arg1); |
|
5022 else |
|
5023 xmlXPathFreeObject(arg2); |
|
5024 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5025 } |
|
5026 |
|
5027 if (arg1 == arg2) { |
|
5028 #ifdef DEBUG_EXPR |
|
5029 xmlGenericError(xmlGenericErrorContext, |
|
5030 "Equal: by pointer\n"); |
|
5031 #endif |
|
5032 return(1); |
|
5033 } |
|
5034 |
|
5035 /* |
|
5036 *If either argument is a nodeset, it's a 'special case' |
|
5037 */ |
|
5038 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || |
|
5039 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5040 /* |
|
5041 *Hack it to assure arg1 is the nodeset |
|
5042 */ |
|
5043 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) { |
|
5044 argtmp = arg2; |
|
5045 arg2 = arg1; |
|
5046 arg1 = argtmp; |
|
5047 } |
|
5048 switch (arg2->type) { |
|
5049 case XPATH_UNDEFINED: |
|
5050 #ifdef DEBUG_EXPR |
|
5051 xmlGenericError(xmlGenericErrorContext, |
|
5052 "Equal: undefined\n"); |
|
5053 #endif |
|
5054 break; |
|
5055 case XPATH_NODESET: |
|
5056 case XPATH_XSLT_TREE: |
|
5057 ret = xmlXPathEqualNodeSets(arg1, arg2, 0); |
|
5058 break; |
|
5059 case XPATH_BOOLEAN: |
|
5060 if ((arg1->nodesetval == NULL) || |
|
5061 (arg1->nodesetval->nodeNr == 0)) ret = 0; |
|
5062 else |
|
5063 ret = 1; |
|
5064 ret = (ret == arg2->boolval); |
|
5065 break; |
|
5066 case XPATH_NUMBER: |
|
5067 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 0); |
|
5068 break; |
|
5069 case XPATH_STRING: |
|
5070 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0); |
|
5071 break; |
|
5072 case XPATH_USERS: |
|
5073 case XPATH_POINT: |
|
5074 case XPATH_RANGE: |
|
5075 case XPATH_LOCATIONSET: |
|
5076 TODO |
|
5077 break; |
|
5078 } |
|
5079 xmlXPathFreeObject(arg1); |
|
5080 xmlXPathFreeObject(arg2); |
|
5081 return(ret); |
|
5082 } |
|
5083 |
|
5084 return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2)); |
|
5085 } |
|
5086 |
|
5087 /** |
|
5088 * xmlXPathNotEqualValues: |
|
5089 * @ctxt: the XPath Parser context |
|
5090 * |
|
5091 * Implement the equal operation on XPath objects content: @arg1 == @arg2 |
|
5092 * |
|
5093 * Returns 0 or 1 depending on the results of the test. |
|
5094 */ |
|
5095 int |
|
5096 xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) { |
|
5097 xmlXPathObjectPtr arg1, arg2, argtmp; |
|
5098 int ret = 0; |
|
5099 |
|
5100 arg2 = valuePop(ctxt); |
|
5101 arg1 = valuePop(ctxt); |
|
5102 if ((arg1 == NULL) || (arg2 == NULL)) { |
|
5103 if (arg1 != NULL) |
|
5104 xmlXPathFreeObject(arg1); |
|
5105 else |
|
5106 xmlXPathFreeObject(arg2); |
|
5107 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5108 } |
|
5109 |
|
5110 if (arg1 == arg2) { |
|
5111 #ifdef DEBUG_EXPR |
|
5112 xmlGenericError(xmlGenericErrorContext, |
|
5113 "NotEqual: by pointer\n"); |
|
5114 #endif |
|
5115 return(0); |
|
5116 } |
|
5117 |
|
5118 /* |
|
5119 *If either argument is a nodeset, it's a 'special case' |
|
5120 */ |
|
5121 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || |
|
5122 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5123 /* |
|
5124 *Hack it to assure arg1 is the nodeset |
|
5125 */ |
|
5126 if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) { |
|
5127 argtmp = arg2; |
|
5128 arg2 = arg1; |
|
5129 arg1 = argtmp; |
|
5130 } |
|
5131 switch (arg2->type) { |
|
5132 case XPATH_UNDEFINED: |
|
5133 #ifdef DEBUG_EXPR |
|
5134 xmlGenericError(xmlGenericErrorContext, |
|
5135 "NotEqual: undefined\n"); |
|
5136 #endif |
|
5137 break; |
|
5138 case XPATH_NODESET: |
|
5139 case XPATH_XSLT_TREE: |
|
5140 ret = xmlXPathEqualNodeSets(arg1, arg2, 1); |
|
5141 break; |
|
5142 case XPATH_BOOLEAN: |
|
5143 if ((arg1->nodesetval == NULL) || |
|
5144 (arg1->nodesetval->nodeNr == 0)) ret = 0; |
|
5145 else |
|
5146 ret = 1; |
|
5147 ret = (ret != arg2->boolval); |
|
5148 break; |
|
5149 case XPATH_NUMBER: |
|
5150 ret = xmlXPathEqualNodeSetFloat(ctxt, arg1, arg2->floatval, 1); |
|
5151 break; |
|
5152 case XPATH_STRING: |
|
5153 ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1); |
|
5154 break; |
|
5155 case XPATH_USERS: |
|
5156 case XPATH_POINT: |
|
5157 case XPATH_RANGE: |
|
5158 case XPATH_LOCATIONSET: |
|
5159 TODO |
|
5160 break; |
|
5161 } |
|
5162 xmlXPathFreeObject(arg1); |
|
5163 xmlXPathFreeObject(arg2); |
|
5164 return(ret); |
|
5165 } |
|
5166 |
|
5167 return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2)); |
|
5168 } |
|
5169 |
|
5170 /** |
|
5171 * xmlXPathCompareValues: |
|
5172 * @ctxt: the XPath Parser context |
|
5173 * @inf: less than (1) or greater than (0) |
|
5174 * @strict: is the comparison strict |
|
5175 * |
|
5176 * Implement the compare operation on XPath objects: |
|
5177 * @arg1 < @arg2 (1, 1, ... |
|
5178 * @arg1 <= @arg2 (1, 0, ... |
|
5179 * @arg1 > @arg2 (0, 1, ... |
|
5180 * @arg1 >= @arg2 (0, 0, ... |
|
5181 * |
|
5182 * When neither object to be compared is a node-set and the operator is |
|
5183 * <=, <, >=, >, then the objects are compared by converted both objects |
|
5184 * to numbers and comparing the numbers according to IEEE 754. The < |
|
5185 * comparison will be true if and only if the first number is less than the |
|
5186 * second number. The <= comparison will be true if and only if the first |
|
5187 * number is less than or equal to the second number. The > comparison |
|
5188 * will be true if and only if the first number is greater than the second |
|
5189 * number. The >= comparison will be true if and only if the first number |
|
5190 * is greater than or equal to the second number. |
|
5191 * |
|
5192 * Returns 1 if the comparison succeeded, 0 if it failed |
|
5193 */ |
|
5194 int |
|
5195 xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) { |
|
5196 int ret = 0, arg1i = 0, arg2i = 0; |
|
5197 xmlXPathObjectPtr arg1, arg2; |
|
5198 |
|
5199 arg2 = valuePop(ctxt); |
|
5200 arg1 = valuePop(ctxt); |
|
5201 if ((arg1 == NULL) || (arg2 == NULL)) { |
|
5202 if (arg1 != NULL) |
|
5203 xmlXPathFreeObject(arg1); |
|
5204 else |
|
5205 xmlXPathFreeObject(arg2); |
|
5206 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5207 } |
|
5208 |
|
5209 if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) || |
|
5210 (arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5211 if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) && |
|
5212 ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){ |
|
5213 ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2); |
|
5214 } else { |
|
5215 if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) { |
|
5216 ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict, |
|
5217 arg1, arg2); |
|
5218 } else { |
|
5219 ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict, |
|
5220 arg2, arg1); |
|
5221 } |
|
5222 } |
|
5223 return(ret); |
|
5224 } |
|
5225 |
|
5226 if (arg1->type != XPATH_NUMBER) { |
|
5227 valuePush(ctxt, arg1); |
|
5228 xmlXPathNumberFunction(ctxt, 1); |
|
5229 arg1 = valuePop(ctxt); |
|
5230 } |
|
5231 if (arg1->type != XPATH_NUMBER) { |
|
5232 xmlXPathFreeObject(arg1); |
|
5233 xmlXPathFreeObject(arg2); |
|
5234 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5235 } |
|
5236 if (arg2->type != XPATH_NUMBER) { |
|
5237 valuePush(ctxt, arg2); |
|
5238 xmlXPathNumberFunction(ctxt, 1); |
|
5239 arg2 = valuePop(ctxt); |
|
5240 } |
|
5241 if (arg2->type != XPATH_NUMBER) { |
|
5242 xmlXPathFreeObject(arg1); |
|
5243 xmlXPathFreeObject(arg2); |
|
5244 XP_ERROR0(XPATH_INVALID_OPERAND); |
|
5245 } |
|
5246 /* |
|
5247 * Add tests for infinity and nan |
|
5248 * => feedback on 3.4 for Inf and NaN |
|
5249 */ |
|
5250 /* Hand check NaN and Infinity comparisons */ |
|
5251 if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) { |
|
5252 ret=0; |
|
5253 } else { |
|
5254 arg1i=xmlXPathIsInf(arg1->floatval); |
|
5255 arg2i=xmlXPathIsInf(arg2->floatval); |
|
5256 if (inf && strict) { |
|
5257 if ((arg1i == -1 && arg2i != -1) || |
|
5258 (arg2i == 1 && arg1i != 1)) { |
|
5259 ret = 1; |
|
5260 } else if (arg1i == 0 && arg2i == 0) { |
|
5261 ret = (arg1->floatval < arg2->floatval); |
|
5262 } else { |
|
5263 ret = 0; |
|
5264 } |
|
5265 } |
|
5266 else if (inf && !strict) { |
|
5267 if (arg1i == -1 || arg2i == 1) { |
|
5268 ret = 1; |
|
5269 } else if (arg1i == 0 && arg2i == 0) { |
|
5270 ret = (arg1->floatval <= arg2->floatval); |
|
5271 } else { |
|
5272 ret = 0; |
|
5273 } |
|
5274 } |
|
5275 else if (!inf && strict) { |
|
5276 if ((arg1i == 1 && arg2i != 1) || |
|
5277 (arg2i == -1 && arg1i != -1)) { |
|
5278 ret = 1; |
|
5279 } else if (arg1i == 0 && arg2i == 0) { |
|
5280 ret = (arg1->floatval > arg2->floatval); |
|
5281 } else { |
|
5282 ret = 0; |
|
5283 } |
|
5284 } |
|
5285 else if (!inf && !strict) { |
|
5286 if (arg1i == 1 || arg2i == -1) { |
|
5287 ret = 1; |
|
5288 } else if (arg1i == 0 && arg2i == 0) { |
|
5289 ret = (arg1->floatval >= arg2->floatval); |
|
5290 } else { |
|
5291 ret = 0; |
|
5292 } |
|
5293 } |
|
5294 } |
|
5295 xmlXPathFreeObject(arg1); |
|
5296 xmlXPathFreeObject(arg2); |
|
5297 return(ret); |
|
5298 } |
|
5299 |
|
5300 /** |
|
5301 * xmlXPathValueFlipSign: |
|
5302 * @ctxt: the XPath Parser context |
|
5303 * |
|
5304 * Implement the unary - operation on an XPath object |
|
5305 * The numeric operators convert their operands to numbers as if |
|
5306 * by calling the number function. |
|
5307 */ |
|
5308 void |
|
5309 xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) { |
|
5310 CAST_TO_NUMBER; |
|
5311 CHECK_TYPE(XPATH_NUMBER); |
|
5312 if (xmlXPathIsNaN(ctxt->value->floatval)) |
|
5313 ctxt->value->floatval=xmlXPathNAN; |
|
5314 else if (xmlXPathIsInf(ctxt->value->floatval) == 1) |
|
5315 ctxt->value->floatval=xmlXPathNINF; |
|
5316 else if (xmlXPathIsInf(ctxt->value->floatval) == -1) |
|
5317 ctxt->value->floatval=xmlXPathPINF; |
|
5318 else if (ctxt->value->floatval == 0) { |
|
5319 if (xmlXPathGetSign(ctxt->value->floatval) == 0) |
|
5320 ctxt->value->floatval = xmlXPathNZERO; |
|
5321 else |
|
5322 ctxt->value->floatval = 0; |
|
5323 } |
|
5324 else |
|
5325 ctxt->value->floatval = - ctxt->value->floatval; |
|
5326 } |
|
5327 |
|
5328 /** |
|
5329 * xmlXPathAddValues: |
|
5330 * @ctxt: the XPath Parser context |
|
5331 * |
|
5332 * Implement the add operation on XPath objects: |
|
5333 * The numeric operators convert their operands to numbers as if |
|
5334 * by calling the number function. |
|
5335 */ |
|
5336 void |
|
5337 xmlXPathAddValues(xmlXPathParserContextPtr ctxt) { |
|
5338 xmlXPathObjectPtr arg; |
|
5339 double val; |
|
5340 |
|
5341 arg = valuePop(ctxt); |
|
5342 if (arg == NULL) |
|
5343 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5344 val = xmlXPathCastToNumber(arg); |
|
5345 xmlXPathFreeObject(arg); |
|
5346 |
|
5347 CAST_TO_NUMBER; |
|
5348 CHECK_TYPE(XPATH_NUMBER); |
|
5349 ctxt->value->floatval += val; |
|
5350 } |
|
5351 |
|
5352 /** |
|
5353 * xmlXPathSubValues: |
|
5354 * @ctxt: the XPath Parser context |
|
5355 * |
|
5356 * Implement the subtraction operation on XPath objects: |
|
5357 * The numeric operators convert their operands to numbers as if |
|
5358 * by calling the number function. |
|
5359 */ |
|
5360 void |
|
5361 xmlXPathSubValues(xmlXPathParserContextPtr ctxt) { |
|
5362 xmlXPathObjectPtr arg; |
|
5363 double val; |
|
5364 |
|
5365 arg = valuePop(ctxt); |
|
5366 if (arg == NULL) |
|
5367 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5368 val = xmlXPathCastToNumber(arg); |
|
5369 xmlXPathFreeObject(arg); |
|
5370 |
|
5371 CAST_TO_NUMBER; |
|
5372 CHECK_TYPE(XPATH_NUMBER); |
|
5373 ctxt->value->floatval -= val; |
|
5374 } |
|
5375 |
|
5376 /** |
|
5377 * xmlXPathMultValues: |
|
5378 * @ctxt: the XPath Parser context |
|
5379 * |
|
5380 * Implement the multiply operation on XPath objects: |
|
5381 * The numeric operators convert their operands to numbers as if |
|
5382 * by calling the number function. |
|
5383 */ |
|
5384 void |
|
5385 xmlXPathMultValues(xmlXPathParserContextPtr ctxt) { |
|
5386 xmlXPathObjectPtr arg; |
|
5387 double val; |
|
5388 |
|
5389 arg = valuePop(ctxt); |
|
5390 if (arg == NULL) |
|
5391 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5392 val = xmlXPathCastToNumber(arg); |
|
5393 xmlXPathFreeObject(arg); |
|
5394 |
|
5395 CAST_TO_NUMBER; |
|
5396 CHECK_TYPE(XPATH_NUMBER); |
|
5397 ctxt->value->floatval *= val; |
|
5398 } |
|
5399 |
|
5400 /** |
|
5401 * xmlXPathDivValues: |
|
5402 * @ctxt: the XPath Parser context |
|
5403 * |
|
5404 * Implement the div operation on XPath objects @arg1 / @arg2: |
|
5405 * The numeric operators convert their operands to numbers as if |
|
5406 * by calling the number function. |
|
5407 */ |
|
5408 void |
|
5409 xmlXPathDivValues(xmlXPathParserContextPtr ctxt) { |
|
5410 xmlXPathObjectPtr arg; |
|
5411 double val; |
|
5412 |
|
5413 arg = valuePop(ctxt); |
|
5414 if (arg == NULL) |
|
5415 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5416 val = xmlXPathCastToNumber(arg); |
|
5417 xmlXPathFreeObject(arg); |
|
5418 |
|
5419 CAST_TO_NUMBER; |
|
5420 CHECK_TYPE(XPATH_NUMBER); |
|
5421 if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval)) |
|
5422 ctxt->value->floatval = xmlXPathNAN; |
|
5423 else if (val == 0 && xmlXPathGetSign(val) != 0) { |
|
5424 if (ctxt->value->floatval == 0) |
|
5425 ctxt->value->floatval = xmlXPathNAN; |
|
5426 else if (ctxt->value->floatval > 0) |
|
5427 ctxt->value->floatval = xmlXPathNINF; |
|
5428 else if (ctxt->value->floatval < 0) |
|
5429 ctxt->value->floatval = xmlXPathPINF; |
|
5430 } |
|
5431 else if (val == 0) { |
|
5432 if (ctxt->value->floatval == 0) |
|
5433 ctxt->value->floatval = xmlXPathNAN; |
|
5434 else if (ctxt->value->floatval > 0) |
|
5435 ctxt->value->floatval = xmlXPathPINF; |
|
5436 else if (ctxt->value->floatval < 0) |
|
5437 ctxt->value->floatval = xmlXPathNINF; |
|
5438 } else |
|
5439 ctxt->value->floatval /= val; |
|
5440 } |
|
5441 |
|
5442 /** |
|
5443 * xmlXPathModValues: |
|
5444 * @ctxt: the XPath Parser context |
|
5445 * |
|
5446 * Implement the mod operation on XPath objects: @arg1 / @arg2 |
|
5447 * The numeric operators convert their operands to numbers as if |
|
5448 * by calling the number function. |
|
5449 */ |
|
5450 void |
|
5451 xmlXPathModValues(xmlXPathParserContextPtr ctxt) { |
|
5452 xmlXPathObjectPtr arg; |
|
5453 double arg1, arg2; |
|
5454 |
|
5455 arg = valuePop(ctxt); |
|
5456 if (arg == NULL) |
|
5457 XP_ERROR(XPATH_INVALID_OPERAND); |
|
5458 arg2 = xmlXPathCastToNumber(arg); |
|
5459 xmlXPathFreeObject(arg); |
|
5460 |
|
5461 CAST_TO_NUMBER; |
|
5462 CHECK_TYPE(XPATH_NUMBER); |
|
5463 arg1 = ctxt->value->floatval; |
|
5464 if (arg2 == 0) |
|
5465 ctxt->value->floatval = xmlXPathNAN; |
|
5466 else { |
|
5467 ctxt->value->floatval = fmod(arg1, arg2); |
|
5468 } |
|
5469 } |
|
5470 |
|
5471 /************************************************************************ |
|
5472 * * |
|
5473 * The traversal functions * |
|
5474 * * |
|
5475 ************************************************************************/ |
|
5476 |
|
5477 /* |
|
5478 * A traversal function enumerates nodes along an axis. |
|
5479 * Initially it must be called with NULL, and it indicates |
|
5480 * termination on the axis by returning NULL. |
|
5481 */ |
|
5482 typedef xmlNodePtr (*xmlXPathTraversalFunction) |
|
5483 (xmlXPathParserContextPtr ctxt, xmlNodePtr cur); |
|
5484 |
|
5485 /** |
|
5486 * xmlXPathNextSelf: |
|
5487 * @ctxt: the XPath Parser context |
|
5488 * @cur: the current node in the traversal |
|
5489 * |
|
5490 * Traversal function for the "self" direction |
|
5491 * The self axis contains just the context node itself |
|
5492 * |
|
5493 * Returns the next element following that axis |
|
5494 */ |
|
5495 xmlNodePtr |
|
5496 xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5497 if (cur == NULL) |
|
5498 return(ctxt->context->node); |
|
5499 return(NULL); |
|
5500 } |
|
5501 |
|
5502 /** |
|
5503 * xmlXPathNextChild: |
|
5504 * @ctxt: the XPath Parser context |
|
5505 * @cur: the current node in the traversal |
|
5506 * |
|
5507 * Traversal function for the "child" direction |
|
5508 * The child axis contains the children of the context node in document order. |
|
5509 * |
|
5510 * Returns the next element following that axis |
|
5511 */ |
|
5512 xmlNodePtr |
|
5513 xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5514 if (cur == NULL) { |
|
5515 if (ctxt->context->node == NULL) return(NULL); |
|
5516 switch (ctxt->context->node->type) { |
|
5517 case XML_ELEMENT_NODE: |
|
5518 case XML_TEXT_NODE: |
|
5519 case XML_CDATA_SECTION_NODE: |
|
5520 case XML_ENTITY_REF_NODE: |
|
5521 case XML_ENTITY_NODE: |
|
5522 case XML_PI_NODE: |
|
5523 case XML_COMMENT_NODE: |
|
5524 case XML_NOTATION_NODE: |
|
5525 case XML_DTD_NODE: |
|
5526 return(ctxt->context->node->children); |
|
5527 case XML_DOCUMENT_NODE: |
|
5528 case XML_DOCUMENT_TYPE_NODE: |
|
5529 case XML_DOCUMENT_FRAG_NODE: |
|
5530 case XML_HTML_DOCUMENT_NODE: |
|
5531 #ifdef LIBXML_DOCB_ENABLED |
|
5532 case XML_DOCB_DOCUMENT_NODE: |
|
5533 #endif |
|
5534 return(((xmlDocPtr) ctxt->context->node)->children); |
|
5535 case XML_ELEMENT_DECL: |
|
5536 case XML_ATTRIBUTE_DECL: |
|
5537 case XML_ENTITY_DECL: |
|
5538 case XML_ATTRIBUTE_NODE: |
|
5539 case XML_NAMESPACE_DECL: |
|
5540 case XML_XINCLUDE_START: |
|
5541 case XML_XINCLUDE_END: |
|
5542 return(NULL); |
|
5543 } |
|
5544 return(NULL); |
|
5545 } |
|
5546 if ((cur->type == XML_DOCUMENT_NODE) || |
|
5547 (cur->type == XML_HTML_DOCUMENT_NODE)) |
|
5548 return(NULL); |
|
5549 return(cur->next); |
|
5550 } |
|
5551 |
|
5552 /** |
|
5553 * xmlXPathNextDescendant: |
|
5554 * @ctxt: the XPath Parser context |
|
5555 * @cur: the current node in the traversal |
|
5556 * |
|
5557 * Traversal function for the "descendant" direction |
|
5558 * the descendant axis contains the descendants of the context node in document |
|
5559 * order; a descendant is a child or a child of a child and so on. |
|
5560 * |
|
5561 * Returns the next element following that axis |
|
5562 */ |
|
5563 xmlNodePtr |
|
5564 xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5565 if (cur == NULL) { |
|
5566 if (ctxt->context->node == NULL) |
|
5567 return(NULL); |
|
5568 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5569 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5570 return(NULL); |
|
5571 |
|
5572 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) |
|
5573 return(ctxt->context->doc->children); |
|
5574 return(ctxt->context->node->children); |
|
5575 } |
|
5576 |
|
5577 if (cur->children != NULL) { |
|
5578 /* |
|
5579 * Do not descend on entities declarations |
|
5580 */ |
|
5581 if (cur->children->type != XML_ENTITY_DECL) { |
|
5582 cur = cur->children; |
|
5583 /* |
|
5584 * Skip DTDs |
|
5585 */ |
|
5586 if (cur->type != XML_DTD_NODE) |
|
5587 return(cur); |
|
5588 } |
|
5589 } |
|
5590 |
|
5591 if (cur == ctxt->context->node) return(NULL); |
|
5592 |
|
5593 while (cur->next != NULL) { |
|
5594 cur = cur->next; |
|
5595 if ((cur->type != XML_ENTITY_DECL) && |
|
5596 (cur->type != XML_DTD_NODE)) |
|
5597 return(cur); |
|
5598 } |
|
5599 |
|
5600 do { |
|
5601 cur = cur->parent; |
|
5602 if (cur == NULL) return(NULL); |
|
5603 if (cur == ctxt->context->node) return(NULL); |
|
5604 if (cur->next != NULL) { |
|
5605 cur = cur->next; |
|
5606 return(cur); |
|
5607 } |
|
5608 } while (cur != NULL); |
|
5609 return(cur); |
|
5610 } |
|
5611 |
|
5612 /** |
|
5613 * xmlXPathNextDescendantOrSelf: |
|
5614 * @ctxt: the XPath Parser context |
|
5615 * @cur: the current node in the traversal |
|
5616 * |
|
5617 * Traversal function for the "descendant-or-self" direction |
|
5618 * the descendant-or-self axis contains the context node and the descendants |
|
5619 * of the context node in document order; thus the context node is the first |
|
5620 * node on the axis, and the first child of the context node is the second node |
|
5621 * on the axis |
|
5622 * |
|
5623 * Returns the next element following that axis |
|
5624 */ |
|
5625 xmlNodePtr |
|
5626 xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5627 if (cur == NULL) { |
|
5628 if (ctxt->context->node == NULL) |
|
5629 return(NULL); |
|
5630 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5631 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5632 return(NULL); |
|
5633 return(ctxt->context->node); |
|
5634 } |
|
5635 |
|
5636 return(xmlXPathNextDescendant(ctxt, cur)); |
|
5637 } |
|
5638 |
|
5639 /** |
|
5640 * xmlXPathNextParent: |
|
5641 * @ctxt: the XPath Parser context |
|
5642 * @cur: the current node in the traversal |
|
5643 * |
|
5644 * Traversal function for the "parent" direction |
|
5645 * The parent axis contains the parent of the context node, if there is one. |
|
5646 * |
|
5647 * Returns the next element following that axis |
|
5648 */ |
|
5649 xmlNodePtr |
|
5650 xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5651 /* |
|
5652 * the parent of an attribute or namespace node is the element |
|
5653 * to which the attribute or namespace node is attached |
|
5654 * Namespace handling !!! |
|
5655 */ |
|
5656 if (cur == NULL) { |
|
5657 if (ctxt->context->node == NULL) return(NULL); |
|
5658 switch (ctxt->context->node->type) { |
|
5659 case XML_ELEMENT_NODE: |
|
5660 case XML_TEXT_NODE: |
|
5661 case XML_CDATA_SECTION_NODE: |
|
5662 case XML_ENTITY_REF_NODE: |
|
5663 case XML_ENTITY_NODE: |
|
5664 case XML_PI_NODE: |
|
5665 case XML_COMMENT_NODE: |
|
5666 case XML_NOTATION_NODE: |
|
5667 case XML_DTD_NODE: |
|
5668 case XML_ELEMENT_DECL: |
|
5669 case XML_ATTRIBUTE_DECL: |
|
5670 case XML_XINCLUDE_START: |
|
5671 case XML_XINCLUDE_END: |
|
5672 case XML_ENTITY_DECL: |
|
5673 if (ctxt->context->node->parent == NULL) |
|
5674 return((xmlNodePtr) ctxt->context->doc); |
|
5675 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) && |
|
5676 ((ctxt->context->node->parent->name[0] == ' ') || |
|
5677 (xmlStrEqual(ctxt->context->node->parent->name, |
|
5678 BAD_CAST "fake node libxslt")))) |
|
5679 return(NULL); |
|
5680 return(ctxt->context->node->parent); |
|
5681 case XML_ATTRIBUTE_NODE: { |
|
5682 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; |
|
5683 |
|
5684 return(att->parent); |
|
5685 } |
|
5686 case XML_DOCUMENT_NODE: |
|
5687 case XML_DOCUMENT_TYPE_NODE: |
|
5688 case XML_DOCUMENT_FRAG_NODE: |
|
5689 case XML_HTML_DOCUMENT_NODE: |
|
5690 #ifdef LIBXML_DOCB_ENABLED |
|
5691 case XML_DOCB_DOCUMENT_NODE: |
|
5692 #endif |
|
5693 return(NULL); |
|
5694 case XML_NAMESPACE_DECL: { |
|
5695 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; |
|
5696 |
|
5697 if ((ns->next != NULL) && |
|
5698 (ns->next->type != XML_NAMESPACE_DECL)) |
|
5699 return((xmlNodePtr) ns->next); |
|
5700 return(NULL); |
|
5701 } |
|
5702 } |
|
5703 } |
|
5704 return(NULL); |
|
5705 } |
|
5706 |
|
5707 /** |
|
5708 * xmlXPathNextAncestor: |
|
5709 * @ctxt: the XPath Parser context |
|
5710 * @cur: the current node in the traversal |
|
5711 * |
|
5712 * Traversal function for the "ancestor" direction |
|
5713 * the ancestor axis contains the ancestors of the context node; the ancestors |
|
5714 * of the context node consist of the parent of context node and the parent's |
|
5715 * parent and so on; the nodes are ordered in reverse document order; thus the |
|
5716 * parent is the first node on the axis, and the parent's parent is the second |
|
5717 * node on the axis |
|
5718 * |
|
5719 * Returns the next element following that axis |
|
5720 */ |
|
5721 xmlNodePtr |
|
5722 xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5723 /* |
|
5724 * the parent of an attribute or namespace node is the element |
|
5725 * to which the attribute or namespace node is attached |
|
5726 * !!!!!!!!!!!!! |
|
5727 */ |
|
5728 if (cur == NULL) { |
|
5729 if (ctxt->context->node == NULL) return(NULL); |
|
5730 switch (ctxt->context->node->type) { |
|
5731 case XML_ELEMENT_NODE: |
|
5732 case XML_TEXT_NODE: |
|
5733 case XML_CDATA_SECTION_NODE: |
|
5734 case XML_ENTITY_REF_NODE: |
|
5735 case XML_ENTITY_NODE: |
|
5736 case XML_PI_NODE: |
|
5737 case XML_COMMENT_NODE: |
|
5738 case XML_DTD_NODE: |
|
5739 case XML_ELEMENT_DECL: |
|
5740 case XML_ATTRIBUTE_DECL: |
|
5741 case XML_ENTITY_DECL: |
|
5742 case XML_NOTATION_NODE: |
|
5743 case XML_XINCLUDE_START: |
|
5744 case XML_XINCLUDE_END: |
|
5745 if (ctxt->context->node->parent == NULL) |
|
5746 return((xmlNodePtr) ctxt->context->doc); |
|
5747 if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) && |
|
5748 ((ctxt->context->node->parent->name[0] == ' ') || |
|
5749 (xmlStrEqual(ctxt->context->node->parent->name, |
|
5750 BAD_CAST "fake node libxslt")))) |
|
5751 return(NULL); |
|
5752 return(ctxt->context->node->parent); |
|
5753 case XML_ATTRIBUTE_NODE: { |
|
5754 xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node; |
|
5755 |
|
5756 return(tmp->parent); |
|
5757 } |
|
5758 case XML_DOCUMENT_NODE: |
|
5759 case XML_DOCUMENT_TYPE_NODE: |
|
5760 case XML_DOCUMENT_FRAG_NODE: |
|
5761 case XML_HTML_DOCUMENT_NODE: |
|
5762 #ifdef LIBXML_DOCB_ENABLED |
|
5763 case XML_DOCB_DOCUMENT_NODE: |
|
5764 #endif |
|
5765 return(NULL); |
|
5766 case XML_NAMESPACE_DECL: { |
|
5767 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; |
|
5768 |
|
5769 if ((ns->next != NULL) && |
|
5770 (ns->next->type != XML_NAMESPACE_DECL)) |
|
5771 return((xmlNodePtr) ns->next); |
|
5772 /* Bad, how did that namespace end up here ? */ |
|
5773 return(NULL); |
|
5774 } |
|
5775 } |
|
5776 return(NULL); |
|
5777 } |
|
5778 if (cur == ctxt->context->doc->children) |
|
5779 return((xmlNodePtr) ctxt->context->doc); |
|
5780 if (cur == (xmlNodePtr) ctxt->context->doc) |
|
5781 return(NULL); |
|
5782 switch (cur->type) { |
|
5783 case XML_ELEMENT_NODE: |
|
5784 case XML_TEXT_NODE: |
|
5785 case XML_CDATA_SECTION_NODE: |
|
5786 case XML_ENTITY_REF_NODE: |
|
5787 case XML_ENTITY_NODE: |
|
5788 case XML_PI_NODE: |
|
5789 case XML_COMMENT_NODE: |
|
5790 case XML_NOTATION_NODE: |
|
5791 case XML_DTD_NODE: |
|
5792 case XML_ELEMENT_DECL: |
|
5793 case XML_ATTRIBUTE_DECL: |
|
5794 case XML_ENTITY_DECL: |
|
5795 case XML_XINCLUDE_START: |
|
5796 case XML_XINCLUDE_END: |
|
5797 if (cur->parent == NULL) |
|
5798 return(NULL); |
|
5799 if ((cur->parent->type == XML_ELEMENT_NODE) && |
|
5800 ((cur->parent->name[0] == ' ') || |
|
5801 (xmlStrEqual(cur->parent->name, |
|
5802 BAD_CAST "fake node libxslt")))) |
|
5803 return(NULL); |
|
5804 return(cur->parent); |
|
5805 case XML_ATTRIBUTE_NODE: { |
|
5806 xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node; |
|
5807 |
|
5808 return(att->parent); |
|
5809 } |
|
5810 case XML_NAMESPACE_DECL: { |
|
5811 xmlNsPtr ns = (xmlNsPtr) ctxt->context->node; |
|
5812 |
|
5813 if ((ns->next != NULL) && |
|
5814 (ns->next->type != XML_NAMESPACE_DECL)) |
|
5815 return((xmlNodePtr) ns->next); |
|
5816 /* Bad, how did that namespace end up here ? */ |
|
5817 return(NULL); |
|
5818 } |
|
5819 case XML_DOCUMENT_NODE: |
|
5820 case XML_DOCUMENT_TYPE_NODE: |
|
5821 case XML_DOCUMENT_FRAG_NODE: |
|
5822 case XML_HTML_DOCUMENT_NODE: |
|
5823 #ifdef LIBXML_DOCB_ENABLED |
|
5824 case XML_DOCB_DOCUMENT_NODE: |
|
5825 #endif |
|
5826 return(NULL); |
|
5827 } |
|
5828 return(NULL); |
|
5829 } |
|
5830 |
|
5831 /** |
|
5832 * xmlXPathNextAncestorOrSelf: |
|
5833 * @ctxt: the XPath Parser context |
|
5834 * @cur: the current node in the traversal |
|
5835 * |
|
5836 * Traversal function for the "ancestor-or-self" direction |
|
5837 * he ancestor-or-self axis contains the context node and ancestors of |
|
5838 * the context node in reverse document order; thus the context node is |
|
5839 * the first node on the axis, and the context node's parent the second; |
|
5840 * parent here is defined the same as with the parent axis. |
|
5841 * |
|
5842 * Returns the next element following that axis |
|
5843 */ |
|
5844 xmlNodePtr |
|
5845 xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5846 if (cur == NULL) |
|
5847 return(ctxt->context->node); |
|
5848 return(xmlXPathNextAncestor(ctxt, cur)); |
|
5849 } |
|
5850 |
|
5851 /** |
|
5852 * xmlXPathNextFollowingSibling: |
|
5853 * @ctxt: the XPath Parser context |
|
5854 * @cur: the current node in the traversal |
|
5855 * |
|
5856 * Traversal function for the "following-sibling" direction |
|
5857 * The following-sibling axis contains the following siblings of the context |
|
5858 * node in document order. |
|
5859 * |
|
5860 * Returns the next element following that axis |
|
5861 */ |
|
5862 xmlNodePtr |
|
5863 xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5864 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5865 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5866 return(NULL); |
|
5867 if (cur == (xmlNodePtr) ctxt->context->doc) |
|
5868 return(NULL); |
|
5869 if (cur == NULL) |
|
5870 return(ctxt->context->node->next); |
|
5871 return(cur->next); |
|
5872 } |
|
5873 |
|
5874 /** |
|
5875 * xmlXPathNextPrecedingSibling: |
|
5876 * @ctxt: the XPath Parser context |
|
5877 * @cur: the current node in the traversal |
|
5878 * |
|
5879 * Traversal function for the "preceding-sibling" direction |
|
5880 * The preceding-sibling axis contains the preceding siblings of the context |
|
5881 * node in reverse document order; the first preceding sibling is first on the |
|
5882 * axis; the sibling preceding that node is the second on the axis and so on. |
|
5883 * |
|
5884 * Returns the next element following that axis |
|
5885 */ |
|
5886 xmlNodePtr |
|
5887 xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5888 if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) || |
|
5889 (ctxt->context->node->type == XML_NAMESPACE_DECL)) |
|
5890 return(NULL); |
|
5891 if (cur == (xmlNodePtr) ctxt->context->doc) |
|
5892 return(NULL); |
|
5893 if (cur == NULL) |
|
5894 return(ctxt->context->node->prev); |
|
5895 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) { |
|
5896 cur = cur->prev; |
|
5897 if (cur == NULL) |
|
5898 return(ctxt->context->node->prev); |
|
5899 } |
|
5900 return(cur->prev); |
|
5901 } |
|
5902 |
|
5903 /** |
|
5904 * xmlXPathNextFollowing: |
|
5905 * @ctxt: the XPath Parser context |
|
5906 * @cur: the current node in the traversal |
|
5907 * |
|
5908 * Traversal function for the "following" direction |
|
5909 * The following axis contains all nodes in the same document as the context |
|
5910 * node that are after the context node in document order, excluding any |
|
5911 * descendants and excluding attribute nodes and namespace nodes; the nodes |
|
5912 * are ordered in document order |
|
5913 * |
|
5914 * Returns the next element following that axis |
|
5915 */ |
|
5916 xmlNodePtr |
|
5917 xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
5918 if (cur != NULL && cur->children != NULL) |
|
5919 return cur->children ; |
|
5920 if (cur == NULL) cur = ctxt->context->node; |
|
5921 if (cur == NULL) return(NULL) ; /* ERROR */ |
|
5922 if (cur->next != NULL) return(cur->next) ; |
|
5923 do { |
|
5924 cur = cur->parent; |
|
5925 if (cur == NULL) return(NULL); |
|
5926 if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL); |
|
5927 if (cur->next != NULL) return(cur->next); |
|
5928 } while (cur != NULL); |
|
5929 return(cur); |
|
5930 } |
|
5931 |
|
5932 /* |
|
5933 * xmlXPathIsAncestor: |
|
5934 * @ancestor: the ancestor node |
|
5935 * @node: the current node |
|
5936 * |
|
5937 * Check that @ancestor is a @node's ancestor |
|
5938 * |
|
5939 * returns 1 if @ancestor is a @node's ancestor, 0 otherwise. |
|
5940 */ |
|
5941 static int |
|
5942 xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) { |
|
5943 if ((ancestor == NULL) || (node == NULL)) return(0); |
|
5944 /* nodes need to be in the same document */ |
|
5945 if (ancestor->doc != node->doc) return(0); |
|
5946 /* avoid searching if ancestor or node is the root node */ |
|
5947 if (ancestor == (xmlNodePtr) node->doc) return(1); |
|
5948 if (node == (xmlNodePtr) ancestor->doc) return(0); |
|
5949 while (node->parent != NULL) { |
|
5950 if (node->parent == ancestor) |
|
5951 return(1); |
|
5952 node = node->parent; |
|
5953 } |
|
5954 return(0); |
|
5955 } |
|
5956 |
|
5957 /** |
|
5958 * xmlXPathNextPreceding: |
|
5959 * @ctxt: the XPath Parser context |
|
5960 * @cur: the current node in the traversal |
|
5961 * |
|
5962 * Traversal function for the "preceding" direction |
|
5963 * the preceding axis contains all nodes in the same document as the context |
|
5964 * node that are before the context node in document order, excluding any |
|
5965 * ancestors and excluding attribute nodes and namespace nodes; the nodes are |
|
5966 * ordered in reverse document order |
|
5967 * |
|
5968 * Returns the next element following that axis |
|
5969 */ |
|
5970 xmlNodePtr |
|
5971 xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) |
|
5972 { |
|
5973 if (cur == NULL) |
|
5974 cur = ctxt->context->node; |
|
5975 if (cur == NULL) |
|
5976 return (NULL); |
|
5977 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) |
|
5978 cur = cur->prev; |
|
5979 do { |
|
5980 if (cur->prev != NULL) { |
|
5981 for (cur = cur->prev; cur->last != NULL; cur = cur->last) ; |
|
5982 return (cur); |
|
5983 } |
|
5984 |
|
5985 cur = cur->parent; |
|
5986 if (cur == NULL) |
|
5987 return (NULL); |
|
5988 if (cur == ctxt->context->doc->children) |
|
5989 return (NULL); |
|
5990 } while (xmlXPathIsAncestor(cur, ctxt->context->node)); |
|
5991 return (cur); |
|
5992 } |
|
5993 |
|
5994 /** |
|
5995 * xmlXPathNextPrecedingInternal: |
|
5996 * @ctxt: the XPath Parser context |
|
5997 * @cur: the current node in the traversal |
|
5998 * |
|
5999 * Traversal function for the "preceding" direction |
|
6000 * the preceding axis contains all nodes in the same document as the context |
|
6001 * node that are before the context node in document order, excluding any |
|
6002 * ancestors and excluding attribute nodes and namespace nodes; the nodes are |
|
6003 * ordered in reverse document order |
|
6004 * This is a faster implementation but internal only since it requires a |
|
6005 * state kept in the parser context: ctxt->ancestor. |
|
6006 * |
|
6007 * Returns the next element following that axis |
|
6008 */ |
|
6009 static xmlNodePtr |
|
6010 xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt, |
|
6011 xmlNodePtr cur) |
|
6012 { |
|
6013 if (cur == NULL) { |
|
6014 cur = ctxt->context->node; |
|
6015 if (cur == NULL) |
|
6016 return (NULL); |
|
6017 if (cur->type == XML_NAMESPACE_DECL) |
|
6018 cur = (xmlNodePtr)((xmlNsPtr)cur)->next; |
|
6019 ctxt->ancestor = cur->parent; |
|
6020 } |
|
6021 if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) |
|
6022 cur = cur->prev; |
|
6023 while (cur->prev == NULL) { |
|
6024 cur = cur->parent; |
|
6025 if (cur == NULL) |
|
6026 return (NULL); |
|
6027 if (cur == ctxt->context->doc->children) |
|
6028 return (NULL); |
|
6029 if (cur != ctxt->ancestor) |
|
6030 return (cur); |
|
6031 ctxt->ancestor = cur->parent; |
|
6032 } |
|
6033 cur = cur->prev; |
|
6034 while (cur->last != NULL) |
|
6035 cur = cur->last; |
|
6036 return (cur); |
|
6037 } |
|
6038 |
|
6039 /** |
|
6040 * xmlXPathNextNamespace: |
|
6041 * @ctxt: the XPath Parser context |
|
6042 * @cur: the current attribute in the traversal |
|
6043 * |
|
6044 * Traversal function for the "namespace" direction |
|
6045 * the namespace axis contains the namespace nodes of the context node; |
|
6046 * the order of nodes on this axis is implementation-defined; the axis will |
|
6047 * be empty unless the context node is an element |
|
6048 * |
|
6049 * We keep the XML namespace node at the end of the list. |
|
6050 * |
|
6051 * Returns the next element following that axis |
|
6052 */ |
|
6053 xmlNodePtr |
|
6054 xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
6055 if (ctxt->context->node->type != XML_ELEMENT_NODE) |
|
6056 return(NULL); |
|
6057 if (ctxt->context->tmpNsList == NULL && |
|
6058 cur != (xmlNodePtr) xmlXPathXMLNamespace) |
|
6059 { |
|
6060 if (ctxt->context->tmpNsList != NULL) |
|
6061 xmlFree(ctxt->context->tmpNsList); |
|
6062 ctxt->context->tmpNsList = |
|
6063 xmlGetNsList(ctxt->context->doc, ctxt->context->node); |
|
6064 ctxt->context->tmpNsNr = 0; |
|
6065 if (ctxt->context->tmpNsList != NULL) { |
|
6066 while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) { |
|
6067 ctxt->context->tmpNsNr++; |
|
6068 } |
|
6069 } |
|
6070 return((xmlNodePtr) xmlXPathXMLNamespace); |
|
6071 } |
|
6072 if (ctxt->context->tmpNsNr > 0) { |
|
6073 return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr]; |
|
6074 } else { |
|
6075 if (ctxt->context->tmpNsList != NULL) |
|
6076 xmlFree(ctxt->context->tmpNsList); |
|
6077 ctxt->context->tmpNsList = NULL; |
|
6078 return(NULL); |
|
6079 } |
|
6080 } |
|
6081 |
|
6082 /** |
|
6083 * xmlXPathNextAttribute: |
|
6084 * @ctxt: the XPath Parser context |
|
6085 * @cur: the current attribute in the traversal |
|
6086 * |
|
6087 * Traversal function for the "attribute" direction |
|
6088 * TODO: support DTD inherited default attributes |
|
6089 * |
|
6090 * Returns the next element following that axis |
|
6091 */ |
|
6092 xmlNodePtr |
|
6093 xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
|
6094 if (ctxt->context->node == NULL) |
|
6095 return(NULL); |
|
6096 if (ctxt->context->node->type != XML_ELEMENT_NODE) |
|
6097 return(NULL); |
|
6098 if (cur == NULL) { |
|
6099 if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc) |
|
6100 return(NULL); |
|
6101 return((xmlNodePtr)ctxt->context->node->properties); |
|
6102 } |
|
6103 return((xmlNodePtr)cur->next); |
|
6104 } |
|
6105 |
|
6106 /************************************************************************ |
|
6107 * * |
|
6108 * NodeTest Functions * |
|
6109 * * |
|
6110 ************************************************************************/ |
|
6111 |
|
6112 #define IS_FUNCTION 200 |
|
6113 |
|
6114 |
|
6115 /************************************************************************ |
|
6116 * * |
|
6117 * Implicit tree core function library * |
|
6118 * * |
|
6119 ************************************************************************/ |
|
6120 |
|
6121 /** |
|
6122 * xmlXPathRoot: |
|
6123 * @ctxt: the XPath Parser context |
|
6124 * |
|
6125 * Initialize the context to the root of the document |
|
6126 */ |
|
6127 void |
|
6128 xmlXPathRoot(xmlXPathParserContextPtr ctxt) { |
|
6129 ctxt->context->node = (xmlNodePtr) ctxt->context->doc; |
|
6130 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6131 } |
|
6132 |
|
6133 /************************************************************************ |
|
6134 * * |
|
6135 * The explicit core function library * |
|
6136 *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib * |
|
6137 * * |
|
6138 ************************************************************************/ |
|
6139 |
|
6140 |
|
6141 /** |
|
6142 * xmlXPathLastFunction: |
|
6143 * @ctxt: the XPath Parser context |
|
6144 * @nargs: the number of arguments |
|
6145 * |
|
6146 * Implement the last() XPath function |
|
6147 * number last() |
|
6148 * The last function returns the number of nodes in the context node list. |
|
6149 */ |
|
6150 void |
|
6151 xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6152 CHECK_ARITY(0); |
|
6153 if (ctxt->context->contextSize >= 0) { |
|
6154 valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize)); |
|
6155 #ifdef DEBUG_EXPR |
|
6156 xmlGenericError(xmlGenericErrorContext, |
|
6157 "last() : %d\n", ctxt->context->contextSize); |
|
6158 #endif |
|
6159 } else { |
|
6160 XP_ERROR(XPATH_INVALID_CTXT_SIZE); |
|
6161 } |
|
6162 } |
|
6163 |
|
6164 /** |
|
6165 * xmlXPathPositionFunction: |
|
6166 * @ctxt: the XPath Parser context |
|
6167 * @nargs: the number of arguments |
|
6168 * |
|
6169 * Implement the position() XPath function |
|
6170 * number position() |
|
6171 * The position function returns the position of the context node in the |
|
6172 * context node list. The first position is 1, and so the last position |
|
6173 * will be equal to last(). |
|
6174 */ |
|
6175 void |
|
6176 xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6177 CHECK_ARITY(0); |
|
6178 if (ctxt->context->proximityPosition >= 0) { |
|
6179 valuePush(ctxt, |
|
6180 xmlXPathNewFloat((double) ctxt->context->proximityPosition)); |
|
6181 #ifdef DEBUG_EXPR |
|
6182 xmlGenericError(xmlGenericErrorContext, "position() : %d\n", |
|
6183 ctxt->context->proximityPosition); |
|
6184 #endif |
|
6185 } else { |
|
6186 XP_ERROR(XPATH_INVALID_CTXT_POSITION); |
|
6187 } |
|
6188 } |
|
6189 |
|
6190 /** |
|
6191 * xmlXPathCountFunction: |
|
6192 * @ctxt: the XPath Parser context |
|
6193 * @nargs: the number of arguments |
|
6194 * |
|
6195 * Implement the count() XPath function |
|
6196 * number count(node-set) |
|
6197 */ |
|
6198 void |
|
6199 xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6200 xmlXPathObjectPtr cur; |
|
6201 |
|
6202 CHECK_ARITY(1); |
|
6203 if ((ctxt->value == NULL) || |
|
6204 ((ctxt->value->type != XPATH_NODESET) && |
|
6205 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6206 XP_ERROR(XPATH_INVALID_TYPE); |
|
6207 cur = valuePop(ctxt); |
|
6208 |
|
6209 if ((cur == NULL) || (cur->nodesetval == NULL)) |
|
6210 valuePush(ctxt, xmlXPathNewFloat((double) 0)); |
|
6211 else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) { |
|
6212 valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr)); |
|
6213 } else { |
|
6214 if ((cur->nodesetval->nodeNr != 1) || |
|
6215 (cur->nodesetval->nodeTab == NULL)) { |
|
6216 valuePush(ctxt, xmlXPathNewFloat((double) 0)); |
|
6217 } else { |
|
6218 xmlNodePtr tmp; |
|
6219 int i = 0; |
|
6220 |
|
6221 tmp = cur->nodesetval->nodeTab[0]; |
|
6222 if (tmp != NULL) { |
|
6223 tmp = tmp->children; |
|
6224 while (tmp != NULL) { |
|
6225 tmp = tmp->next; |
|
6226 i++; |
|
6227 } |
|
6228 } |
|
6229 valuePush(ctxt, xmlXPathNewFloat((double) i)); |
|
6230 } |
|
6231 } |
|
6232 xmlXPathFreeObject(cur); |
|
6233 } |
|
6234 |
|
6235 /** |
|
6236 * xmlXPathGetElementsByIds: |
|
6237 * @doc: the document |
|
6238 * @ids: a whitespace separated list of IDs |
|
6239 * |
|
6240 * Selects elements by their unique ID. |
|
6241 * |
|
6242 * Returns a node-set of selected elements. |
|
6243 */ |
|
6244 static xmlNodeSetPtr |
|
6245 xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) { |
|
6246 xmlNodeSetPtr ret; |
|
6247 const xmlChar *cur = ids; |
|
6248 xmlChar *ID; |
|
6249 xmlAttrPtr attr; |
|
6250 xmlNodePtr elem = NULL; |
|
6251 |
|
6252 if (ids == NULL) return(NULL); |
|
6253 |
|
6254 ret = xmlXPathNodeSetCreate(NULL); |
|
6255 |
|
6256 if ( !ret ) |
|
6257 return NULL; |
|
6258 while (IS_BLANK_CH(*cur)) cur++; |
|
6259 while (*cur != 0) { |
|
6260 while ((!IS_BLANK_CH(*cur)) && (*cur != 0)) |
|
6261 cur++; |
|
6262 |
|
6263 ID = xmlStrndup(ids, cur - ids); |
|
6264 if (ID != NULL) { |
|
6265 /* |
|
6266 * We used to check the fact that the value passed |
|
6267 * was an NCName, but this generated much troubles for |
|
6268 * me and Aleksey Sanin, people blatantly violated that |
|
6269 * constaint, like Visa3D spec. |
|
6270 * if (xmlValidateNCName(ID, 1) == 0) |
|
6271 */ |
|
6272 attr = xmlGetID(doc, ID); |
|
6273 if (attr != NULL) { |
|
6274 if (attr->type == XML_ATTRIBUTE_NODE) |
|
6275 elem = attr->parent; |
|
6276 else if (attr->type == XML_ELEMENT_NODE) |
|
6277 elem = (xmlNodePtr) attr; |
|
6278 else |
|
6279 elem = NULL; |
|
6280 if (elem != NULL) |
|
6281 xmlXPathNodeSetAdd(ret, elem); |
|
6282 } |
|
6283 xmlFree(ID); |
|
6284 } |
|
6285 |
|
6286 while (IS_BLANK_CH(*cur)) cur++; |
|
6287 ids = cur; |
|
6288 } |
|
6289 return(ret); |
|
6290 } |
|
6291 |
|
6292 /** |
|
6293 * xmlXPathIdFunction: |
|
6294 * @ctxt: the XPath Parser context |
|
6295 * @nargs: the number of arguments |
|
6296 * |
|
6297 * Implement the id() XPath function |
|
6298 * node-set id(object) |
|
6299 * The id function selects elements by their unique ID |
|
6300 * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set, |
|
6301 * then the result is the union of the result of applying id to the |
|
6302 * string value of each of the nodes in the argument node-set. When the |
|
6303 * argument to id is of any other type, the argument is converted to a |
|
6304 * string as if by a call to the string function; the string is split |
|
6305 * into a whitespace-separated list of tokens (whitespace is any sequence |
|
6306 * of characters matching the production S); the result is a node-set |
|
6307 * containing the elements in the same document as the context node that |
|
6308 * have a unique ID equal to any of the tokens in the list. |
|
6309 */ |
|
6310 void |
|
6311 xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6312 xmlChar *tokens; |
|
6313 xmlNodeSetPtr ret; |
|
6314 xmlXPathObjectPtr obj; |
|
6315 xmlXPathObjectPtr wrappedNodeSet; |
|
6316 |
|
6317 CHECK_ARITY(1); |
|
6318 obj = valuePop(ctxt); |
|
6319 if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND); |
|
6320 if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) { |
|
6321 xmlNodeSetPtr ns; |
|
6322 int i; |
|
6323 |
|
6324 ret = xmlXPathNodeSetCreate(NULL); |
|
6325 |
|
6326 if (obj->nodesetval != NULL) { |
|
6327 for (i = 0; i < obj->nodesetval->nodeNr; i++) { |
|
6328 tokens = |
|
6329 xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]); |
|
6330 ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens); |
|
6331 ret = xmlXPathNodeSetMerge(ret, ns); |
|
6332 xmlXPathFreeNodeSet(ns); |
|
6333 if (tokens != NULL) |
|
6334 xmlFree(tokens); |
|
6335 } |
|
6336 } |
|
6337 |
|
6338 xmlXPathFreeObject(obj); |
|
6339 valuePush(ctxt, xmlXPathWrapNodeSet(ret)); |
|
6340 return; |
|
6341 } |
|
6342 obj = xmlXPathConvertString(obj); |
|
6343 |
|
6344 ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval); |
|
6345 wrappedNodeSet = xmlXPathWrapNodeSet(ret); |
|
6346 if ( !wrappedNodeSet ) |
|
6347 { |
|
6348 xmlXPathFreeNodeSet( ret ); |
|
6349 } |
|
6350 valuePush(ctxt, wrappedNodeSet); |
|
6351 |
|
6352 xmlXPathFreeObject(obj); |
|
6353 return; |
|
6354 } |
|
6355 |
|
6356 /** |
|
6357 * xmlXPathLocalNameFunction: |
|
6358 * @ctxt: the XPath Parser context |
|
6359 * @nargs: the number of arguments |
|
6360 * |
|
6361 * Implement the local-name() XPath function |
|
6362 * string local-name(node-set?) |
|
6363 * The local-name function returns a string containing the local part |
|
6364 * of the name of the node in the argument node-set that is first in |
|
6365 * document order. If the node-set is empty or the first node has no |
|
6366 * name, an empty string is returned. If the argument is omitted it |
|
6367 * defaults to the context node. |
|
6368 */ |
|
6369 void |
|
6370 xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6371 xmlXPathObjectPtr cur; |
|
6372 |
|
6373 if (nargs == 0) { |
|
6374 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6375 nargs = 1; |
|
6376 } |
|
6377 |
|
6378 CHECK_ARITY(1); |
|
6379 if ((ctxt->value == NULL) || |
|
6380 ((ctxt->value->type != XPATH_NODESET) && |
|
6381 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6382 XP_ERROR(XPATH_INVALID_TYPE); |
|
6383 cur = valuePop(ctxt); |
|
6384 |
|
6385 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { |
|
6386 valuePush(ctxt, xmlXPathNewCString("")); |
|
6387 } else { |
|
6388 int i = 0; /* Should be first in document order !!!!! */ |
|
6389 switch (cur->nodesetval->nodeTab[i]->type) { |
|
6390 case XML_ELEMENT_NODE: |
|
6391 case XML_ATTRIBUTE_NODE: |
|
6392 case XML_PI_NODE: |
|
6393 if (cur->nodesetval->nodeTab[i]->name[0] == ' ') |
|
6394 valuePush(ctxt, xmlXPathNewCString("")); |
|
6395 else |
|
6396 valuePush(ctxt, |
|
6397 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); |
|
6398 break; |
|
6399 case XML_NAMESPACE_DECL: |
|
6400 valuePush(ctxt, xmlXPathNewString( |
|
6401 ((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix)); |
|
6402 break; |
|
6403 default: |
|
6404 valuePush(ctxt, xmlXPathNewCString("")); |
|
6405 } |
|
6406 } |
|
6407 xmlXPathFreeObject(cur); |
|
6408 } |
|
6409 |
|
6410 /** |
|
6411 * xmlXPathNamespaceURIFunction: |
|
6412 * @ctxt: the XPath Parser context |
|
6413 * @nargs: the number of arguments |
|
6414 * |
|
6415 * Implement the namespace-uri() XPath function |
|
6416 * string namespace-uri(node-set?) |
|
6417 * The namespace-uri function returns a string containing the |
|
6418 * namespace URI of the expanded name of the node in the argument |
|
6419 * node-set that is first in document order. If the node-set is empty, |
|
6420 * the first node has no name, or the expanded name has no namespace |
|
6421 * URI, an empty string is returned. If the argument is omitted it |
|
6422 * defaults to the context node. |
|
6423 */ |
|
6424 void |
|
6425 xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6426 xmlXPathObjectPtr cur; |
|
6427 |
|
6428 if (nargs == 0) { |
|
6429 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6430 nargs = 1; |
|
6431 } |
|
6432 CHECK_ARITY(1); |
|
6433 if ((ctxt->value == NULL) || |
|
6434 ((ctxt->value->type != XPATH_NODESET) && |
|
6435 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6436 XP_ERROR(XPATH_INVALID_TYPE); |
|
6437 cur = valuePop(ctxt); |
|
6438 |
|
6439 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { |
|
6440 valuePush(ctxt, xmlXPathNewCString("")); |
|
6441 } else { |
|
6442 int i = 0; /* Should be first in document order !!!!! */ |
|
6443 switch (cur->nodesetval->nodeTab[i]->type) { |
|
6444 case XML_ELEMENT_NODE: |
|
6445 case XML_ATTRIBUTE_NODE: |
|
6446 if (cur->nodesetval->nodeTab[i]->ns == NULL) |
|
6447 valuePush(ctxt, xmlXPathNewCString("")); |
|
6448 else |
|
6449 valuePush(ctxt, xmlXPathNewString( |
|
6450 cur->nodesetval->nodeTab[i]->ns->href)); |
|
6451 break; |
|
6452 default: |
|
6453 valuePush(ctxt, xmlXPathNewCString("")); |
|
6454 } |
|
6455 } |
|
6456 xmlXPathFreeObject(cur); |
|
6457 } |
|
6458 |
|
6459 /** |
|
6460 * xmlXPathNameFunction: |
|
6461 * @ctxt: the XPath Parser context |
|
6462 * @nargs: the number of arguments |
|
6463 * |
|
6464 * Implement the name() XPath function |
|
6465 * string name(node-set?) |
|
6466 * The name function returns a string containing a QName representing |
|
6467 * the name of the node in the argument node-set that is first in document |
|
6468 * order. The QName must represent the name with respect to the namespace |
|
6469 * declarations in effect on the node whose name is being represented. |
|
6470 * Typically, this will be the form in which the name occurred in the XML |
|
6471 * source. This need not be the case if there are namespace declarations |
|
6472 * in effect on the node that associate multiple prefixes with the same |
|
6473 * namespace. However, an implementation may include information about |
|
6474 * the original prefix in its representation of nodes; in this case, an |
|
6475 * implementation can ensure that the returned string is always the same |
|
6476 * as the QName used in the XML source. If the argument it omitted it |
|
6477 * defaults to the context node. |
|
6478 * Libxml keep the original prefix so the "real qualified name" used is |
|
6479 * returned. |
|
6480 */ |
|
6481 static void |
|
6482 xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) |
|
6483 { |
|
6484 xmlXPathObjectPtr cur; |
|
6485 |
|
6486 if (nargs == 0) { |
|
6487 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
6488 nargs = 1; |
|
6489 } |
|
6490 |
|
6491 CHECK_ARITY(1); |
|
6492 if ((ctxt->value == NULL) || |
|
6493 ((ctxt->value->type != XPATH_NODESET) && |
|
6494 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
6495 XP_ERROR(XPATH_INVALID_TYPE); |
|
6496 cur = valuePop(ctxt); |
|
6497 |
|
6498 if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { |
|
6499 valuePush(ctxt, xmlXPathNewCString("")); |
|
6500 } else { |
|
6501 int i = 0; /* Should be first in document order !!!!! */ |
|
6502 |
|
6503 switch (cur->nodesetval->nodeTab[i]->type) { |
|
6504 case XML_ELEMENT_NODE: |
|
6505 case XML_ATTRIBUTE_NODE: |
|
6506 if (cur->nodesetval->nodeTab[i]->name[0] == ' ') |
|
6507 valuePush(ctxt, xmlXPathNewCString("")); |
|
6508 else if ((cur->nodesetval->nodeTab[i]->ns == NULL) || |
|
6509 (cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) { |
|
6510 valuePush(ctxt, |
|
6511 xmlXPathNewString(cur->nodesetval->nodeTab[i]->name)); |
|
6512 |
|
6513 } else { |
|
6514 xmlChar *fullname; |
|
6515 |
|
6516 fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name, |
|
6517 cur->nodesetval->nodeTab[i]->ns->prefix, |
|
6518 NULL, 0); |
|
6519 if (fullname == cur->nodesetval->nodeTab[i]->name) |
|
6520 fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name); |
|
6521 if (fullname == NULL) { |
|
6522 XP_ERROR(XPATH_MEMORY_ERROR); |
|
6523 } |
|
6524 valuePush(ctxt, xmlXPathWrapString(fullname)); |
|
6525 } |
|
6526 break; |
|
6527 default: |
|
6528 valuePush(ctxt, |
|
6529 xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i])); |
|
6530 xmlXPathLocalNameFunction(ctxt, 1); |
|
6531 } |
|
6532 } |
|
6533 xmlXPathFreeObject(cur); |
|
6534 } |
|
6535 |
|
6536 |
|
6537 /** |
|
6538 * xmlXPathStringFunction: |
|
6539 * @ctxt: the XPath Parser context |
|
6540 * @nargs: the number of arguments |
|
6541 * |
|
6542 * Implement the string() XPath function |
|
6543 * string string(object?) |
|
6544 * The string function converts an object to a string as follows: |
|
6545 * - A node-set is converted to a string by returning the value of |
|
6546 * the node in the node-set that is first in document order. |
|
6547 * If the node-set is empty, an empty string is returned. |
|
6548 * - A number is converted to a string as follows |
|
6549 * + NaN is converted to the string NaN |
|
6550 * + positive zero is converted to the string 0 |
|
6551 * + negative zero is converted to the string 0 |
|
6552 * + positive infinity is converted to the string Infinity |
|
6553 * + negative infinity is converted to the string -Infinity |
|
6554 * + if the number is an integer, the number is represented in |
|
6555 * decimal form as a Number with no decimal point and no leading |
|
6556 * zeros, preceded by a minus sign (-) if the number is negative |
|
6557 * + otherwise, the number is represented in decimal form as a |
|
6558 * Number including a decimal point with at least one digit |
|
6559 * before the decimal point and at least one digit after the |
|
6560 * decimal point, preceded by a minus sign (-) if the number |
|
6561 * is negative; there must be no leading zeros before the decimal |
|
6562 * point apart possibly from the one required digit immediately |
|
6563 * before the decimal point; beyond the one required digit |
|
6564 * after the decimal point there must be as many, but only as |
|
6565 * many, more digits as are needed to uniquely distinguish the |
|
6566 * number from all other IEEE 754 numeric values. |
|
6567 * - The boolean false value is converted to the string false. |
|
6568 * The boolean true value is converted to the string true. |
|
6569 * |
|
6570 * If the argument is omitted, it defaults to a node-set with the |
|
6571 * context node as its only member. |
|
6572 */ |
|
6573 void |
|
6574 xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6575 xmlXPathObjectPtr cur; |
|
6576 |
|
6577 if (nargs == 0) { |
|
6578 valuePush(ctxt, |
|
6579 xmlXPathWrapString( |
|
6580 xmlXPathCastNodeToString(ctxt->context->node))); |
|
6581 return; |
|
6582 } |
|
6583 |
|
6584 CHECK_ARITY(1); |
|
6585 cur = valuePop(ctxt); |
|
6586 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); |
|
6587 cur = xmlXPathConvertString(cur); |
|
6588 if(OOM_FLAG) return; |
|
6589 valuePush(ctxt, cur); |
|
6590 } |
|
6591 |
|
6592 /** |
|
6593 * xmlXPathStringLengthFunction: |
|
6594 * @ctxt: the XPath Parser context |
|
6595 * @nargs: the number of arguments |
|
6596 * |
|
6597 * Implement the string-length() XPath function |
|
6598 * number string-length(string?) |
|
6599 * The string-length returns the number of characters in the string |
|
6600 * (see [3.6 Strings]). If the argument is omitted, it defaults to |
|
6601 * the context node converted to a string, in other words the value |
|
6602 * of the context node. |
|
6603 */ |
|
6604 void |
|
6605 xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6606 xmlXPathObjectPtr cur; |
|
6607 |
|
6608 if (nargs == 0) { |
|
6609 if (ctxt->context->node == NULL) { |
|
6610 valuePush(ctxt, xmlXPathNewFloat(0)); |
|
6611 } else { |
|
6612 xmlChar *content; |
|
6613 |
|
6614 content = xmlXPathCastNodeToString(ctxt->context->node); |
|
6615 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content))); |
|
6616 xmlFree(content); |
|
6617 } |
|
6618 return; |
|
6619 } |
|
6620 CHECK_ARITY(1); |
|
6621 CAST_TO_STRING; |
|
6622 CHECK_TYPE(XPATH_STRING); |
|
6623 cur = valuePop(ctxt); |
|
6624 valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval))); |
|
6625 xmlXPathFreeObject(cur); |
|
6626 } |
|
6627 |
|
6628 /** |
|
6629 * xmlXPathConcatFunction: |
|
6630 * @ctxt: the XPath Parser context |
|
6631 * @nargs: the number of arguments |
|
6632 * |
|
6633 * Implement the concat() XPath function |
|
6634 * string concat(string, string, string*) |
|
6635 * The concat function returns the concatenation of its arguments. |
|
6636 */ |
|
6637 void |
|
6638 xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6639 xmlXPathObjectPtr cur, newobj; |
|
6640 xmlChar *tmp; |
|
6641 |
|
6642 if (nargs < 2) { |
|
6643 CHECK_ARITY(2); |
|
6644 } |
|
6645 |
|
6646 CAST_TO_STRING; |
|
6647 cur = valuePop(ctxt); |
|
6648 if ((cur == NULL) || (cur->type != XPATH_STRING)) { |
|
6649 xmlXPathFreeObject(cur); |
|
6650 return; |
|
6651 } |
|
6652 nargs--; |
|
6653 |
|
6654 while (nargs > 0) { |
|
6655 CAST_TO_STRING; |
|
6656 newobj = valuePop(ctxt); |
|
6657 if ((newobj == NULL) || (newobj->type != XPATH_STRING)) { |
|
6658 xmlXPathFreeObject(newobj); |
|
6659 xmlXPathFreeObject(cur); |
|
6660 XP_ERROR(XPATH_INVALID_TYPE); |
|
6661 } |
|
6662 tmp = xmlStrcat(newobj->stringval, cur->stringval); |
|
6663 newobj->stringval = cur->stringval; |
|
6664 cur->stringval = tmp; |
|
6665 |
|
6666 xmlXPathFreeObject(newobj); |
|
6667 nargs--; |
|
6668 } |
|
6669 valuePush(ctxt, cur); |
|
6670 } |
|
6671 |
|
6672 /** |
|
6673 * xmlXPathContainsFunction: |
|
6674 * @ctxt: the XPath Parser context |
|
6675 * @nargs: the number of arguments |
|
6676 * |
|
6677 * Implement the contains() XPath function |
|
6678 * boolean contains(string, string) |
|
6679 * The contains function returns true if the first argument string |
|
6680 * contains the second argument string, and otherwise returns false. |
|
6681 */ |
|
6682 void |
|
6683 xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6684 xmlXPathObjectPtr hay, needle; |
|
6685 |
|
6686 CHECK_ARITY(2); |
|
6687 CAST_TO_STRING; |
|
6688 CHECK_TYPE(XPATH_STRING); |
|
6689 needle = valuePop(ctxt); |
|
6690 CAST_TO_STRING; |
|
6691 hay = valuePop(ctxt); |
|
6692 if ((hay == NULL) || (hay->type != XPATH_STRING)) { |
|
6693 xmlXPathFreeObject(hay); |
|
6694 xmlXPathFreeObject(needle); |
|
6695 XP_ERROR(XPATH_INVALID_TYPE); |
|
6696 } |
|
6697 if (xmlStrstr(hay->stringval, needle->stringval)) |
|
6698 valuePush(ctxt, xmlXPathNewBoolean(1)); |
|
6699 else |
|
6700 valuePush(ctxt, xmlXPathNewBoolean(0)); |
|
6701 xmlXPathFreeObject(hay); |
|
6702 xmlXPathFreeObject(needle); |
|
6703 } |
|
6704 |
|
6705 /** |
|
6706 * xmlXPathStartsWithFunction: |
|
6707 * @ctxt: the XPath Parser context |
|
6708 * @nargs: the number of arguments |
|
6709 * |
|
6710 * Implement the starts-with() XPath function |
|
6711 * boolean starts-with(string, string) |
|
6712 * The starts-with function returns true if the first argument string |
|
6713 * starts with the second argument string, and otherwise returns false. |
|
6714 */ |
|
6715 void |
|
6716 xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6717 xmlXPathObjectPtr hay, needle; |
|
6718 int n; |
|
6719 |
|
6720 CHECK_ARITY(2); |
|
6721 CAST_TO_STRING; |
|
6722 CHECK_TYPE(XPATH_STRING); |
|
6723 needle = valuePop(ctxt); |
|
6724 CAST_TO_STRING; |
|
6725 hay = valuePop(ctxt); |
|
6726 if ((hay == NULL) || (hay->type != XPATH_STRING)) { |
|
6727 xmlXPathFreeObject(hay); |
|
6728 xmlXPathFreeObject(needle); |
|
6729 XP_ERROR(XPATH_INVALID_TYPE); |
|
6730 } |
|
6731 n = xmlStrlen(needle->stringval); |
|
6732 if (xmlStrncmp(hay->stringval, needle->stringval, n)) |
|
6733 valuePush(ctxt, xmlXPathNewBoolean(0)); |
|
6734 else |
|
6735 valuePush(ctxt, xmlXPathNewBoolean(1)); |
|
6736 xmlXPathFreeObject(hay); |
|
6737 xmlXPathFreeObject(needle); |
|
6738 } |
|
6739 |
|
6740 /** |
|
6741 * xmlXPathSubstringFunction: |
|
6742 * @ctxt: the XPath Parser context |
|
6743 * @nargs: the number of arguments |
|
6744 * |
|
6745 * Implement the substring() XPath function |
|
6746 * string substring(string, number, number?) |
|
6747 * The substring function returns the substring of the first argument |
|
6748 * starting at the position specified in the second argument with |
|
6749 * length specified in the third argument. For example, |
|
6750 * substring("12345",2,3) returns "234". If the third argument is not |
|
6751 * specified, it returns the substring starting at the position specified |
|
6752 * in the second argument and continuing to the end of the string. For |
|
6753 * example, substring("12345",2) returns "2345". More precisely, each |
|
6754 * character in the string (see [3.6 Strings]) is considered to have a |
|
6755 * numeric position: the position of the first character is 1, the position |
|
6756 * of the second character is 2 and so on. The returned substring contains |
|
6757 * those characters for which the position of the character is greater than |
|
6758 * or equal to the second argument and, if the third argument is specified, |
|
6759 * less than the sum of the second and third arguments; the comparisons |
|
6760 * and addition used for the above follow the standard IEEE 754 rules. Thus: |
|
6761 * - substring("12345", 1.5, 2.6) returns "234" |
|
6762 * - substring("12345", 0, 3) returns "12" |
|
6763 * - substring("12345", 0 div 0, 3) returns "" |
|
6764 * - substring("12345", 1, 0 div 0) returns "" |
|
6765 * - substring("12345", -42, 1 div 0) returns "12345" |
|
6766 * - substring("12345", -1 div 0, 1 div 0) returns "" |
|
6767 */ |
|
6768 void |
|
6769 xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6770 xmlXPathObjectPtr str, start, len; |
|
6771 double le=0, in; |
|
6772 // TODO: Name "l" is bad for a variable -- refactor it |
|
6773 int i, l, m; |
|
6774 xmlChar *ret; |
|
6775 |
|
6776 if (nargs < 2) { |
|
6777 // TODO: Use of these CHECK_ARITY macros is bad ... |
|
6778 // -- make check manually OR define normal macro for that |
|
6779 CHECK_ARITY(2); |
|
6780 } |
|
6781 if (nargs > 3) { |
|
6782 CHECK_ARITY(3); |
|
6783 } |
|
6784 /* |
|
6785 * take care of possible last (position) argument |
|
6786 */ |
|
6787 if (nargs == 3) { |
|
6788 CAST_TO_NUMBER; |
|
6789 CHECK_TYPE(XPATH_NUMBER); |
|
6790 len = valuePop(ctxt); |
|
6791 le = len->floatval; |
|
6792 xmlXPathFreeObject(len); |
|
6793 } |
|
6794 |
|
6795 CAST_TO_NUMBER; |
|
6796 CHECK_TYPE(XPATH_NUMBER); |
|
6797 start = valuePop(ctxt); |
|
6798 in = start->floatval; |
|
6799 xmlXPathFreeObject(start); |
|
6800 CAST_TO_STRING; |
|
6801 CHECK_TYPE(XPATH_STRING); |
|
6802 str = valuePop(ctxt); |
|
6803 m = xmlUTF8Strlen((const unsigned char *)str->stringval); |
|
6804 |
|
6805 /* |
|
6806 * If last pos not present, calculate last position |
|
6807 */ |
|
6808 if (nargs != 3) { |
|
6809 le = (double)m; |
|
6810 if (in < 1.0) |
|
6811 in = 1.0; |
|
6812 } |
|
6813 |
|
6814 /* Need to check for the special cases where either |
|
6815 * the index is NaN, the length is NaN, or both |
|
6816 * arguments are infinity (relying on Inf + -Inf = NaN) |
|
6817 */ |
|
6818 if (!xmlXPathIsNaN(in + le) && !xmlXPathIsInf(in)) { |
|
6819 /* |
|
6820 * To meet the requirements of the spec, the arguments |
|
6821 * must be converted to integer format before |
|
6822 * initial index calculations are done |
|
6823 * |
|
6824 * First we go to integer form, rounding up |
|
6825 * and checking for special cases |
|
6826 */ |
|
6827 i = (int) in; |
|
6828 if (((double)i)+0.5 <= in) i++; |
|
6829 |
|
6830 if (xmlXPathIsInf(le) == 1) { |
|
6831 l = m; |
|
6832 if (i < 1) |
|
6833 i = 1; |
|
6834 } |
|
6835 else if (xmlXPathIsInf(le) == -1 || le < 0.0) |
|
6836 l = 0; |
|
6837 else { |
|
6838 l = (int) le; |
|
6839 if (((double)l)+0.5 <= le) l++; |
|
6840 } |
|
6841 |
|
6842 /* Now we normalize inidices */ |
|
6843 i -= 1; |
|
6844 l += i; |
|
6845 if (i < 0) |
|
6846 i = 0; |
|
6847 if (l > m) |
|
6848 l = m; |
|
6849 |
|
6850 /* number of chars to copy */ |
|
6851 l -= i; |
|
6852 |
|
6853 ret = xmlUTF8Strsub(str->stringval, i, l); |
|
6854 } |
|
6855 else { |
|
6856 ret = NULL; |
|
6857 } |
|
6858 |
|
6859 if (ret == NULL) |
|
6860 valuePush(ctxt, xmlXPathNewCString("")); |
|
6861 else { |
|
6862 valuePush(ctxt, xmlXPathNewString(ret)); |
|
6863 xmlFree(ret); |
|
6864 } |
|
6865 |
|
6866 xmlXPathFreeObject(str); |
|
6867 } |
|
6868 |
|
6869 /** |
|
6870 * xmlXPathSubstringBeforeFunction: |
|
6871 * @ctxt: the XPath Parser context |
|
6872 * @nargs: the number of arguments |
|
6873 * |
|
6874 * Implement the substring-before() XPath function |
|
6875 * string substring-before(string, string) |
|
6876 * The substring-before function returns the substring of the first |
|
6877 * argument string that precedes the first occurrence of the second |
|
6878 * argument string in the first argument string, or the empty string |
|
6879 * if the first argument string does not contain the second argument |
|
6880 * string. For example, substring-before("1999/04/01","/") returns 1999. |
|
6881 */ |
|
6882 void |
|
6883 xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6884 xmlXPathObjectPtr str; |
|
6885 xmlXPathObjectPtr find; |
|
6886 xmlBufferPtr target; |
|
6887 const xmlChar *point; |
|
6888 int offset; |
|
6889 |
|
6890 CHECK_ARITY(2); |
|
6891 CAST_TO_STRING; |
|
6892 find = valuePop(ctxt); |
|
6893 CAST_TO_STRING; |
|
6894 str = valuePop(ctxt); |
|
6895 |
|
6896 target = xmlBufferCreate(); |
|
6897 if (target) { |
|
6898 point = xmlStrstr(str->stringval, find->stringval); |
|
6899 if (point) { |
|
6900 offset = (int)(point - str->stringval); |
|
6901 xmlBufferAdd(target, str->stringval, offset); |
|
6902 } |
|
6903 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
6904 xmlBufferFree(target); |
|
6905 } |
|
6906 |
|
6907 xmlXPathFreeObject(str); |
|
6908 xmlXPathFreeObject(find); |
|
6909 } |
|
6910 |
|
6911 /** |
|
6912 * xmlXPathSubstringAfterFunction: |
|
6913 * @ctxt: the XPath Parser context |
|
6914 * @nargs: the number of arguments |
|
6915 * |
|
6916 * Implement the substring-after() XPath function |
|
6917 * string substring-after(string, string) |
|
6918 * The substring-after function returns the substring of the first |
|
6919 * argument string that follows the first occurrence of the second |
|
6920 * argument string in the first argument string, or the empty stringi |
|
6921 * if the first argument string does not contain the second argument |
|
6922 * string. For example, substring-after("1999/04/01","/") returns 04/01, |
|
6923 * and substring-after("1999/04/01","19") returns 99/04/01. |
|
6924 */ |
|
6925 void |
|
6926 xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6927 xmlXPathObjectPtr str; |
|
6928 xmlXPathObjectPtr find; |
|
6929 xmlBufferPtr target; |
|
6930 const xmlChar *point; |
|
6931 int offset; |
|
6932 |
|
6933 CHECK_ARITY(2); |
|
6934 CAST_TO_STRING; |
|
6935 find = valuePop(ctxt); |
|
6936 CAST_TO_STRING; |
|
6937 str = valuePop(ctxt); |
|
6938 |
|
6939 target = xmlBufferCreate(); |
|
6940 if (target) { |
|
6941 point = xmlStrstr(str->stringval, find->stringval); |
|
6942 if (point) { |
|
6943 offset = (int)(point - str->stringval) + xmlStrlen(find->stringval); |
|
6944 xmlBufferAdd(target, &str->stringval[offset], |
|
6945 xmlStrlen(str->stringval) - offset); |
|
6946 } |
|
6947 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
6948 xmlBufferFree(target); |
|
6949 } |
|
6950 |
|
6951 xmlXPathFreeObject(str); |
|
6952 xmlXPathFreeObject(find); |
|
6953 } |
|
6954 |
|
6955 /** |
|
6956 * xmlXPathNormalizeFunction: |
|
6957 * @ctxt: the XPath Parser context |
|
6958 * @nargs: the number of arguments |
|
6959 * |
|
6960 * Implement the normalize-space() XPath function |
|
6961 * string normalize-space(string?) |
|
6962 * The normalize-space function returns the argument string with white |
|
6963 * space normalized by stripping leading and trailing whitespace |
|
6964 * and replacing sequences of whitespace characters by a single |
|
6965 * space. Whitespace characters are the same allowed by the S production |
|
6966 * in XML. If the argument is omitted, it defaults to the context |
|
6967 * node converted to a string, in other words the value of the context node. |
|
6968 */ |
|
6969 void |
|
6970 xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
6971 xmlXPathObjectPtr obj = NULL; |
|
6972 xmlChar *source = NULL; |
|
6973 xmlBufferPtr target; |
|
6974 xmlChar blank; |
|
6975 |
|
6976 if (nargs == 0) { |
|
6977 /* Use current context node */ |
|
6978 valuePush(ctxt, |
|
6979 xmlXPathWrapString( |
|
6980 xmlXPathCastNodeToString(ctxt->context->node))); |
|
6981 nargs = 1; |
|
6982 } |
|
6983 |
|
6984 CHECK_ARITY(1); |
|
6985 CAST_TO_STRING; |
|
6986 CHECK_TYPE(XPATH_STRING); |
|
6987 obj = valuePop(ctxt); |
|
6988 source = obj->stringval; |
|
6989 |
|
6990 target = xmlBufferCreate(); |
|
6991 if (target && source) { |
|
6992 |
|
6993 /* Skip leading whitespaces */ |
|
6994 while (IS_BLANK_CH(*source)) |
|
6995 source++; |
|
6996 |
|
6997 /* Collapse intermediate whitespaces, and skip trailing whitespaces */ |
|
6998 blank = 0; |
|
6999 while (*source) { |
|
7000 if (IS_BLANK_CH(*source)) { |
|
7001 blank = 0x20; |
|
7002 } else { |
|
7003 if (blank) { |
|
7004 xmlBufferAdd(target, &blank, 1); |
|
7005 blank = 0; |
|
7006 } |
|
7007 xmlBufferAdd(target, source, 1); |
|
7008 } |
|
7009 source++; |
|
7010 } |
|
7011 |
|
7012 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
7013 xmlBufferFree(target); |
|
7014 } |
|
7015 xmlXPathFreeObject(obj); |
|
7016 } |
|
7017 |
|
7018 /** |
|
7019 * xmlXPathTranslateFunction: |
|
7020 * @ctxt: the XPath Parser context |
|
7021 * @nargs: the number of arguments |
|
7022 * |
|
7023 * Implement the translate() XPath function |
|
7024 * string translate(string, string, string) |
|
7025 * The translate function returns the first argument string with |
|
7026 * occurrences of characters in the second argument string replaced |
|
7027 * by the character at the corresponding position in the third argument |
|
7028 * string. For example, translate("bar","abc","ABC") returns the string |
|
7029 * BAr. If there is a character in the second argument string with no |
|
7030 * character at a corresponding position in the third argument string |
|
7031 * (because the second argument string is longer than the third argument |
|
7032 * string), then occurrences of that character in the first argument |
|
7033 * string are removed. For example, translate("--aaa--","abc-","ABC") |
|
7034 * returns "AAA". If a character occurs more than once in second |
|
7035 * argument string, then the first occurrence determines the replacement |
|
7036 * character. If the third argument string is longer than the second |
|
7037 * argument string, then excess characters are ignored. |
|
7038 */ |
|
7039 void |
|
7040 xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7041 xmlXPathObjectPtr str; |
|
7042 xmlXPathObjectPtr from; |
|
7043 xmlXPathObjectPtr to; |
|
7044 xmlBufferPtr target; |
|
7045 int offset, max; |
|
7046 xmlChar ch; |
|
7047 xmlChar *point; |
|
7048 xmlChar *cptr; |
|
7049 |
|
7050 CHECK_ARITY(3); |
|
7051 |
|
7052 CAST_TO_STRING; |
|
7053 to = valuePop(ctxt); |
|
7054 CAST_TO_STRING; |
|
7055 from = valuePop(ctxt); |
|
7056 CAST_TO_STRING; |
|
7057 str = valuePop(ctxt); |
|
7058 |
|
7059 target = xmlBufferCreate(); |
|
7060 if (target) { |
|
7061 max = xmlUTF8Strlen(to->stringval); |
|
7062 for (cptr = str->stringval; (ch=*cptr); ) { |
|
7063 offset = xmlUTF8Strloc(from->stringval, cptr); |
|
7064 if (offset >= 0) { |
|
7065 if (offset < max) { |
|
7066 point = xmlUTF8Strpos(to->stringval, offset); |
|
7067 if (point) |
|
7068 xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1)); |
|
7069 } |
|
7070 } else |
|
7071 xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1)); |
|
7072 |
|
7073 /* Step to next character in input */ |
|
7074 cptr++; |
|
7075 if ( ch & 0x80 ) { |
|
7076 /* if not simple ascii, verify proper format */ |
|
7077 if ( (ch & 0xc0) != 0xc0 ) { |
|
7078 xmlGenericError(xmlGenericErrorContext, |
|
7079 EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n")); |
|
7080 break; |
|
7081 } |
|
7082 /* then skip over remaining bytes for this char */ |
|
7083 while ( (ch <<= 1) & 0x80 ) |
|
7084 if ( (*cptr++ & 0xc0) != 0x80 ) { |
|
7085 xmlGenericError(xmlGenericErrorContext, |
|
7086 EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n")); |
|
7087 break; |
|
7088 } |
|
7089 if (ch & 0x80) /* must have had error encountered */ |
|
7090 break; |
|
7091 } |
|
7092 } |
|
7093 } |
|
7094 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
7095 xmlBufferFree(target); |
|
7096 xmlXPathFreeObject(str); |
|
7097 xmlXPathFreeObject(from); |
|
7098 xmlXPathFreeObject(to); |
|
7099 } |
|
7100 |
|
7101 /** |
|
7102 * xmlXPathBooleanFunction: |
|
7103 * @ctxt: the XPath Parser context |
|
7104 * @nargs: the number of arguments |
|
7105 * |
|
7106 * Implement the boolean() XPath function |
|
7107 * boolean boolean(object) |
|
7108 * The boolean function converts its argument to a boolean as follows: |
|
7109 * - a number is true if and only if it is neither positive or |
|
7110 * negative zero nor NaN |
|
7111 * - a node-set is true if and only if it is non-empty |
|
7112 * - a string is true if and only if its length is non-zero |
|
7113 */ |
|
7114 void |
|
7115 xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7116 xmlXPathObjectPtr cur; |
|
7117 |
|
7118 CHECK_ARITY(1); |
|
7119 cur = valuePop(ctxt); |
|
7120 if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND); |
|
7121 cur = xmlXPathConvertBoolean(cur); |
|
7122 valuePush(ctxt, cur); |
|
7123 } |
|
7124 |
|
7125 /** |
|
7126 * xmlXPathNotFunction: |
|
7127 * @ctxt: the XPath Parser context |
|
7128 * @nargs: the number of arguments |
|
7129 * |
|
7130 * Implement the not() XPath function |
|
7131 * boolean not(boolean) |
|
7132 * The not function returns true if its argument is false, |
|
7133 * and false otherwise. |
|
7134 */ |
|
7135 void |
|
7136 xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7137 CHECK_ARITY(1); |
|
7138 CAST_TO_BOOLEAN; |
|
7139 CHECK_TYPE(XPATH_BOOLEAN); |
|
7140 ctxt->value->boolval = ! ctxt->value->boolval; |
|
7141 } |
|
7142 |
|
7143 /** |
|
7144 * xmlXPathTrueFunction: |
|
7145 * @ctxt: the XPath Parser context |
|
7146 * @nargs: the number of arguments |
|
7147 * |
|
7148 * Implement the true() XPath function |
|
7149 * boolean true() |
|
7150 */ |
|
7151 void |
|
7152 xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7153 CHECK_ARITY(0); |
|
7154 valuePush(ctxt, xmlXPathNewBoolean(1)); |
|
7155 } |
|
7156 |
|
7157 /** |
|
7158 * xmlXPathFalseFunction: |
|
7159 * @ctxt: the XPath Parser context |
|
7160 * @nargs: the number of arguments |
|
7161 * |
|
7162 * Implement the false() XPath function |
|
7163 * boolean false() |
|
7164 */ |
|
7165 void |
|
7166 xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7167 CHECK_ARITY(0); |
|
7168 valuePush(ctxt, xmlXPathNewBoolean(0)); |
|
7169 } |
|
7170 |
|
7171 /** |
|
7172 * xmlXPathLangFunction: |
|
7173 * @ctxt: the XPath Parser context |
|
7174 * @nargs: the number of arguments |
|
7175 * |
|
7176 * Implement the lang() XPath function |
|
7177 * boolean lang(string) |
|
7178 * The lang function returns true or false depending on whether the |
|
7179 * language of the context node as specified by xml:lang attributes |
|
7180 * is the same as or is a sublanguage of the language specified by |
|
7181 * the argument string. The language of the context node is determined |
|
7182 * by the value of the xml:lang attribute on the context node, or, if |
|
7183 * the context node has no xml:lang attribute, by the value of the |
|
7184 * xml:lang attribute on the nearest ancestor of the context node that |
|
7185 * has an xml:lang attribute. If there is no such attribute, then lang |
|
7186 * returns false. If there is such an attribute, then lang returns |
|
7187 * true if the attribute value is equal to the argument ignoring case, |
|
7188 * or if there is some suffix starting with - such that the attribute |
|
7189 * value is equal to the argument ignoring that suffix of the attribute |
|
7190 * value and ignoring case. |
|
7191 */ |
|
7192 void |
|
7193 xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7194 xmlXPathObjectPtr val; |
|
7195 const xmlChar *theLang; |
|
7196 const xmlChar *lang; |
|
7197 int ret = 0; |
|
7198 int i; |
|
7199 |
|
7200 CHECK_ARITY(1); |
|
7201 CAST_TO_STRING; |
|
7202 CHECK_TYPE(XPATH_STRING); |
|
7203 val = valuePop(ctxt); |
|
7204 lang = val->stringval; |
|
7205 theLang = xmlNodeGetLang(ctxt->context->node); |
|
7206 if ((theLang != NULL) && (lang != NULL)) { |
|
7207 for (i = 0;lang[i] != 0;i++) |
|
7208 if (toupper(lang[i]) != toupper(theLang[i])) |
|
7209 goto not_equal; |
|
7210 ret = 1; |
|
7211 } |
|
7212 not_equal: |
|
7213 if (theLang) |
|
7214 xmlFree((void *)theLang); |
|
7215 xmlXPathFreeObject(val); |
|
7216 valuePush(ctxt, xmlXPathNewBoolean(ret)); |
|
7217 } |
|
7218 |
|
7219 /** |
|
7220 * xmlXPathNumberFunction: |
|
7221 * @ctxt: the XPath Parser context |
|
7222 * @nargs: the number of arguments |
|
7223 * |
|
7224 * Implement the number() XPath function |
|
7225 * number number(object?) |
|
7226 */ |
|
7227 void |
|
7228 xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7229 xmlXPathObjectPtr cur; |
|
7230 double res; |
|
7231 |
|
7232 if (nargs == 0) { |
|
7233 if (ctxt->context->node == NULL) { |
|
7234 valuePush(ctxt, xmlXPathNewFloat(0.0)); |
|
7235 } else { |
|
7236 xmlChar* content = xmlNodeGetContent(ctxt->context->node); |
|
7237 |
|
7238 res = xmlXPathStringEvalNumber(content); |
|
7239 valuePush(ctxt, xmlXPathNewFloat(res)); |
|
7240 xmlFree(content); |
|
7241 } |
|
7242 return; |
|
7243 } |
|
7244 |
|
7245 CHECK_ARITY(1); |
|
7246 cur = valuePop(ctxt); |
|
7247 cur = xmlXPathConvertNumber(cur); |
|
7248 valuePush(ctxt, cur); |
|
7249 } |
|
7250 |
|
7251 /** |
|
7252 * xmlXPathSumFunction: |
|
7253 * @ctxt: the XPath Parser context |
|
7254 * @nargs: the number of arguments |
|
7255 * |
|
7256 * Implement the sum() XPath function |
|
7257 * number sum(node-set) |
|
7258 * The sum function returns the sum of the values of the nodes in |
|
7259 * the argument node-set. |
|
7260 */ |
|
7261 void |
|
7262 xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7263 xmlXPathObjectPtr cur; |
|
7264 int i; |
|
7265 double res = 0.0; |
|
7266 |
|
7267 CHECK_ARITY(1); |
|
7268 if ((ctxt->value == NULL) || |
|
7269 ((ctxt->value->type != XPATH_NODESET) && |
|
7270 (ctxt->value->type != XPATH_XSLT_TREE))) |
|
7271 XP_ERROR(XPATH_INVALID_TYPE); |
|
7272 cur = valuePop(ctxt); |
|
7273 |
|
7274 if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) { |
|
7275 for (i = 0; i < cur->nodesetval->nodeNr; i++) { |
|
7276 res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]); |
|
7277 } |
|
7278 } |
|
7279 valuePush(ctxt, xmlXPathNewFloat(res)); |
|
7280 xmlXPathFreeObject(cur); |
|
7281 } |
|
7282 |
|
7283 /** |
|
7284 * xmlXPathFloorFunction: |
|
7285 * @ctxt: the XPath Parser context |
|
7286 * @nargs: the number of arguments |
|
7287 * |
|
7288 * Implement the floor() XPath function |
|
7289 * number floor(number) |
|
7290 * The floor function returns the largest (closest to positive infinity) |
|
7291 * number that is not greater than the argument and that is an integer. |
|
7292 */ |
|
7293 void |
|
7294 xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7295 double f; |
|
7296 |
|
7297 CHECK_ARITY(1); |
|
7298 CAST_TO_NUMBER; |
|
7299 CHECK_TYPE(XPATH_NUMBER); |
|
7300 |
|
7301 f = (double)((int) ctxt->value->floatval); |
|
7302 if (f != ctxt->value->floatval) { |
|
7303 if (ctxt->value->floatval > 0) |
|
7304 ctxt->value->floatval = f; |
|
7305 else |
|
7306 ctxt->value->floatval = f - 1; |
|
7307 } |
|
7308 } |
|
7309 |
|
7310 /** |
|
7311 * xmlXPathCeilingFunction: |
|
7312 * @ctxt: the XPath Parser context |
|
7313 * @nargs: the number of arguments |
|
7314 * |
|
7315 * Implement the ceiling() XPath function |
|
7316 * number ceiling(number) |
|
7317 * The ceiling function returns the smallest (closest to negative infinity) |
|
7318 * number that is not less than the argument and that is an integer. |
|
7319 */ |
|
7320 void |
|
7321 xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7322 double f; |
|
7323 |
|
7324 CHECK_ARITY(1); |
|
7325 CAST_TO_NUMBER; |
|
7326 CHECK_TYPE(XPATH_NUMBER); |
|
7327 |
|
7328 #if 0 |
|
7329 ctxt->value->floatval = ceil(ctxt->value->floatval); |
|
7330 #else |
|
7331 f = (double)((int) ctxt->value->floatval); |
|
7332 if (f != ctxt->value->floatval) { |
|
7333 if (ctxt->value->floatval > 0) |
|
7334 ctxt->value->floatval = f + 1; |
|
7335 else { |
|
7336 if (ctxt->value->floatval < 0 && f == 0) |
|
7337 ctxt->value->floatval = xmlXPathNZERO; |
|
7338 else |
|
7339 ctxt->value->floatval = f; |
|
7340 } |
|
7341 |
|
7342 } |
|
7343 #endif |
|
7344 } |
|
7345 |
|
7346 /** |
|
7347 * xmlXPathRoundFunction: |
|
7348 * @ctxt: the XPath Parser context |
|
7349 * @nargs: the number of arguments |
|
7350 * |
|
7351 * Implement the round() XPath function |
|
7352 * number round(number) |
|
7353 * The round function returns the number that is closest to the |
|
7354 * argument and that is an integer. If there are two such numbers, |
|
7355 * then the one that is even is returned. |
|
7356 */ |
|
7357 void |
|
7358 xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
7359 double f; |
|
7360 |
|
7361 CHECK_ARITY(1); |
|
7362 CAST_TO_NUMBER; |
|
7363 CHECK_TYPE(XPATH_NUMBER); |
|
7364 |
|
7365 if ((xmlXPathIsNaN(ctxt->value->floatval)) || |
|
7366 (xmlXPathIsInf(ctxt->value->floatval) == 1) || |
|
7367 (xmlXPathIsInf(ctxt->value->floatval) == -1) || |
|
7368 (ctxt->value->floatval == 0.0)) |
|
7369 return; |
|
7370 |
|
7371 f = (double)((int) ctxt->value->floatval); |
|
7372 if (ctxt->value->floatval < 0) { |
|
7373 if (ctxt->value->floatval < f - 0.5) |
|
7374 ctxt->value->floatval = f - 1; |
|
7375 else |
|
7376 ctxt->value->floatval = f; |
|
7377 if (ctxt->value->floatval == 0) |
|
7378 ctxt->value->floatval = xmlXPathNZERO; |
|
7379 } else { |
|
7380 if (ctxt->value->floatval < f + 0.5) |
|
7381 ctxt->value->floatval = f; |
|
7382 else |
|
7383 ctxt->value->floatval = f + 1; |
|
7384 } |
|
7385 } |
|
7386 |
|
7387 /************************************************************************ |
|
7388 * * |
|
7389 * The Parser * |
|
7390 * * |
|
7391 ************************************************************************/ |
|
7392 |
|
7393 /* |
|
7394 * a few forward declarations since we use a recursive call based |
|
7395 * implementation. |
|
7396 */ |
|
7397 static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt); |
|
7398 static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter); |
|
7399 static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt); |
|
7400 static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt); |
|
7401 static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, |
|
7402 int qualified); |
|
7403 |
|
7404 /** |
|
7405 * xmlXPathCurrentChar: |
|
7406 * @ctxt: the XPath parser context |
|
7407 * @cur: pointer to the beginning of the char |
|
7408 * @len: pointer to the length of the char read |
|
7409 * |
|
7410 * The current char value, if using UTF-8 this may actually span multiple |
|
7411 * bytes in the input buffer. |
|
7412 * |
|
7413 * Returns the current char value and its length |
|
7414 */ |
|
7415 |
|
7416 static int |
|
7417 xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) { |
|
7418 unsigned char c; |
|
7419 unsigned int val; |
|
7420 const xmlChar *cur; |
|
7421 |
|
7422 if (ctxt == NULL) |
|
7423 return(0); |
|
7424 cur = ctxt->cur; |
|
7425 |
|
7426 /* |
|
7427 * We are supposed to handle UTF8, check it's valid |
|
7428 * From rfc2044: encoding of the Unicode values on UTF-8: |
|
7429 * |
|
7430 * UCS-4 range (hex.) UTF-8 octet sequence (binary) |
|
7431 * 0000 0000-0000 007F 0xxxxxxx |
|
7432 * 0000 0080-0000 07FF 110xxxxx 10xxxxxx |
|
7433 * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx |
|
7434 * |
|
7435 * Check for the 0x110000 limit too |
|
7436 */ |
|
7437 c = *cur; |
|
7438 if (c & 0x80) { |
|
7439 if ((cur[1] & 0xc0) != 0x80) |
|
7440 goto encoding_error; |
|
7441 if ((c & 0xe0) == 0xe0) { |
|
7442 |
|
7443 if ((cur[2] & 0xc0) != 0x80) |
|
7444 goto encoding_error; |
|
7445 if ((c & 0xf0) == 0xf0) { |
|
7446 if (((c & 0xf8) != 0xf0) || |
|
7447 ((cur[3] & 0xc0) != 0x80)) |
|
7448 goto encoding_error; |
|
7449 /* 4-byte code */ |
|
7450 *len = 4; |
|
7451 val = (cur[0] & 0x7) << 18; |
|
7452 val |= (cur[1] & 0x3f) << 12; |
|
7453 val |= (cur[2] & 0x3f) << 6; |
|
7454 val |= cur[3] & 0x3f; |
|
7455 } else { |
|
7456 /* 3-byte code */ |
|
7457 *len = 3; |
|
7458 val = (cur[0] & 0xf) << 12; |
|
7459 val |= (cur[1] & 0x3f) << 6; |
|
7460 val |= cur[2] & 0x3f; |
|
7461 } |
|
7462 } else { |
|
7463 /* 2-byte code */ |
|
7464 *len = 2; |
|
7465 val = (cur[0] & 0x1f) << 6; |
|
7466 val |= cur[1] & 0x3f; |
|
7467 } |
|
7468 if (!IS_CHAR(val)) { |
|
7469 XP_ERROR0(XPATH_INVALID_CHAR_ERROR); |
|
7470 } |
|
7471 return(val); |
|
7472 } else { |
|
7473 /* 1-byte code */ |
|
7474 *len = 1; |
|
7475 return((int) *cur); |
|
7476 } |
|
7477 encoding_error: |
|
7478 /* |
|
7479 * If we detect an UTF8 error that probably means that the |
|
7480 * input encoding didn't get properly advertised in the |
|
7481 * declaration header. Report the error and switch the encoding |
|
7482 * to ISO-Latin-1 (if you don't like this policy, just declare the |
|
7483 * encoding !) |
|
7484 */ |
|
7485 *len = 0; |
|
7486 XP_ERROR0(XPATH_ENCODING_ERROR); |
|
7487 } |
|
7488 |
|
7489 /** |
|
7490 * xmlXPathParseNCName: |
|
7491 * @ctxt: the XPath Parser context |
|
7492 * |
|
7493 * parse an XML namespace non qualified name. |
|
7494 * |
|
7495 * [NS 3] NCName ::= (Letter | '_') (NCNameChar)* |
|
7496 * |
|
7497 * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' | |
|
7498 * CombiningChar | Extender |
|
7499 * |
|
7500 * Returns the namespace name or NULL |
|
7501 */ |
|
7502 |
|
7503 xmlChar * |
|
7504 xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) { |
|
7505 const xmlChar *in; |
|
7506 xmlChar *ret; |
|
7507 int count = 0; |
|
7508 |
|
7509 /* |
|
7510 * Accelerator for simple ASCII names |
|
7511 */ |
|
7512 in = ctxt->cur; |
|
7513 if (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7514 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7515 (*in == '_')) { |
|
7516 in++; |
|
7517 while (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7518 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7519 ((*in >= 0x30) && (*in <= 0x39)) || |
|
7520 (*in == '_') || (*in == '.') || |
|
7521 (*in == '-')) |
|
7522 in++; |
|
7523 if ((*in == ' ') || (*in == '>') || (*in == '/') || |
|
7524 (*in == '[') || (*in == ']') || (*in == ':') || |
|
7525 (*in == '@') || (*in == '*')) { |
|
7526 count = in - ctxt->cur; |
|
7527 if (count == 0) |
|
7528 return(NULL); |
|
7529 ret = xmlStrndup(ctxt->cur, count); |
|
7530 if(OOM_FLAG) return(NULL); |
|
7531 ctxt->cur = in; |
|
7532 return(ret); |
|
7533 } |
|
7534 } |
|
7535 return(xmlXPathParseNameComplex(ctxt, 0)); |
|
7536 } |
|
7537 |
|
7538 |
|
7539 /** |
|
7540 * xmlXPathParseQName: |
|
7541 * @ctxt: the XPath Parser context |
|
7542 * @prefix: a xmlChar ** |
|
7543 * |
|
7544 * parse an XML qualified name |
|
7545 * |
|
7546 * [NS 5] QName ::= (Prefix ':')? LocalPart |
|
7547 * |
|
7548 * [NS 6] Prefix ::= NCName |
|
7549 * |
|
7550 * [NS 7] LocalPart ::= NCName |
|
7551 * |
|
7552 * Returns the function returns the local part, and prefix is updated |
|
7553 * to get the Prefix if any. |
|
7554 */ |
|
7555 |
|
7556 static xmlChar * |
|
7557 xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) { |
|
7558 xmlChar *ret = NULL; |
|
7559 |
|
7560 *prefix = NULL; |
|
7561 ret = xmlXPathParseNCName(ctxt); |
|
7562 if (CUR == ':') { |
|
7563 *prefix = ret; |
|
7564 NEXT; |
|
7565 ret = xmlXPathParseNCName(ctxt); |
|
7566 } |
|
7567 return(ret); |
|
7568 } |
|
7569 |
|
7570 /** |
|
7571 * xmlXPathParseName: |
|
7572 * @ctxt: the XPath Parser context |
|
7573 * |
|
7574 * parse an XML name |
|
7575 * |
|
7576 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | |
|
7577 * CombiningChar | Extender |
|
7578 * |
|
7579 * [5] Name ::= (Letter | '_' | ':') (NameChar)* |
|
7580 * |
|
7581 * Returns the namespace name or NULL |
|
7582 */ |
|
7583 |
|
7584 xmlChar * |
|
7585 xmlXPathParseName(xmlXPathParserContextPtr ctxt) { |
|
7586 const xmlChar *in; |
|
7587 xmlChar *ret; |
|
7588 int count = 0; |
|
7589 |
|
7590 /* |
|
7591 * Accelerator for simple ASCII names |
|
7592 */ |
|
7593 in = ctxt->cur; |
|
7594 if (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7595 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7596 (*in == '_') || (*in == ':')) { |
|
7597 in++; |
|
7598 while (((*in >= 0x61) && (*in <= 0x7A)) || |
|
7599 ((*in >= 0x41) && (*in <= 0x5A)) || |
|
7600 ((*in >= 0x30) && (*in <= 0x39)) || |
|
7601 (*in == '_') || (*in == '-') || |
|
7602 (*in == ':') || (*in == '.')) |
|
7603 in++; |
|
7604 if ((*in > 0) && (*in < 0x80)) { |
|
7605 count = in - ctxt->cur; |
|
7606 ret = xmlStrndup(ctxt->cur, count); |
|
7607 ctxt->cur = in; |
|
7608 return(ret); |
|
7609 } |
|
7610 } |
|
7611 return(xmlXPathParseNameComplex(ctxt, 1)); |
|
7612 } |
|
7613 |
|
7614 /* |
|
7615 * OOM: possible --> check OOM flag |
|
7616 */ |
|
7617 static xmlChar* |
|
7618 xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) |
|
7619 { |
|
7620 xmlChar buf[XML_MAX_NAMELEN + 5]; |
|
7621 int len = 0, l; // TODO: rename 'l' variable |
|
7622 int c; |
|
7623 |
|
7624 /* |
|
7625 * Handler for more complex cases |
|
7626 */ |
|
7627 c = CUR_CHAR(l); |
|
7628 if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */ |
|
7629 (c == '[') || (c == ']') || (c == '@') || /* accelerators */ |
|
7630 (c == '*') || /* accelerators */ |
|
7631 (!IS_LETTER(c) && |
|
7632 (c != '_') && |
|
7633 qualified && |
|
7634 (c != ':') |
|
7635 ) |
|
7636 ) |
|
7637 { |
|
7638 return(NULL); |
|
7639 } |
|
7640 |
|
7641 while((c != ' ') && (c != '>') && (c != '/') |
|
7642 && /* test bigname.xml */ |
|
7643 ( |
|
7644 IS_LETTER(c) || |
|
7645 IS_DIGIT(c) || |
|
7646 (c == '.') || |
|
7647 (c == '-') || |
|
7648 (c == '_') || |
|
7649 (qualified && (c == ':')) || |
|
7650 IS_COMBINING(c) || |
|
7651 IS_EXTENDER(c) |
|
7652 ) |
|
7653 ) |
|
7654 { |
|
7655 COPY_BUF(l,buf,len,c); |
|
7656 NEXTL(l); |
|
7657 c = CUR_CHAR(l); |
|
7658 if (len >= XML_MAX_NAMELEN) |
|
7659 { |
|
7660 /* |
|
7661 * Okay someone managed to make a huge name, so he's ready to pay |
|
7662 * for the processing speed. |
|
7663 */ |
|
7664 xmlChar* buffer; |
|
7665 int max = len * 2; |
|
7666 |
|
7667 buffer = (xmlChar*) xmlMallocAtomic(max * sizeof(xmlChar)); |
|
7668 if (!buffer) { |
|
7669 XP_ERROR0(XPATH_MEMORY_ERROR); |
|
7670 } |
|
7671 memcpy(buffer, buf, len); |
|
7672 while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */ |
|
7673 (c == '.') || (c == '-') || |
|
7674 (c == '_') || ((qualified) && (c == ':')) || |
|
7675 (IS_COMBINING(c)) || |
|
7676 (IS_EXTENDER(c))) |
|
7677 { |
|
7678 if (len + 10 > max) |
|
7679 { |
|
7680 xmlChar* tmp; |
|
7681 max *= 2; |
|
7682 // DONE: Fix xmlRealloc |
|
7683 tmp = (xmlChar*) xmlRealloc(buffer, max * sizeof(xmlChar)); |
|
7684 if (!tmp) { |
|
7685 xmlFree(buffer); |
|
7686 XP_ERROR0(XPATH_MEMORY_ERROR); |
|
7687 } |
|
7688 buffer = tmp; |
|
7689 } |
|
7690 COPY_BUF(l,buffer,len,c); |
|
7691 NEXTL(l); |
|
7692 c = CUR_CHAR(l); |
|
7693 } |
|
7694 buffer[len] = 0; |
|
7695 return(buffer); |
|
7696 } |
|
7697 } |
|
7698 if (len == 0) |
|
7699 return(NULL); |
|
7700 return(xmlStrndup(buf, len)); |
|
7701 } |
|
7702 |
|
7703 #define MAX_FRAC 20 |
|
7704 |
|
7705 /* |
|
7706 * These are used as divisors for the fractional part of a number. |
|
7707 * Since the table includes 1.0 (representing '0' fractional digits), |
|
7708 * it must be dimensioned at MAX_FRAC+1 (bug 133921) |
|
7709 */ |
|
7710 static const double my_pow10[MAX_FRAC+1] = { |
|
7711 1.0, 10.0, 100.0, 1000.0, 10000.0, |
|
7712 100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0, |
|
7713 10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0, |
|
7714 100000000000000.0, |
|
7715 1000000000000000.0, 10000000000000000.0, 100000000000000000.0, |
|
7716 1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0 |
|
7717 }; |
|
7718 |
|
7719 |
|
7720 /** |
|
7721 * xmlXPathStringEvalNumber: |
|
7722 * @str: A string to scan |
|
7723 * |
|
7724 * [30a] Float ::= Number ('e' Digits?)? |
|
7725 * |
|
7726 * [30] Number ::= Digits ('.' Digits?)? |
|
7727 * | '.' Digits |
|
7728 * [31] Digits ::= [0-9]+ |
|
7729 * |
|
7730 * Compile a Number in the string |
|
7731 * In complement of the Number expression, this function also handles |
|
7732 * negative values : '-' Number. |
|
7733 * |
|
7734 * Returns the double value. |
|
7735 * |
|
7736 * OOM: never |
|
7737 */ |
|
7738 double |
|
7739 xmlXPathStringEvalNumber(const xmlChar *str) { |
|
7740 const xmlChar *cur = str; |
|
7741 double ret; |
|
7742 int ok = 0; |
|
7743 int isneg = 0; |
|
7744 int exponent = 0; |
|
7745 int is_exponent_negative = 0; |
|
7746 #ifdef __GNUC__ |
|
7747 unsigned long tmp = 0; |
|
7748 double temp; |
|
7749 #endif |
|
7750 if (cur == NULL) |
|
7751 return(0); |
|
7752 // |
|
7753 while (IS_BLANK_CH(*cur)) |
|
7754 { |
|
7755 cur++; |
|
7756 } |
|
7757 |
|
7758 if ((*cur != '.') && |
|
7759 ((*cur < '0') || (*cur > '9')) && |
|
7760 (*cur != '-')) |
|
7761 { |
|
7762 return(xmlXPathNAN); |
|
7763 } |
|
7764 if (*cur == '-') { |
|
7765 isneg = 1; |
|
7766 cur++; |
|
7767 } |
|
7768 |
|
7769 #ifdef __GNUC__ |
|
7770 /* |
|
7771 * tmp/temp is a workaround against a gcc compiler bug |
|
7772 * http://veillard.com/gcc.bug |
|
7773 */ |
|
7774 ret = 0; |
|
7775 while ((*cur >= '0') && (*cur <= '9')) |
|
7776 { |
|
7777 ret = ret * 10; |
|
7778 tmp = (*cur - '0'); |
|
7779 ok = 1; |
|
7780 cur++; |
|
7781 temp = (double) tmp; |
|
7782 ret = ret + temp; |
|
7783 } |
|
7784 #else |
|
7785 ret = 0; |
|
7786 while ((*cur >= '0') && (*cur <= '9')) |
|
7787 { |
|
7788 ret = ret * 10 + (*cur - '0'); |
|
7789 ok = 1; |
|
7790 cur++; |
|
7791 } |
|
7792 #endif |
|
7793 |
|
7794 if (*cur == '.') |
|
7795 { |
|
7796 int v, frac = 0; |
|
7797 double fraction = 0; |
|
7798 |
|
7799 cur++; |
|
7800 if (((*cur < '0') || (*cur > '9')) && (!ok)) |
|
7801 { |
|
7802 return(xmlXPathNAN); |
|
7803 } |
|
7804 while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC)) |
|
7805 { |
|
7806 v = (*cur - '0'); |
|
7807 fraction = fraction * 10 + v; |
|
7808 frac = frac + 1; |
|
7809 cur++; |
|
7810 } |
|
7811 fraction /= my_pow10[frac]; |
|
7812 ret = ret + fraction; |
|
7813 while ((*cur >= '0') && (*cur <= '9')) |
|
7814 { |
|
7815 cur++; |
|
7816 } |
|
7817 } |
|
7818 if ((*cur == 'e') || (*cur == 'E')) |
|
7819 { |
|
7820 cur++; |
|
7821 if (*cur == '-') |
|
7822 { |
|
7823 is_exponent_negative = 1; |
|
7824 cur++; |
|
7825 } |
|
7826 while ((*cur >= '0') && (*cur <= '9')) |
|
7827 { |
|
7828 exponent = exponent * 10 + (*cur - '0'); |
|
7829 cur++; |
|
7830 } |
|
7831 } |
|
7832 while (IS_BLANK_CH(*cur)) |
|
7833 { |
|
7834 cur++; |
|
7835 } |
|
7836 if (*cur != 0) |
|
7837 return(xmlXPathNAN); |
|
7838 if (isneg) |
|
7839 ret = -ret; |
|
7840 if (is_exponent_negative) |
|
7841 exponent = -exponent; |
|
7842 // |
|
7843 ret *= pow(10.0, (double)exponent); |
|
7844 return(ret); |
|
7845 } |
|
7846 |
|
7847 /** |
|
7848 * xmlXPathCompNumber: |
|
7849 * @ctxt: the XPath Parser context |
|
7850 * |
|
7851 * [30] Number ::= Digits ('.' Digits?)? |
|
7852 * | '.' Digits |
|
7853 * [31] Digits ::= [0-9]+ |
|
7854 * |
|
7855 * Compile a Number, then push it on the stack |
|
7856 * |
|
7857 */ |
|
7858 static void |
|
7859 xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) |
|
7860 { |
|
7861 double ret = 0.0; |
|
7862 double mult = 1; |
|
7863 int ok = 0; |
|
7864 int exponent = 0; |
|
7865 int is_exponent_negative = 0; |
|
7866 #ifdef __GNUC__ |
|
7867 unsigned long tmp = 0; |
|
7868 double temp; |
|
7869 #endif |
|
7870 |
|
7871 CHECK_ERROR; |
|
7872 if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) { |
|
7873 XP_ERROR(XPATH_NUMBER_ERROR); |
|
7874 } |
|
7875 #ifdef __GNUC__ |
|
7876 /* |
|
7877 * tmp/temp is a workaround against a gcc compiler bug |
|
7878 * http://veillard.com/gcc.bug |
|
7879 */ |
|
7880 ret = 0; |
|
7881 while ((CUR >= '0') && (CUR <= '9')) { |
|
7882 ret = ret * 10; |
|
7883 tmp = (CUR - '0'); |
|
7884 ok = 1; |
|
7885 NEXT; |
|
7886 temp = (double) tmp; |
|
7887 ret = ret + temp; |
|
7888 } |
|
7889 #else |
|
7890 ret = 0; |
|
7891 while ((CUR >= '0') && (CUR <= '9')) { |
|
7892 ret = ret * 10 + (CUR - '0'); |
|
7893 ok = 1; |
|
7894 NEXT; |
|
7895 } |
|
7896 #endif |
|
7897 if (CUR == '.') { |
|
7898 NEXT; |
|
7899 if (((CUR < '0') || (CUR > '9')) && (!ok)) { |
|
7900 XP_ERROR(XPATH_NUMBER_ERROR); |
|
7901 } |
|
7902 while ((CUR >= '0') && (CUR <= '9')) { |
|
7903 mult /= 10; |
|
7904 ret = ret + (CUR - '0') * mult; |
|
7905 NEXT; |
|
7906 } |
|
7907 } |
|
7908 if ((CUR == 'e') || (CUR == 'E')) { |
|
7909 NEXT; |
|
7910 if (CUR == '-') { |
|
7911 is_exponent_negative = 1; |
|
7912 NEXT; |
|
7913 } |
|
7914 while ((CUR >= '0') && (CUR <= '9')) { |
|
7915 exponent = exponent * 10 + (CUR - '0'); |
|
7916 NEXT; |
|
7917 } |
|
7918 if (is_exponent_negative) |
|
7919 exponent = -exponent; |
|
7920 ret *= pow(10.0, (double) exponent); |
|
7921 } |
|
7922 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0, |
|
7923 xmlXPathNewFloat(ret), NULL); |
|
7924 } |
|
7925 |
|
7926 /** |
|
7927 * xmlXPathParseLiteral: |
|
7928 * @ctxt: the XPath Parser context |
|
7929 * |
|
7930 * Parse a Literal |
|
7931 * |
|
7932 * [29] Literal ::= '"' [^"]* '"' |
|
7933 * | "'" [^']* "'" |
|
7934 * |
|
7935 * Returns the value found or NULL in case of error |
|
7936 */ |
|
7937 static xmlChar * |
|
7938 xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) { |
|
7939 const xmlChar *q; |
|
7940 xmlChar *ret = NULL; |
|
7941 |
|
7942 if (CUR == '"') { |
|
7943 NEXT; |
|
7944 q = CUR_PTR; |
|
7945 while ((IS_CHAR_CH(CUR)) && (CUR != '"')) |
|
7946 NEXT; |
|
7947 if (!IS_CHAR_CH(CUR)) { |
|
7948 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR); |
|
7949 } else { |
|
7950 ret = xmlStrndup(q, CUR_PTR - q); |
|
7951 NEXT; |
|
7952 } |
|
7953 } else if (CUR == '\'') { |
|
7954 NEXT; |
|
7955 q = CUR_PTR; |
|
7956 while ((IS_CHAR_CH(CUR)) && (CUR != '\'')) |
|
7957 NEXT; |
|
7958 if (!IS_CHAR_CH(CUR)) { |
|
7959 XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR); |
|
7960 } else { |
|
7961 ret = xmlStrndup(q, CUR_PTR - q); |
|
7962 NEXT; |
|
7963 } |
|
7964 } else { |
|
7965 XP_ERROR0(XPATH_START_LITERAL_ERROR); |
|
7966 } |
|
7967 return(ret); |
|
7968 } |
|
7969 |
|
7970 /** |
|
7971 * xmlXPathCompLiteral: |
|
7972 * @ctxt: the XPath Parser context |
|
7973 * |
|
7974 * Parse a Literal and push it on the stack. |
|
7975 * |
|
7976 * [29] Literal ::= '"' [^"]* '"' |
|
7977 * | "'" [^']* "'" |
|
7978 * |
|
7979 * TODO: xmlXPathCompLiteral memory allocation could be improved. |
|
7980 */ |
|
7981 static void |
|
7982 xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) { |
|
7983 const xmlChar *q; |
|
7984 xmlChar *ret = NULL; |
|
7985 |
|
7986 if (CUR == '"') { |
|
7987 NEXT; |
|
7988 q = CUR_PTR; |
|
7989 while ((IS_CHAR_CH(CUR)) && (CUR != '"')) |
|
7990 NEXT; |
|
7991 if (!IS_CHAR_CH(CUR)) { |
|
7992 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); |
|
7993 } else { |
|
7994 ret = xmlStrndup(q, CUR_PTR - q); |
|
7995 NEXT; |
|
7996 } |
|
7997 } else if (CUR == '\'') { |
|
7998 NEXT; |
|
7999 q = CUR_PTR; |
|
8000 while ((IS_CHAR_CH(CUR)) && (CUR != '\'')) |
|
8001 NEXT; |
|
8002 if (!IS_CHAR_CH(CUR)) { |
|
8003 XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); |
|
8004 } else { |
|
8005 ret = xmlStrndup(q, CUR_PTR - q); |
|
8006 NEXT; |
|
8007 } |
|
8008 } else { |
|
8009 XP_ERROR(XPATH_START_LITERAL_ERROR); |
|
8010 } |
|
8011 if (ret == NULL) return; |
|
8012 PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0, |
|
8013 xmlXPathNewString(ret), NULL); |
|
8014 xmlFree(ret); |
|
8015 } |
|
8016 |
|
8017 /** |
|
8018 * xmlXPathCompVariableReference: |
|
8019 * @ctxt: the XPath Parser context |
|
8020 * |
|
8021 * Parse a VariableReference, evaluate it and push it on the stack. |
|
8022 * |
|
8023 * The variable bindings consist of a mapping from variable names |
|
8024 * to variable values. The value of a variable is an object, which can be |
|
8025 * of any of the types that are possible for the value of an expression, |
|
8026 * and may also be of additional types not specified here. |
|
8027 * |
|
8028 * Early evaluation is possible since: |
|
8029 * The variable bindings [...] used to evaluate a subexpression are |
|
8030 * always the same as those used to evaluate the containing expression. |
|
8031 * |
|
8032 * [36] VariableReference ::= '$' QName |
|
8033 */ |
|
8034 static void |
|
8035 xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) { |
|
8036 xmlChar *name; |
|
8037 xmlChar *prefix; |
|
8038 |
|
8039 SKIP_BLANKS; |
|
8040 if (CUR != '$') { |
|
8041 XP_ERROR(XPATH_VARIABLE_REF_ERROR); |
|
8042 } |
|
8043 NEXT; |
|
8044 name = xmlXPathParseQName(ctxt, &prefix); |
|
8045 if (name == NULL) { |
|
8046 XP_ERROR(XPATH_VARIABLE_REF_ERROR); |
|
8047 } |
|
8048 ctxt->comp->last = -1; |
|
8049 PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0, |
|
8050 name, prefix); |
|
8051 SKIP_BLANKS; |
|
8052 } |
|
8053 |
|
8054 /** |
|
8055 * xmlXPathIsNodeType: |
|
8056 * @name: a name string |
|
8057 * |
|
8058 * Is the name given a NodeType one. |
|
8059 * |
|
8060 * [38] NodeType ::= 'comment' |
|
8061 * | 'text' |
|
8062 * | 'processing-instruction' |
|
8063 * | 'node' |
|
8064 * |
|
8065 * Returns 1 if true 0 otherwise |
|
8066 */ |
|
8067 int |
|
8068 xmlXPathIsNodeType(const xmlChar *name) { |
|
8069 if (name == NULL) |
|
8070 return(0); |
|
8071 |
|
8072 if (xmlStrEqual(name, BAD_CAST "node")) |
|
8073 return(1); |
|
8074 if (xmlStrEqual(name, BAD_CAST "text")) |
|
8075 return(1); |
|
8076 if (xmlStrEqual(name, BAD_CAST "comment")) |
|
8077 return(1); |
|
8078 if (xmlStrEqual(name, BAD_CAST "processing-instruction")) |
|
8079 return(1); |
|
8080 return(0); |
|
8081 } |
|
8082 |
|
8083 /** |
|
8084 * xmlXPathCompFunctionCall: |
|
8085 * @ctxt: the XPath Parser context |
|
8086 * |
|
8087 * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')' |
|
8088 * [17] Argument ::= Expr |
|
8089 * |
|
8090 * Compile a function call, the evaluation of all arguments are |
|
8091 * pushed on the stack |
|
8092 */ |
|
8093 static void |
|
8094 xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) { |
|
8095 xmlChar *name; |
|
8096 xmlChar *prefix; |
|
8097 int nbargs = 0; |
|
8098 |
|
8099 name = xmlXPathParseQName(ctxt, &prefix); |
|
8100 if (name == NULL) { |
|
8101 XP_ERROR(XPATH_EXPR_ERROR); |
|
8102 } |
|
8103 SKIP_BLANKS; |
|
8104 #ifdef DEBUG_EXPR |
|
8105 if (prefix == NULL) |
|
8106 xmlGenericError(xmlGenericErrorContext, "Calling function %s\n", |
|
8107 name); |
|
8108 else |
|
8109 xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n", |
|
8110 prefix, name); |
|
8111 #endif |
|
8112 |
|
8113 if (CUR != '(') { |
|
8114 XP_ERROR(XPATH_EXPR_ERROR); |
|
8115 } |
|
8116 NEXT; |
|
8117 SKIP_BLANKS; |
|
8118 |
|
8119 ctxt->comp->last = -1; |
|
8120 if (CUR != ')') { |
|
8121 while (CUR != 0) { |
|
8122 int op1 = ctxt->comp->last; |
|
8123 ctxt->comp->last = -1; |
|
8124 xmlXPathCompileExpr(ctxt); |
|
8125 if(OOM_FLAG) |
|
8126 { |
|
8127 xmlFree(name); |
|
8128 return; |
|
8129 } |
|
8130 CHECK_ERROR; |
|
8131 PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0); |
|
8132 nbargs++; |
|
8133 if (CUR == ')') break; |
|
8134 if (CUR != ',') { |
|
8135 XP_ERROR(XPATH_EXPR_ERROR); |
|
8136 } |
|
8137 NEXT; |
|
8138 SKIP_BLANKS; |
|
8139 } |
|
8140 } |
|
8141 if ( PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1) |
|
8142 goto Err; |
|
8143 NEXT; |
|
8144 SKIP_BLANKS; |
|
8145 return; |
|
8146 Err: |
|
8147 if(name) |
|
8148 xmlFree(name); |
|
8149 if(prefix) |
|
8150 xmlFree(prefix); |
|
8151 } |
|
8152 |
|
8153 /** |
|
8154 * xmlXPathCompPrimaryExpr: |
|
8155 * @ctxt: the XPath Parser context |
|
8156 * |
|
8157 * [15] PrimaryExpr ::= VariableReference |
|
8158 * | '(' Expr ')' |
|
8159 * | Literal |
|
8160 * | Number |
|
8161 * | FunctionCall |
|
8162 * |
|
8163 * Compile a primary expression. |
|
8164 */ |
|
8165 static void |
|
8166 xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) { |
|
8167 SKIP_BLANKS; |
|
8168 if (CUR == '$') xmlXPathCompVariableReference(ctxt); |
|
8169 else if (CUR == '(') { |
|
8170 NEXT; |
|
8171 SKIP_BLANKS; |
|
8172 xmlXPathCompileExpr(ctxt); |
|
8173 CHECK_ERROR; |
|
8174 if (CUR != ')') { |
|
8175 XP_ERROR(XPATH_EXPR_ERROR); |
|
8176 } |
|
8177 NEXT; |
|
8178 SKIP_BLANKS; |
|
8179 } else if (IS_DIGIT_CH(CUR) || (CUR == '.' && IS_DIGIT_CH(NXT(1)))) { |
|
8180 xmlXPathCompNumber(ctxt); |
|
8181 } else if ((CUR == '\'') || (CUR == '"')) { |
|
8182 xmlXPathCompLiteral(ctxt); |
|
8183 } else { |
|
8184 xmlXPathCompFunctionCall(ctxt); |
|
8185 } |
|
8186 SKIP_BLANKS; |
|
8187 } |
|
8188 |
|
8189 /** |
|
8190 * xmlXPathCompFilterExpr: |
|
8191 * @ctxt: the XPath Parser context |
|
8192 * |
|
8193 * [20] FilterExpr ::= PrimaryExpr |
|
8194 * | FilterExpr Predicate |
|
8195 * |
|
8196 * Compile a filter expression. |
|
8197 * Square brackets are used to filter expressions in the same way that |
|
8198 * they are used in location paths. It is an error if the expression to |
|
8199 * be filtered does not evaluate to a node-set. The context node list |
|
8200 * used for evaluating the expression in square brackets is the node-set |
|
8201 * to be filtered listed in document order. |
|
8202 */ |
|
8203 |
|
8204 static void |
|
8205 xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) { |
|
8206 xmlXPathCompPrimaryExpr(ctxt); |
|
8207 CHECK_ERROR; |
|
8208 SKIP_BLANKS; |
|
8209 |
|
8210 while (CUR == '[') { |
|
8211 xmlXPathCompPredicate(ctxt, 1); |
|
8212 SKIP_BLANKS; |
|
8213 } |
|
8214 |
|
8215 |
|
8216 } |
|
8217 |
|
8218 /** |
|
8219 * xmlXPathScanName: |
|
8220 * @ctxt: the XPath Parser context |
|
8221 * |
|
8222 * Trickery: parse an XML name but without consuming the input flow |
|
8223 * Needed to avoid insanity in the parser state. |
|
8224 * |
|
8225 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | |
|
8226 * CombiningChar | Extender |
|
8227 * |
|
8228 * [5] Name ::= (Letter | '_' | ':') (NameChar)* |
|
8229 * |
|
8230 * [6] Names ::= Name (S Name)* |
|
8231 * |
|
8232 * Returns the Name parsed or NULL |
|
8233 * |
|
8234 * OOM: possible --> sets OOM when NULL is return |
|
8235 */ |
|
8236 |
|
8237 static xmlChar* |
|
8238 xmlXPathScanName(xmlXPathParserContextPtr ctxt) { |
|
8239 xmlChar buf[XML_MAX_NAMELEN]; |
|
8240 int len = 0; |
|
8241 |
|
8242 SKIP_BLANKS; |
|
8243 if (!IS_LETTER_CH(CUR) && |
|
8244 (CUR != '_') && |
|
8245 (CUR != ':')) |
|
8246 { |
|
8247 return(NULL); |
|
8248 } |
|
8249 // TODO: OPTIMIZE: Repeated while conditions: NXT(len) |
|
8250 while ((IS_LETTER_CH(NXT(len))) || |
|
8251 (IS_DIGIT_CH(NXT(len))) || |
|
8252 (NXT(len) == '.') || |
|
8253 (NXT(len) == '-') || |
|
8254 (NXT(len) == '_') || |
|
8255 (NXT(len) == ':') || |
|
8256 (IS_COMBINING_CH(NXT(len))) || |
|
8257 (IS_EXTENDER_CH(NXT(len)))) |
|
8258 { |
|
8259 buf[len] = NXT(len); |
|
8260 len++; |
|
8261 if (len >= XML_MAX_NAMELEN) // TODO: Make a configurable parameter |
|
8262 { |
|
8263 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlScanName: reached XML_MAX_NAMELEN limit\n")); |
|
8264 while ( |
|
8265 (IS_LETTER_CH(NXT(len))) || |
|
8266 (IS_DIGIT_CH(NXT(len))) || |
|
8267 (NXT(len) == '.') || |
|
8268 (NXT(len) == '-') || |
|
8269 (NXT(len) == '_') || |
|
8270 (NXT(len) == ':') || |
|
8271 (IS_COMBINING_CH(NXT(len))) || |
|
8272 (IS_EXTENDER_CH(NXT(len)))) |
|
8273 { |
|
8274 len++; |
|
8275 } |
|
8276 break; // while |
|
8277 } |
|
8278 } |
|
8279 return(xmlStrndup(buf, len)); // may cause OOM |
|
8280 } |
|
8281 |
|
8282 /** |
|
8283 * xmlXPathCompPathExpr: |
|
8284 * @ctxt: the XPath Parser context |
|
8285 * |
|
8286 * [19] PathExpr ::= LocationPath |
|
8287 * | FilterExpr |
|
8288 * | FilterExpr '/' RelativeLocationPath |
|
8289 * | FilterExpr '//' RelativeLocationPath |
|
8290 * |
|
8291 * Compile a path expression. |
|
8292 * The / operator and // operators combine an arbitrary expression |
|
8293 * and a relative location path. It is an error if the expression |
|
8294 * does not evaluate to a node-set. |
|
8295 * The / operator does composition in the same way as when / is |
|
8296 * used in a location path. As in location paths, // is short for |
|
8297 * /descendant-or-self::node()/. |
|
8298 * |
|
8299 * OOM: |
|
8300 */ |
|
8301 |
|
8302 static void |
|
8303 xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) { |
|
8304 int lc = 1; /* Should we branch to LocationPath ? */ |
|
8305 xmlChar *name = NULL; /* we may have to preparse a name to find out */ |
|
8306 |
|
8307 SKIP_BLANKS; |
|
8308 if ((CUR == '$') || (CUR == '(') || (IS_DIGIT_CH(CUR)) || |
|
8309 (CUR == '\'') || (CUR == '"') || (CUR == '.' && IS_DIGIT_CH(NXT(1)))) |
|
8310 { |
|
8311 lc = 0; |
|
8312 } else if (CUR == '*') { |
|
8313 /* relative or absolute location path */ |
|
8314 lc = 1; |
|
8315 } else if (CUR == '/') { |
|
8316 /* relative or absolute location path */ |
|
8317 lc = 1; |
|
8318 } else if (CUR == '@') { |
|
8319 /* relative abbreviated attribute location path */ |
|
8320 lc = 1; |
|
8321 } else if (CUR == '.') { |
|
8322 /* relative abbreviated attribute location path */ |
|
8323 lc = 1; |
|
8324 } else { |
|
8325 /* |
|
8326 * Problem is finding if we have a name here whether it's: |
|
8327 * - a nodetype |
|
8328 * - a function call in which case it's followed by '(' |
|
8329 * - an axis in which case it's followed by ':' |
|
8330 * - a element name |
|
8331 * We do an a priori analysis here rather than having to |
|
8332 * maintain parsed token content through the recursive function |
|
8333 * calls. This looks uglier but makes the code easier to |
|
8334 * read/write/debug. |
|
8335 */ |
|
8336 SKIP_BLANKS; |
|
8337 name = xmlXPathScanName(ctxt); |
|
8338 if(OOM_FLAG) return; |
|
8339 if (name && (xmlStrstr(name, (xmlChar *) "::") != NULL)) { |
|
8340 #ifdef DEBUG_STEP |
|
8341 xmlGenericError(xmlGenericErrorContext, "PathExpr: Axis\n"); |
|
8342 #endif |
|
8343 lc = 1; |
|
8344 xmlFree(name); |
|
8345 } else if (name != NULL) { |
|
8346 int len =xmlStrlen(name); |
|
8347 |
|
8348 |
|
8349 while (NXT(len) != 0) { |
|
8350 if (NXT(len) == '/') { |
|
8351 /* element name */ |
|
8352 #ifdef DEBUG_STEP |
|
8353 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8354 #endif |
|
8355 lc = 1; |
|
8356 break; |
|
8357 } else if (IS_BLANK_CH(NXT(len))) { |
|
8358 /* ignore blanks */ |
|
8359 ; |
|
8360 } else if (NXT(len) == ':') { |
|
8361 #ifdef DEBUG_STEP |
|
8362 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8363 #endif |
|
8364 lc = 1; |
|
8365 break; |
|
8366 } else if ((NXT(len) == '(')) { |
|
8367 /* Note Type or Function */ |
|
8368 if (xmlXPathIsNodeType(name)) { |
|
8369 #ifdef DEBUG_STEP |
|
8370 xmlGenericError(xmlGenericErrorContext, "PathExpr: Type search\n"); |
|
8371 #endif |
|
8372 lc = 1; |
|
8373 } else { |
|
8374 #ifdef DEBUG_STEP |
|
8375 xmlGenericError(xmlGenericErrorContext, "PathExpr: function call\n"); |
|
8376 #endif |
|
8377 lc = 0; |
|
8378 } |
|
8379 break; |
|
8380 } else if ((NXT(len) == '[')) { |
|
8381 /* element name */ |
|
8382 #ifdef DEBUG_STEP |
|
8383 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8384 #endif |
|
8385 lc = 1; |
|
8386 break; |
|
8387 } else if ((NXT(len) == '<') || |
|
8388 (NXT(len) == '>') || |
|
8389 (NXT(len) == '=')) |
|
8390 { |
|
8391 lc = 1; |
|
8392 break; |
|
8393 } else { |
|
8394 lc = 1; |
|
8395 break; |
|
8396 } |
|
8397 len++; |
|
8398 } |
|
8399 if (NXT(len) == 0) { |
|
8400 #ifdef DEBUG_STEP |
|
8401 xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n"); |
|
8402 #endif |
|
8403 /* element name */ |
|
8404 lc = 1; |
|
8405 } |
|
8406 xmlFree(name); |
|
8407 } else { |
|
8408 /* make sure all cases are covered explicitly */ |
|
8409 XP_ERROR(XPATH_EXPR_ERROR); |
|
8410 } |
|
8411 } |
|
8412 |
|
8413 if (lc) { |
|
8414 if (CUR == '/') { |
|
8415 PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0); |
|
8416 } else { |
|
8417 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0); |
|
8418 } |
|
8419 xmlXPathCompLocationPath(ctxt); |
|
8420 if(OOM_FLAG) return; |
|
8421 } else { |
|
8422 xmlXPathCompFilterExpr(ctxt); |
|
8423 CHECK_ERROR; |
|
8424 if ((CUR == '/') && (NXT(1) == '/')) |
|
8425 { |
|
8426 SKIP(2); |
|
8427 SKIP_BLANKS; |
|
8428 |
|
8429 PUSH_LONG_EXPR( |
|
8430 XPATH_OP_COLLECT, |
|
8431 AXIS_DESCENDANT_OR_SELF, |
|
8432 NODE_TEST_TYPE, |
|
8433 NODE_TYPE_NODE, |
|
8434 NULL, |
|
8435 NULL); |
|
8436 |
|
8437 PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0); |
|
8438 |
|
8439 xmlXPathCompRelativeLocationPath(ctxt); |
|
8440 } else if (CUR == '/') { |
|
8441 xmlXPathCompRelativeLocationPath(ctxt); |
|
8442 } |
|
8443 } |
|
8444 SKIP_BLANKS; |
|
8445 } |
|
8446 |
|
8447 /** |
|
8448 * xmlXPathCompUnionExpr: |
|
8449 * @ctxt: the XPath Parser context |
|
8450 * |
|
8451 * [18] UnionExpr ::= PathExpr |
|
8452 * | UnionExpr '|' PathExpr |
|
8453 * |
|
8454 * Compile an union expression. |
|
8455 * |
|
8456 * OOM: |
|
8457 */ |
|
8458 |
|
8459 static void |
|
8460 xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) { |
|
8461 xmlXPathCompPathExpr(ctxt); |
|
8462 if(OOM_FLAG) return; |
|
8463 CHECK_ERROR; |
|
8464 SKIP_BLANKS; |
|
8465 while (CUR == '|') { |
|
8466 int op1 = ctxt->comp->last; |
|
8467 PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0); |
|
8468 |
|
8469 NEXT; |
|
8470 SKIP_BLANKS; |
|
8471 xmlXPathCompPathExpr(ctxt); |
|
8472 |
|
8473 PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0); |
|
8474 |
|
8475 SKIP_BLANKS; |
|
8476 } |
|
8477 } |
|
8478 |
|
8479 /** |
|
8480 * xmlXPathCompUnaryExpr: |
|
8481 * @ctxt: the XPath Parser context |
|
8482 * |
|
8483 * [27] UnaryExpr ::= UnionExpr |
|
8484 * | '-' UnaryExpr |
|
8485 * |
|
8486 * Compile an unary expression. |
|
8487 * |
|
8488 * OOM: |
|
8489 */ |
|
8490 |
|
8491 static void |
|
8492 xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) { |
|
8493 int minus = 0; |
|
8494 int found = 0; |
|
8495 |
|
8496 SKIP_BLANKS; |
|
8497 while (CUR == '-') { |
|
8498 minus = 1 - minus; |
|
8499 found = 1; |
|
8500 NEXT; |
|
8501 SKIP_BLANKS; |
|
8502 } |
|
8503 |
|
8504 xmlXPathCompUnionExpr(ctxt); |
|
8505 if(OOM_FLAG) return; |
|
8506 CHECK_ERROR; |
|
8507 if (found) { |
|
8508 if (minus) |
|
8509 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0); |
|
8510 else |
|
8511 PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0); |
|
8512 } |
|
8513 } |
|
8514 |
|
8515 /** |
|
8516 * xmlXPathCompMultiplicativeExpr: |
|
8517 * @ctxt: the XPath Parser context |
|
8518 * |
|
8519 * [26] MultiplicativeExpr ::= UnaryExpr |
|
8520 * | MultiplicativeExpr MultiplyOperator UnaryExpr |
|
8521 * | MultiplicativeExpr 'div' UnaryExpr |
|
8522 * | MultiplicativeExpr 'mod' UnaryExpr |
|
8523 * [34] MultiplyOperator ::= '*' |
|
8524 * |
|
8525 * Compile an Additive expression. |
|
8526 * |
|
8527 * OOM: |
|
8528 */ |
|
8529 |
|
8530 static void |
|
8531 xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) { |
|
8532 xmlXPathCompUnaryExpr(ctxt); |
|
8533 if(OOM_FLAG) return; |
|
8534 CHECK_ERROR; |
|
8535 SKIP_BLANKS; |
|
8536 while ((CUR == '*') || |
|
8537 ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) || |
|
8538 ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) { |
|
8539 int op = -1; |
|
8540 int op1 = ctxt->comp->last; |
|
8541 |
|
8542 if (CUR == '*') { |
|
8543 op = 0; |
|
8544 NEXT; |
|
8545 } else if (CUR == 'd') { |
|
8546 op = 1; |
|
8547 SKIP(3); |
|
8548 } else if (CUR == 'm') { |
|
8549 op = 2; |
|
8550 SKIP(3); |
|
8551 } |
|
8552 SKIP_BLANKS; |
|
8553 xmlXPathCompUnaryExpr(ctxt); |
|
8554 CHECK_ERROR; |
|
8555 PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0); |
|
8556 SKIP_BLANKS; |
|
8557 } |
|
8558 } |
|
8559 |
|
8560 /** |
|
8561 * xmlXPathCompAdditiveExpr: |
|
8562 * @ctxt: the XPath Parser context |
|
8563 * |
|
8564 * [25] AdditiveExpr ::= MultiplicativeExpr |
|
8565 * | AdditiveExpr '+' MultiplicativeExpr |
|
8566 * | AdditiveExpr '-' MultiplicativeExpr |
|
8567 * |
|
8568 * Compile an Additive expression. |
|
8569 * |
|
8570 * OOM: |
|
8571 */ |
|
8572 |
|
8573 static void |
|
8574 xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) { |
|
8575 |
|
8576 xmlXPathCompMultiplicativeExpr(ctxt); |
|
8577 if(OOM_FLAG) return; |
|
8578 CHECK_ERROR; |
|
8579 SKIP_BLANKS; |
|
8580 while ((CUR == '+') || (CUR == '-')) { |
|
8581 int plus; |
|
8582 int op1 = ctxt->comp->last; |
|
8583 |
|
8584 if (CUR == '+') plus = 1; |
|
8585 else plus = 0; |
|
8586 NEXT; |
|
8587 SKIP_BLANKS; |
|
8588 xmlXPathCompMultiplicativeExpr(ctxt); |
|
8589 CHECK_ERROR; |
|
8590 PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0); |
|
8591 SKIP_BLANKS; |
|
8592 } |
|
8593 } |
|
8594 |
|
8595 /** |
|
8596 * xmlXPathCompRelationalExpr: |
|
8597 * @ctxt: the XPath Parser context |
|
8598 * |
|
8599 * [24] RelationalExpr ::= AdditiveExpr |
|
8600 * | RelationalExpr '<' AdditiveExpr |
|
8601 * | RelationalExpr '>' AdditiveExpr |
|
8602 * | RelationalExpr '<=' AdditiveExpr |
|
8603 * | RelationalExpr '>=' AdditiveExpr |
|
8604 * |
|
8605 * A <= B > C is allowed ? Answer from James, yes with |
|
8606 * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr |
|
8607 * which is basically what got implemented. |
|
8608 * |
|
8609 * Compile a Relational expression, then push the result |
|
8610 * on the stack |
|
8611 * |
|
8612 * OOM: |
|
8613 */ |
|
8614 |
|
8615 static void |
|
8616 xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) { |
|
8617 xmlChar ch; |
|
8618 int* last; |
|
8619 // XML ENGINE: Changes/optimization were applied here |
|
8620 xmlXPathCompAdditiveExpr(ctxt); |
|
8621 if(OOM_FLAG) return; |
|
8622 CHECK_ERROR; |
|
8623 SKIP_BLANKS; |
|
8624 last = &(ctxt->comp->last); |
|
8625 // NOTE: this loop allows expressions like A > B <= C < D .. etc. |
|
8626 // which will be evaluated in a way that may be unexpected: |
|
8627 // e.g. the example above is evaluated as |
|
8628 // ((A > B) <= C) < D with boolean results within parentheses |
|
8629 // converted to 1 or 0 (from 'true' and 'false') |
|
8630 // TODO: Check how well this confoms to the spec |
|
8631 for(;;) |
|
8632 { |
|
8633 ch = *ctxt->cur; |
|
8634 if (ch == '<' || ch == '>') |
|
8635 { |
|
8636 int op1 = *last; |
|
8637 int strict = (*(++(ctxt->cur)) == '=') ? 0 : 1; // evaluate 'strict' and NEXT |
|
8638 if (!strict) |
|
8639 ctxt->cur++; // NEXT again if '=' |
|
8640 |
|
8641 SKIP_BLANKS; |
|
8642 xmlXPathCompAdditiveExpr(ctxt); |
|
8643 CHECK_ERROR; |
|
8644 PUSH_BINARY_EXPR( |
|
8645 XPATH_OP_CMP, |
|
8646 op1, |
|
8647 *last, |
|
8648 (ch == '<') & 1, |
|
8649 strict); |
|
8650 SKIP_BLANKS; |
|
8651 } |
|
8652 else |
|
8653 return; |
|
8654 } // for(;;) |
|
8655 } |
|
8656 |
|
8657 /** |
|
8658 * xmlXPathCompEqualityExpr: |
|
8659 * @ctxt: the XPath Parser context |
|
8660 * |
|
8661 * [23] EqualityExpr ::= RelationalExpr |
|
8662 * | EqualityExpr '=' RelationalExpr |
|
8663 * | EqualityExpr '!=' RelationalExpr |
|
8664 * |
|
8665 * A != B != C is allowed ? Answer from James, yes with |
|
8666 * (RelationalExpr = RelationalExpr) = RelationalExpr |
|
8667 * (RelationalExpr != RelationalExpr) != RelationalExpr |
|
8668 * which is basically what got implemented. |
|
8669 * |
|
8670 * Compile an Equality expression. |
|
8671 * |
|
8672 * OOM: |
|
8673 */ |
|
8674 static void |
|
8675 xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) { |
|
8676 xmlXPathCompRelationalExpr(ctxt); |
|
8677 if(OOM_FLAG) return; |
|
8678 CHECK_ERROR; |
|
8679 SKIP_BLANKS; |
|
8680 while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) { |
|
8681 int eq; |
|
8682 int op1 = ctxt->comp->last; |
|
8683 |
|
8684 if (CUR == '=') eq = 1; |
|
8685 else eq = 0; |
|
8686 NEXT; |
|
8687 if (!eq) NEXT; |
|
8688 SKIP_BLANKS; |
|
8689 xmlXPathCompRelationalExpr(ctxt); |
|
8690 CHECK_ERROR; |
|
8691 PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0); |
|
8692 SKIP_BLANKS; |
|
8693 } |
|
8694 } |
|
8695 |
|
8696 /** |
|
8697 * xmlXPathCompAndExpr: |
|
8698 * @ctxt: the XPath Parser context |
|
8699 * |
|
8700 * [22] AndExpr ::= EqualityExpr |
|
8701 * | AndExpr 'and' EqualityExpr |
|
8702 * |
|
8703 * Compile an AND expression. |
|
8704 * |
|
8705 * OOM: |
|
8706 */ |
|
8707 static void |
|
8708 xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) { |
|
8709 xmlXPathCompEqualityExpr(ctxt); |
|
8710 if(OOM_FLAG) return; |
|
8711 CHECK_ERROR; |
|
8712 SKIP_BLANKS; |
|
8713 while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) { |
|
8714 int op1 = ctxt->comp->last; |
|
8715 SKIP(3); |
|
8716 SKIP_BLANKS; |
|
8717 xmlXPathCompEqualityExpr(ctxt); |
|
8718 CHECK_ERROR; |
|
8719 PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0); |
|
8720 SKIP_BLANKS; |
|
8721 } |
|
8722 } |
|
8723 |
|
8724 #define XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS |
|
8725 /** |
|
8726 * xmlXPathCompileExpr: |
|
8727 * @ctxt: the XPath Parser context |
|
8728 * |
|
8729 * [14] Expr ::= OrExpr |
|
8730 * [21] OrExpr ::= AndExpr |
|
8731 * | OrExpr 'or' AndExpr |
|
8732 * |
|
8733 * Parse and compile an expression |
|
8734 * |
|
8735 * OOM: possible --> check OOM flag //// |
|
8736 */ |
|
8737 static void |
|
8738 xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) |
|
8739 { |
|
8740 xmlXPathCompAndExpr(ctxt); |
|
8741 CHECK_ERROR; |
|
8742 if(OOM_FLAG){ |
|
8743 XP_ERROR(XPATH_MEMORY_ERROR); |
|
8744 } |
|
8745 SKIP_BLANKS; |
|
8746 while ((CUR == 'o') && (NXT(1) == 'r')) { |
|
8747 int op1 = ctxt->comp->last; |
|
8748 SKIP(2); |
|
8749 SKIP_BLANKS; |
|
8750 xmlXPathCompAndExpr(ctxt); |
|
8751 CHECK_ERROR; |
|
8752 PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0); |
|
8753 op1 = ctxt->comp->nbStep; |
|
8754 SKIP_BLANKS; |
|
8755 } |
|
8756 if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) { |
|
8757 /* more ops could be optimized too */ |
|
8758 PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0); |
|
8759 } |
|
8760 |
|
8761 #ifdef XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS |
|
8762 // Free unused memory in the tail of ctxt->comp->steps array |
|
8763 { |
|
8764 xmlXPathStepOp* tmp; |
|
8765 // DONE: OPTIMIZE: free the rest of preallocated step table |
|
8766 // This is a exeprimental code in XML Engine |
|
8767 // DONE: Fix xmlRealloc (Note: OOM should not happen since the size is smaller, but...) |
|
8768 tmp = (xmlXPathStepOp*)xmlRealloc(ctxt->comp->steps, sizeof(xmlXPathStepOp) * ctxt->comp->nbStep); |
|
8769 if(tmp) |
|
8770 { |
|
8771 ctxt->comp->steps = tmp; |
|
8772 ctxt->comp->maxStep = ctxt->comp->nbStep; |
|
8773 } |
|
8774 } |
|
8775 #endif |
|
8776 // |
|
8777 } |
|
8778 |
|
8779 /** |
|
8780 * xmlXPathCompPredicate: |
|
8781 * @ctxt: the XPath Parser context |
|
8782 * @filter: act as a filter |
|
8783 * |
|
8784 * [8] Predicate ::= '[' PredicateExpr ']' |
|
8785 * [9] PredicateExpr ::= Expr |
|
8786 * |
|
8787 * Compile a predicate expression |
|
8788 */ |
|
8789 static void |
|
8790 xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) { |
|
8791 int op1 = ctxt->comp->last; |
|
8792 |
|
8793 SKIP_BLANKS; |
|
8794 if (CUR != '[') { |
|
8795 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); |
|
8796 } |
|
8797 NEXT; |
|
8798 SKIP_BLANKS; |
|
8799 |
|
8800 ctxt->comp->last = -1; |
|
8801 xmlXPathCompileExpr(ctxt); |
|
8802 CHECK_ERROR; |
|
8803 |
|
8804 if (CUR != ']') { |
|
8805 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); |
|
8806 } |
|
8807 |
|
8808 if (filter) |
|
8809 PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0); |
|
8810 else |
|
8811 PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0); |
|
8812 |
|
8813 NEXT; |
|
8814 SKIP_BLANKS; |
|
8815 } |
|
8816 |
|
8817 /** |
|
8818 * xmlXPathCompNodeTest: |
|
8819 * @ctxt: the XPath Parser context |
|
8820 * @test: pointer to a xmlXPathTestVal |
|
8821 * @type: pointer to a xmlXPathTypeVal |
|
8822 * @prefix: placeholder for a possible name prefix |
|
8823 * |
|
8824 * [7] NodeTest ::= NameTest |
|
8825 * | NodeType '(' ')' |
|
8826 * | 'processing-instruction' '(' Literal ')' |
|
8827 * |
|
8828 * [37] NameTest ::= '*' |
|
8829 * | NCName ':' '*' |
|
8830 * | QName |
|
8831 * [38] NodeType ::= 'comment' |
|
8832 * | 'text' |
|
8833 * | 'processing-instruction' |
|
8834 * | 'node' |
|
8835 * |
|
8836 * Returns the name found and updates @test, @type and @prefix appropriately |
|
8837 */ |
|
8838 static xmlChar * |
|
8839 xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test, |
|
8840 xmlXPathTypeVal *type, const xmlChar **prefix, |
|
8841 xmlChar *name) { |
|
8842 int blanks; |
|
8843 |
|
8844 if ((test == NULL) || (type == NULL) || (prefix == NULL)) { |
|
8845 STRANGE; |
|
8846 return(NULL); |
|
8847 } |
|
8848 *type = (xmlXPathTypeVal) 0; |
|
8849 *test = (xmlXPathTestVal) 0; |
|
8850 *prefix = NULL; |
|
8851 SKIP_BLANKS; |
|
8852 |
|
8853 if ((name == NULL) && (CUR == '*')) { |
|
8854 /* |
|
8855 * All elements |
|
8856 */ |
|
8857 NEXT; |
|
8858 *test = NODE_TEST_ALL; |
|
8859 return(NULL); |
|
8860 } |
|
8861 |
|
8862 if (name == NULL) |
|
8863 name = xmlXPathParseNCName(ctxt); |
|
8864 if (name == NULL) { |
|
8865 XP_ERROR0(XPATH_EXPR_ERROR); |
|
8866 } |
|
8867 |
|
8868 blanks = IS_BLANK_CH(CUR); |
|
8869 SKIP_BLANKS; |
|
8870 if (CUR == '(') { |
|
8871 NEXT; |
|
8872 /* |
|
8873 * NodeType or PI search |
|
8874 */ |
|
8875 if (xmlStrEqual(name, BAD_CAST "comment")) |
|
8876 *type = NODE_TYPE_COMMENT; |
|
8877 else if (xmlStrEqual(name, BAD_CAST "node")) |
|
8878 *type = NODE_TYPE_NODE; |
|
8879 else if (xmlStrEqual(name, BAD_CAST "processing-instruction")) |
|
8880 *type = NODE_TYPE_PI; |
|
8881 else if (xmlStrEqual(name, BAD_CAST "text")) |
|
8882 *type = NODE_TYPE_TEXT; |
|
8883 else { |
|
8884 if (name != NULL) |
|
8885 xmlFree(name); |
|
8886 XP_ERROR0(XPATH_EXPR_ERROR); |
|
8887 } |
|
8888 |
|
8889 *test = NODE_TEST_TYPE; |
|
8890 |
|
8891 SKIP_BLANKS; |
|
8892 if (*type == NODE_TYPE_PI) { |
|
8893 /* |
|
8894 * Specific case: search a PI by name. |
|
8895 */ |
|
8896 if (name != NULL) |
|
8897 xmlFree(name); |
|
8898 name = NULL; |
|
8899 if (CUR != ')') { |
|
8900 name = xmlXPathParseLiteral(ctxt); |
|
8901 CHECK_ERROR 0; |
|
8902 *test = NODE_TEST_PI; |
|
8903 SKIP_BLANKS; |
|
8904 } |
|
8905 } |
|
8906 if (CUR != ')') { |
|
8907 if (name != NULL) |
|
8908 xmlFree(name); |
|
8909 XP_ERROR0(XPATH_UNCLOSED_ERROR); |
|
8910 } |
|
8911 NEXT; |
|
8912 return(name); |
|
8913 } |
|
8914 *test = NODE_TEST_NAME; |
|
8915 if ((!blanks) && (CUR == ':')) { |
|
8916 NEXT; |
|
8917 |
|
8918 /* |
|
8919 * Since currently the parser context don't have a |
|
8920 * namespace list associated: |
|
8921 * The namespace name for this prefix can be computed |
|
8922 * only at evaluation time. The compilation is done |
|
8923 * outside of any context. |
|
8924 */ |
|
8925 #if 0 |
|
8926 *prefix = xmlXPathNsLookup(ctxt->context, name); |
|
8927 if (name != NULL) |
|
8928 xmlFree(name); |
|
8929 if (*prefix == NULL) { |
|
8930 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); |
|
8931 } |
|
8932 #else |
|
8933 *prefix = name; |
|
8934 #endif |
|
8935 |
|
8936 if (CUR == '*') { |
|
8937 /* |
|
8938 * All elements |
|
8939 */ |
|
8940 NEXT; |
|
8941 *test = NODE_TEST_ALL; |
|
8942 return(NULL); |
|
8943 } |
|
8944 |
|
8945 name = xmlXPathParseNCName(ctxt); |
|
8946 if (name == NULL) { |
|
8947 XP_ERROR0(XPATH_EXPR_ERROR); |
|
8948 } |
|
8949 } |
|
8950 return(name); |
|
8951 } |
|
8952 |
|
8953 /** |
|
8954 * xmlXPathIsAxisName: |
|
8955 * @name: a preparsed name token |
|
8956 * |
|
8957 * [6] AxisName ::= 'ancestor' |
|
8958 * | 'ancestor-or-self' |
|
8959 * | 'attribute' |
|
8960 * | 'child' |
|
8961 * | 'descendant' |
|
8962 * | 'descendant-or-self' |
|
8963 * | 'following' |
|
8964 * | 'following-sibling' |
|
8965 * | 'namespace' |
|
8966 * | 'parent' |
|
8967 * | 'preceding' |
|
8968 * | 'preceding-sibling' |
|
8969 * | 'self' |
|
8970 * |
|
8971 * Returns the axis or 0 |
|
8972 */ |
|
8973 static xmlXPathAxisVal |
|
8974 xmlXPathIsAxisName(const xmlChar *name) { |
|
8975 xmlXPathAxisVal ret = (xmlXPathAxisVal) 0; |
|
8976 switch (name[0]) { |
|
8977 // TODO: OPTIMIZE: Reorder case's optimally |
|
8978 case 'a': |
|
8979 if (xmlStrEqual(name, BAD_CAST "ancestor")) |
|
8980 ret = AXIS_ANCESTOR; |
|
8981 if (xmlStrEqual(name, BAD_CAST "ancestor-or-self")) |
|
8982 ret = AXIS_ANCESTOR_OR_SELF; |
|
8983 if (xmlStrEqual(name, BAD_CAST "attribute")) |
|
8984 ret = AXIS_ATTRIBUTE; |
|
8985 break; |
|
8986 case 'c': |
|
8987 if (xmlStrEqual(name, BAD_CAST "child")) |
|
8988 ret = AXIS_CHILD; |
|
8989 break; |
|
8990 case 'd': |
|
8991 if (xmlStrEqual(name, BAD_CAST "descendant")) |
|
8992 ret = AXIS_DESCENDANT; |
|
8993 if (xmlStrEqual(name, BAD_CAST "descendant-or-self")) |
|
8994 ret = AXIS_DESCENDANT_OR_SELF; |
|
8995 break; |
|
8996 case 'f': |
|
8997 if (xmlStrEqual(name, BAD_CAST "following")) |
|
8998 ret = AXIS_FOLLOWING; |
|
8999 if (xmlStrEqual(name, BAD_CAST "following-sibling")) |
|
9000 ret = AXIS_FOLLOWING_SIBLING; |
|
9001 break; |
|
9002 case 'n': |
|
9003 if (xmlStrEqual(name, BAD_CAST "namespace")) |
|
9004 ret = AXIS_NAMESPACE; |
|
9005 break; |
|
9006 case 'p': |
|
9007 if (xmlStrEqual(name, BAD_CAST "parent")) |
|
9008 ret = AXIS_PARENT; |
|
9009 if (xmlStrEqual(name, BAD_CAST "preceding")) |
|
9010 ret = AXIS_PRECEDING; |
|
9011 if (xmlStrEqual(name, BAD_CAST "preceding-sibling")) |
|
9012 ret = AXIS_PRECEDING_SIBLING; |
|
9013 break; |
|
9014 case 's': |
|
9015 if (xmlStrEqual(name, BAD_CAST "self")) |
|
9016 ret = AXIS_SELF; |
|
9017 break; |
|
9018 } |
|
9019 return(ret); |
|
9020 } |
|
9021 |
|
9022 /** |
|
9023 * xmlXPathCompStep: |
|
9024 * @ctxt: the XPath Parser context |
|
9025 * |
|
9026 * [4] Step ::= AxisSpecifier NodeTest Predicate* |
|
9027 * | AbbreviatedStep |
|
9028 * |
|
9029 * [12] AbbreviatedStep ::= '.' | '..' |
|
9030 * |
|
9031 * [5] AxisSpecifier ::= AxisName '::' |
|
9032 * | AbbreviatedAxisSpecifier |
|
9033 * |
|
9034 * [13] AbbreviatedAxisSpecifier ::= '@'? |
|
9035 * |
|
9036 * Modified for XPtr range support as: |
|
9037 * |
|
9038 * [4xptr] Step ::= AxisSpecifier NodeTest Predicate* |
|
9039 * | AbbreviatedStep |
|
9040 * | 'range-to' '(' Expr ')' Predicate* |
|
9041 * |
|
9042 * Compile one step in a Location Path |
|
9043 * A location step of . is short for self::node(). This is |
|
9044 * particularly useful in conjunction with //. For example, the |
|
9045 * location path .//para is short for |
|
9046 * self::node()/descendant-or-self::node()/child::para |
|
9047 * and so will select all para descendant elements of the context |
|
9048 * node. |
|
9049 * Similarly, a location step of .. is short for parent::node(). |
|
9050 * For example, ../title is short for parent::node()/child::title |
|
9051 * and so will select the title children of the parent of the context |
|
9052 * node. |
|
9053 */ |
|
9054 static void |
|
9055 xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { |
|
9056 #ifdef LIBXML_XPTR_ENABLED |
|
9057 int rangeto = 0; |
|
9058 int op2 = -1; |
|
9059 #endif |
|
9060 |
|
9061 SKIP_BLANKS; |
|
9062 if ((CUR == '.') && (NXT(1) == '.')) { |
|
9063 SKIP(2); |
|
9064 SKIP_BLANKS; |
|
9065 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT, |
|
9066 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9067 } else if (CUR == '.') { |
|
9068 NEXT; |
|
9069 SKIP_BLANKS; |
|
9070 } else { |
|
9071 xmlChar *name = NULL; |
|
9072 const xmlChar *prefix = NULL; |
|
9073 xmlXPathTestVal test; |
|
9074 xmlXPathAxisVal axis = (xmlXPathAxisVal) 0; |
|
9075 xmlXPathTypeVal type; |
|
9076 int op1; |
|
9077 |
|
9078 /* |
|
9079 * The modification needed for XPointer change to the production |
|
9080 */ |
|
9081 #ifdef LIBXML_XPTR_ENABLED |
|
9082 if (ctxt->xptr) { |
|
9083 name = xmlXPathParseNCName(ctxt); |
|
9084 if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) { |
|
9085 op2 = ctxt->comp->last; |
|
9086 xmlFree(name); |
|
9087 SKIP_BLANKS; |
|
9088 if (CUR != '(') { |
|
9089 XP_ERROR(XPATH_EXPR_ERROR); |
|
9090 } |
|
9091 NEXT; |
|
9092 SKIP_BLANKS; |
|
9093 |
|
9094 xmlXPathCompileExpr(ctxt); |
|
9095 /* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */ |
|
9096 CHECK_ERROR; |
|
9097 |
|
9098 SKIP_BLANKS; |
|
9099 if (CUR != ')') { |
|
9100 XP_ERROR(XPATH_EXPR_ERROR); |
|
9101 } |
|
9102 NEXT; |
|
9103 rangeto = 1; |
|
9104 goto eval_predicates; |
|
9105 } |
|
9106 } |
|
9107 #endif |
|
9108 if (CUR == '*') { |
|
9109 axis = AXIS_CHILD; |
|
9110 } else { |
|
9111 if (name == NULL) |
|
9112 name = xmlXPathParseNCName(ctxt); |
|
9113 if(OOM_FLAG) return; |
|
9114 if (name != NULL) { |
|
9115 axis = xmlXPathIsAxisName(name); |
|
9116 if (axis != 0) { |
|
9117 SKIP_BLANKS; |
|
9118 if ((CUR == ':') && (NXT(1) == ':')) { |
|
9119 SKIP(2); |
|
9120 xmlFree(name); |
|
9121 name = NULL; |
|
9122 } else { |
|
9123 /* an element name can conflict with an axis one :-\ */ |
|
9124 axis = AXIS_CHILD; |
|
9125 } |
|
9126 } else { |
|
9127 axis = AXIS_CHILD; |
|
9128 } |
|
9129 } else if (CUR == '@') { |
|
9130 NEXT; |
|
9131 axis = AXIS_ATTRIBUTE; |
|
9132 } else { |
|
9133 axis = AXIS_CHILD; |
|
9134 } |
|
9135 } |
|
9136 |
|
9137 CHECK_ERROR; |
|
9138 |
|
9139 name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name); |
|
9140 if (test == 0) |
|
9141 return; |
|
9142 |
|
9143 #ifdef DEBUG_STEP |
|
9144 xmlGenericError(xmlGenericErrorContext, |
|
9145 "Basis : computing new set\n"); |
|
9146 #endif |
|
9147 |
|
9148 #ifdef DEBUG_STEP |
|
9149 xmlGenericError(xmlGenericErrorContext, "Basis : "); |
|
9150 if (ctxt->value == NULL) |
|
9151 xmlGenericError(xmlGenericErrorContext, "no value\n"); |
|
9152 else if (ctxt->value->nodesetval == NULL) |
|
9153 xmlGenericError(xmlGenericErrorContext, "Empty\n"); |
|
9154 else |
|
9155 xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval); |
|
9156 #endif |
|
9157 |
|
9158 #ifdef LIBXML_XPTR_ENABLED |
|
9159 eval_predicates: |
|
9160 #endif |
|
9161 op1 = ctxt->comp->last; |
|
9162 ctxt->comp->last = -1; |
|
9163 |
|
9164 SKIP_BLANKS; |
|
9165 while (CUR == '[') { |
|
9166 xmlXPathCompPredicate(ctxt, 0); |
|
9167 } |
|
9168 |
|
9169 #ifdef LIBXML_XPTR_ENABLED |
|
9170 if (rangeto) { |
|
9171 PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0); |
|
9172 } else |
|
9173 #endif |
|
9174 PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis, |
|
9175 test, type, (void *)prefix, (void *)name); |
|
9176 |
|
9177 } |
|
9178 #ifdef DEBUG_STEP |
|
9179 xmlGenericError(xmlGenericErrorContext, "Step : "); |
|
9180 if (ctxt->value == NULL) |
|
9181 xmlGenericError(xmlGenericErrorContext, "no value\n"); |
|
9182 else if (ctxt->value->nodesetval == NULL) |
|
9183 xmlGenericError(xmlGenericErrorContext, "Empty\n"); |
|
9184 else |
|
9185 xmlGenericErrorContextNodeSet(xmlGenericErrorContext, |
|
9186 ctxt->value->nodesetval); |
|
9187 #endif |
|
9188 } |
|
9189 |
|
9190 /** |
|
9191 * xmlXPathCompRelativeLocationPath: |
|
9192 * @ctxt: the XPath Parser context |
|
9193 * |
|
9194 * [3] RelativeLocationPath ::= Step |
|
9195 * | RelativeLocationPath '/' Step |
|
9196 * | AbbreviatedRelativeLocationPath |
|
9197 * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step |
|
9198 * |
|
9199 * Compile a relative location path. |
|
9200 */ |
|
9201 static void |
|
9202 xmlXPathCompRelativeLocationPath |
|
9203 (xmlXPathParserContextPtr ctxt) { |
|
9204 SKIP_BLANKS; |
|
9205 if ((CUR == '/') && (NXT(1) == '/')) { |
|
9206 SKIP(2); |
|
9207 SKIP_BLANKS; |
|
9208 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, |
|
9209 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9210 } else if (CUR == '/') { |
|
9211 NEXT; |
|
9212 SKIP_BLANKS; |
|
9213 } |
|
9214 xmlXPathCompStep(ctxt); |
|
9215 if(OOM_FLAG) return; |
|
9216 SKIP_BLANKS; |
|
9217 while (CUR == '/') { |
|
9218 if ((CUR == '/') && (NXT(1) == '/')) { |
|
9219 SKIP(2); |
|
9220 SKIP_BLANKS; |
|
9221 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, |
|
9222 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9223 xmlXPathCompStep(ctxt); |
|
9224 if(OOM_FLAG) return; |
|
9225 } else if (CUR == '/') { |
|
9226 NEXT; |
|
9227 SKIP_BLANKS; |
|
9228 xmlXPathCompStep(ctxt); |
|
9229 } |
|
9230 SKIP_BLANKS; |
|
9231 } |
|
9232 } |
|
9233 |
|
9234 /** |
|
9235 * xmlXPathCompLocationPath: |
|
9236 * @ctxt: the XPath Parser context |
|
9237 * |
|
9238 * [1] LocationPath ::= RelativeLocationPath |
|
9239 * | AbsoluteLocationPath |
|
9240 * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath? |
|
9241 * | AbbreviatedAbsoluteLocationPath |
|
9242 * [10] AbbreviatedAbsoluteLocationPath ::= |
|
9243 * '//' RelativeLocationPath |
|
9244 * |
|
9245 * Compile a location path |
|
9246 * |
|
9247 * // is short for /descendant-or-self::node()/. For example, |
|
9248 * //para is short for /descendant-or-self::node()/child::para and |
|
9249 * so will select any para element in the document (even a para element |
|
9250 * that is a document element will be selected by //para since the |
|
9251 * document element node is a child of the root node); div//para is |
|
9252 * short for div/descendant-or-self::node()/child::para and so will |
|
9253 * select all para descendants of div children. |
|
9254 */ |
|
9255 static void |
|
9256 xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) { |
|
9257 SKIP_BLANKS; |
|
9258 if (CUR != '/') { |
|
9259 xmlXPathCompRelativeLocationPath(ctxt); |
|
9260 } else { |
|
9261 while (CUR == '/') { |
|
9262 if ((CUR == '/') && (NXT(1) == '/')) { |
|
9263 SKIP(2); |
|
9264 SKIP_BLANKS; |
|
9265 PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF, |
|
9266 NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL); |
|
9267 xmlXPathCompRelativeLocationPath(ctxt); |
|
9268 if(OOM_FLAG) return; |
|
9269 } else if (CUR == '/') { |
|
9270 NEXT; |
|
9271 SKIP_BLANKS; |
|
9272 if ((CUR != 0 ) && |
|
9273 ((IS_LETTER_CH(CUR)) || (CUR == '_') || (CUR == '.') || |
|
9274 (CUR == '@') || (CUR == '*'))) |
|
9275 { |
|
9276 xmlXPathCompRelativeLocationPath(ctxt); |
|
9277 if(OOM_FLAG) return; |
|
9278 } |
|
9279 } |
|
9280 } |
|
9281 } |
|
9282 } |
|
9283 |
|
9284 /************************************************************************ |
|
9285 * * |
|
9286 * XPath precompiled expression evaluation * |
|
9287 * * |
|
9288 ************************************************************************/ |
|
9289 |
|
9290 static int |
|
9291 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op); |
|
9292 |
|
9293 /** |
|
9294 * xmlXPathNodeCollectAndTest: |
|
9295 * @ctxt: the XPath Parser context |
|
9296 * @op: the XPath precompiled step operation |
|
9297 * @first: pointer to the first element in document order |
|
9298 * @last: pointer to the last element in document order |
|
9299 * |
|
9300 * This is the function implementing a step: based on the current list |
|
9301 * of nodes, it builds up a new list, looking at all nodes under that |
|
9302 * axis and selecting them. It also does the predicate filtering |
|
9303 * |
|
9304 * Pushes the new NodeSet resulting from the search. |
|
9305 * |
|
9306 * Returns the number of nodes traversed or -1 when OOM |
|
9307 */ |
|
9308 static int |
|
9309 xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
|
9310 xmlXPathStepOpPtr op, |
|
9311 xmlNodePtr * first, xmlNodePtr * last) |
|
9312 { |
|
9313 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; |
|
9314 xmlXPathTestVal test = (xmlXPathTestVal) op->value2; |
|
9315 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3; |
|
9316 |
|
9317 const xmlChar* prefix = (xmlChar*) op->value4; |
|
9318 const xmlChar* name = (xmlChar*) op->value5; |
|
9319 const xmlChar* URI = NULL; |
|
9320 |
|
9321 #ifdef DEBUG_STEP |
|
9322 int n = 0; |
|
9323 #endif |
|
9324 |
|
9325 int i, t = 0; |
|
9326 xmlNodeSetPtr ret = NULL; |
|
9327 xmlNodeSetPtr list = NULL; |
|
9328 xmlXPathTraversalFunction next = NULL; |
|
9329 void (*addNode) (xmlNodeSetPtr, xmlNodePtr); |
|
9330 xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr); |
|
9331 xmlNodePtr cur = NULL; |
|
9332 xmlXPathObjectPtr obj; |
|
9333 xmlNodeSetPtr nodelist; |
|
9334 xmlNodePtr tmp; |
|
9335 |
|
9336 CHECK_TYPE0(XPATH_NODESET); |
|
9337 obj = valuePop(ctxt); |
|
9338 addNode = xmlXPathNodeSetAdd; |
|
9339 mergeNodeSet = xmlXPathNodeSetMerge; |
|
9340 if (prefix != NULL) { |
|
9341 URI = xmlXPathNsLookup(ctxt->context, prefix); |
|
9342 if (URI == NULL) |
|
9343 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); |
|
9344 } |
|
9345 |
|
9346 #ifdef DEBUG_STEP |
|
9347 xmlGenericError(xmlGenericErrorContext, "new step : "); |
|
9348 #endif |
|
9349 |
|
9350 switch (axis) |
|
9351 { |
|
9352 // TODO: OPTIMIZATION: Reorder options according to expectation of invocation |
|
9353 case AXIS_ANCESTOR: { |
|
9354 #ifdef DEBUG_STEP |
|
9355 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' "); |
|
9356 #endif |
|
9357 first = NULL; |
|
9358 next = xmlXPathNextAncestor; |
|
9359 break; |
|
9360 } |
|
9361 |
|
9362 case AXIS_ANCESTOR_OR_SELF: { |
|
9363 #ifdef DEBUG_STEP |
|
9364 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors-or-self' "); |
|
9365 #endif |
|
9366 first = NULL; |
|
9367 next = xmlXPathNextAncestorOrSelf; |
|
9368 break; |
|
9369 } |
|
9370 |
|
9371 case AXIS_ATTRIBUTE:{ |
|
9372 #ifdef DEBUG_STEP |
|
9373 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' "); |
|
9374 #endif |
|
9375 first = NULL; |
|
9376 last = NULL; |
|
9377 next = xmlXPathNextAttribute; |
|
9378 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9379 break; |
|
9380 } |
|
9381 |
|
9382 case AXIS_CHILD:{ |
|
9383 #ifdef DEBUG_STEP |
|
9384 xmlGenericError(xmlGenericErrorContext, "axis 'child' "); |
|
9385 #endif |
|
9386 last = NULL; |
|
9387 next = xmlXPathNextChild; |
|
9388 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9389 break; |
|
9390 } |
|
9391 |
|
9392 case AXIS_DESCENDANT:{ |
|
9393 #ifdef DEBUG_STEP |
|
9394 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' "); |
|
9395 #endif |
|
9396 last = NULL; |
|
9397 next = xmlXPathNextDescendant; |
|
9398 break; |
|
9399 } |
|
9400 |
|
9401 case AXIS_DESCENDANT_OR_SELF:{ |
|
9402 #ifdef DEBUG_STEP |
|
9403 xmlGenericError(xmlGenericErrorContext, "axis 'descendant-or-self' "); |
|
9404 #endif |
|
9405 last = NULL; |
|
9406 next = xmlXPathNextDescendantOrSelf; |
|
9407 break; |
|
9408 } |
|
9409 |
|
9410 case AXIS_FOLLOWING:{ |
|
9411 #ifdef DEBUG_STEP |
|
9412 xmlGenericError(xmlGenericErrorContext, "axis 'following' "); |
|
9413 #endif |
|
9414 last = NULL; |
|
9415 next = xmlXPathNextFollowing; |
|
9416 break; |
|
9417 } |
|
9418 |
|
9419 case AXIS_FOLLOWING_SIBLING:{ |
|
9420 #ifdef DEBUG_STEP |
|
9421 xmlGenericError(xmlGenericErrorContext, "axis 'following-siblings' "); |
|
9422 #endif |
|
9423 last = NULL; |
|
9424 next = xmlXPathNextFollowingSibling; |
|
9425 break; |
|
9426 } |
|
9427 |
|
9428 case AXIS_NAMESPACE:{ |
|
9429 #ifdef DEBUG_STEP |
|
9430 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' "); |
|
9431 #endif |
|
9432 first = NULL; |
|
9433 last = NULL; |
|
9434 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; |
|
9435 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9436 break; |
|
9437 } |
|
9438 |
|
9439 case AXIS_PARENT:{ |
|
9440 #ifdef DEBUG_STEP |
|
9441 xmlGenericError(xmlGenericErrorContext, "axis 'parent' "); |
|
9442 #endif |
|
9443 first = NULL; |
|
9444 next = xmlXPathNextParent; |
|
9445 break; |
|
9446 } |
|
9447 |
|
9448 case AXIS_PRECEDING:{ |
|
9449 #ifdef DEBUG_STEP |
|
9450 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' "); |
|
9451 #endif |
|
9452 first = NULL; |
|
9453 next = xmlXPathNextPrecedingInternal; |
|
9454 break; |
|
9455 } |
|
9456 |
|
9457 case AXIS_PRECEDING_SIBLING:{ |
|
9458 #ifdef DEBUG_STEP |
|
9459 xmlGenericError(xmlGenericErrorContext, "axis 'preceding-sibling' "); |
|
9460 #endif |
|
9461 first = NULL; |
|
9462 next = xmlXPathNextPrecedingSibling; |
|
9463 break; |
|
9464 } |
|
9465 |
|
9466 case AXIS_SELF:{ |
|
9467 #ifdef DEBUG_STEP |
|
9468 xmlGenericError(xmlGenericErrorContext, "axis 'self' "); |
|
9469 #endif |
|
9470 first = NULL; |
|
9471 last = NULL; |
|
9472 next = xmlXPathNextSelf; |
|
9473 mergeNodeSet = xmlXPathNodeSetMergeUnique; |
|
9474 break; |
|
9475 } |
|
9476 |
|
9477 } // switch(axis) |
|
9478 |
|
9479 if (next == NULL) |
|
9480 return(0); |
|
9481 |
|
9482 nodelist = obj->nodesetval; |
|
9483 if (nodelist == NULL) { |
|
9484 xmlXPathFreeObject(obj); |
|
9485 valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); |
|
9486 return(0); |
|
9487 } |
|
9488 addNode = xmlXPathNodeSetAddUnique; |
|
9489 ret = NULL; |
|
9490 |
|
9491 #ifdef DEBUG_STEP |
|
9492 { |
|
9493 xmlGenericError(xmlGenericErrorContext, " context contains %d nodes\n", nodelist->nodeNr); |
|
9494 switch (test) { |
|
9495 case NODE_TEST_NONE: |
|
9496 xmlGenericError(xmlGenericErrorContext, " searching for none !!!\n"); |
|
9497 break; |
|
9498 case NODE_TEST_TYPE: |
|
9499 xmlGenericError(xmlGenericErrorContext, " searching for type %d\n", type); |
|
9500 break; |
|
9501 case NODE_TEST_PI: |
|
9502 xmlGenericError(xmlGenericErrorContext, " searching for PI !!!\n"); |
|
9503 break; |
|
9504 case NODE_TEST_ALL: |
|
9505 xmlGenericError(xmlGenericErrorContext, " searching for *\n"); |
|
9506 break; |
|
9507 case NODE_TEST_NS: |
|
9508 xmlGenericError(xmlGenericErrorContext, " searching for namespace %s\n", prefix); |
|
9509 break; |
|
9510 case NODE_TEST_NAME: |
|
9511 xmlGenericError(xmlGenericErrorContext, " searching for name %s\n", name); |
|
9512 if (prefix != NULL) |
|
9513 xmlGenericError(xmlGenericErrorContext, " with namespace %s\n", prefix); |
|
9514 break; |
|
9515 } |
|
9516 xmlGenericError(xmlGenericErrorContext, "Testing : "); |
|
9517 } |
|
9518 #endif |
|
9519 /* |
|
9520 * 2.3 Node Tests |
|
9521 * - For the attribute axis, the principal node type is attribute. |
|
9522 * - For the namespace axis, the principal node type is namespace. |
|
9523 * - For other axes, the principal node type is element. |
|
9524 * |
|
9525 * A node test * is true for any node of the |
|
9526 * principal node type. For example, child::* will |
|
9527 * select all element children of the context node |
|
9528 */ |
|
9529 tmp = ctxt->context->node; |
|
9530 |
|
9531 for (i = 0; i < nodelist->nodeNr; i++) |
|
9532 { |
|
9533 ctxt->context->node = nodelist->nodeTab[i]; |
|
9534 cur = NULL; |
|
9535 list = xmlXPathNodeSetCreate(NULL); |
|
9536 if(OOM_FLAG) |
|
9537 { |
|
9538 xmlXPathFreeObject(obj); |
|
9539 xmlXPathFreeNodeSet(ret); |
|
9540 return(-1); |
|
9541 } |
|
9542 // |
|
9543 do { |
|
9544 cur = next(ctxt, cur); |
|
9545 if (cur == NULL) |
|
9546 break; |
|
9547 if ((first != NULL) && (*first == cur)) |
|
9548 break; |
|
9549 if (((t % 256) == 0) && |
|
9550 (first != NULL) && (*first != NULL) && |
|
9551 (xmlXPathCmpNodes(*first, cur) >= 0)) |
|
9552 break; |
|
9553 if ((last != NULL) && (*last == cur)) |
|
9554 break; |
|
9555 if (((t % 256) == 0) && |
|
9556 (last != NULL) && (*last != NULL) && |
|
9557 (xmlXPathCmpNodes(cur, *last) >= 0)) |
|
9558 break; |
|
9559 |
|
9560 t++; |
|
9561 #ifdef DEBUG_STEP |
|
9562 xmlGenericError(xmlGenericErrorContext, " %s", cur->name); |
|
9563 #endif |
|
9564 switch (test) |
|
9565 { |
|
9566 case NODE_TEST_NONE:{ |
|
9567 ctxt->context->node = tmp; |
|
9568 STRANGE return(t); |
|
9569 } |
|
9570 case NODE_TEST_TYPE:{ |
|
9571 // TODO: OPTIMIZATION: Reorder to maximize performance of partial evaluation |
|
9572 if ((cur->type == type) || |
|
9573 ((type == NODE_TYPE_NODE) && |
|
9574 ((cur->type == XML_DOCUMENT_NODE) || |
|
9575 (cur->type == XML_HTML_DOCUMENT_NODE) || |
|
9576 (cur->type == XML_ELEMENT_NODE) || |
|
9577 (cur->type == XML_NAMESPACE_DECL) || |
|
9578 (cur->type == XML_ATTRIBUTE_NODE) || |
|
9579 (cur->type == XML_PI_NODE) || |
|
9580 (cur->type == XML_COMMENT_NODE) || |
|
9581 (cur->type == XML_CDATA_SECTION_NODE) || |
|
9582 (cur->type == XML_TEXT_NODE))) || |
|
9583 ((type == NODE_TYPE_TEXT) && |
|
9584 (cur->type == XML_CDATA_SECTION_NODE))) |
|
9585 { |
|
9586 #ifdef DEBUG_STEP |
|
9587 n++; |
|
9588 #endif |
|
9589 addNode(list, cur); |
|
9590 } |
|
9591 break; |
|
9592 } |
|
9593 |
|
9594 case NODE_TEST_PI: { |
|
9595 if (cur->type == XML_PI_NODE) |
|
9596 { |
|
9597 if ((name != NULL) && (!xmlStrEqual(name, cur->name))) |
|
9598 break; |
|
9599 #ifdef DEBUG_STEP |
|
9600 n++; |
|
9601 #endif |
|
9602 addNode(list, cur); |
|
9603 } |
|
9604 break; |
|
9605 } |
|
9606 |
|
9607 case NODE_TEST_ALL: { |
|
9608 if (axis == AXIS_ATTRIBUTE) { |
|
9609 if (cur->type == XML_ATTRIBUTE_NODE) { |
|
9610 #ifdef DEBUG_STEP |
|
9611 n++; |
|
9612 #endif |
|
9613 addNode(list, cur); |
|
9614 } |
|
9615 } else |
|
9616 if (axis == AXIS_NAMESPACE) { |
|
9617 if (cur->type == XML_NAMESPACE_DECL) { |
|
9618 #ifdef DEBUG_STEP |
|
9619 n++; |
|
9620 #endif |
|
9621 xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur); |
|
9622 } |
|
9623 } else { |
|
9624 if (cur->type == XML_ELEMENT_NODE) { |
|
9625 if (prefix == NULL) { |
|
9626 #ifdef DEBUG_STEP |
|
9627 n++; |
|
9628 #endif |
|
9629 addNode(list, cur); |
|
9630 if(OOM_FLAG) |
|
9631 { |
|
9632 xmlXPathFreeObject(obj); |
|
9633 xmlXPathFreeNodeSet(list); |
|
9634 xmlXPathFreeNodeSet(ret); |
|
9635 return(-1); |
|
9636 } |
|
9637 } else |
|
9638 if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) { |
|
9639 #ifdef DEBUG_STEP |
|
9640 n++; |
|
9641 #endif |
|
9642 addNode(list, cur); |
|
9643 } |
|
9644 } |
|
9645 } |
|
9646 break; |
|
9647 } |
|
9648 |
|
9649 case NODE_TEST_NS: { |
|
9650 TODO; |
|
9651 break; |
|
9652 } |
|
9653 case NODE_TEST_NAME:{ |
|
9654 switch (cur->type) { |
|
9655 case XML_ELEMENT_NODE: { |
|
9656 if (xmlStrEqual(name, cur->name)) { |
|
9657 if (prefix == NULL) { |
|
9658 if (cur->ns == NULL) { // should we add here || cur->ns->pref == NULL ? |
|
9659 #ifdef DEBUG_STEP |
|
9660 n++; |
|
9661 #endif |
|
9662 addNode(list, cur); |
|
9663 if(OOM_FLAG) |
|
9664 { |
|
9665 xmlXPathFreeObject(obj); |
|
9666 xmlXPathFreeNodeSet(list); |
|
9667 return(-1); |
|
9668 } |
|
9669 } |
|
9670 } else { |
|
9671 if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) { |
|
9672 #ifdef DEBUG_STEP |
|
9673 n++; |
|
9674 #endif |
|
9675 addNode(list, cur); |
|
9676 } |
|
9677 } |
|
9678 } |
|
9679 break; |
|
9680 } |
|
9681 |
|
9682 case XML_ATTRIBUTE_NODE:{ |
|
9683 xmlAttrPtr attr = (xmlAttrPtr) cur; |
|
9684 |
|
9685 if (xmlStrEqual(name, attr->name)) { |
|
9686 if (prefix == NULL) { |
|
9687 if ((attr->ns == NULL) || (attr->ns->prefix == NULL)) { |
|
9688 #ifdef DEBUG_STEP |
|
9689 n++; |
|
9690 #endif |
|
9691 addNode(list, (xmlNodePtr) attr); |
|
9692 if(OOM_FLAG) |
|
9693 { |
|
9694 xmlXPathFreeObject(obj); |
|
9695 xmlXPathFreeNodeSet(list); |
|
9696 return(-1); |
|
9697 } |
|
9698 } |
|
9699 } else { |
|
9700 if ((attr->ns != NULL) && (xmlStrEqual(URI, attr->ns->href))) { |
|
9701 #ifdef DEBUG_STEP |
|
9702 n++; |
|
9703 #endif |
|
9704 addNode(list, (xmlNodePtr) attr); |
|
9705 } |
|
9706 } |
|
9707 } |
|
9708 break; |
|
9709 } |
|
9710 case XML_NAMESPACE_DECL:{ |
|
9711 if (cur->type == XML_NAMESPACE_DECL) { |
|
9712 xmlNsPtr ns = (xmlNsPtr) cur; |
|
9713 |
|
9714 if ((ns->prefix != NULL) && (name != NULL) |
|
9715 && (xmlStrEqual(ns->prefix, name))) |
|
9716 { |
|
9717 #ifdef DEBUG_STEP |
|
9718 n++; |
|
9719 #endif |
|
9720 xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur); |
|
9721 } |
|
9722 } |
|
9723 break; |
|
9724 } |
|
9725 |
|
9726 default: |
|
9727 break; |
|
9728 } |
|
9729 |
|
9730 break; |
|
9731 break; |
|
9732 } |
|
9733 } // switch(test) |
|
9734 } while (cur != NULL); |
|
9735 |
|
9736 /* |
|
9737 * If there is some predicate filtering do it now |
|
9738 */ |
|
9739 if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0)) |
|
9740 { |
|
9741 xmlXPathObjectPtr obj2; |
|
9742 |
|
9743 valuePush(ctxt, xmlXPathWrapNodeSet(list)); |
|
9744 if(OOM_FLAG) |
|
9745 { |
|
9746 xmlXPathFreeObject(obj); |
|
9747 xmlXPathFreeNodeSet(list); |
|
9748 xmlXPathFreeNodeSet(ret); |
|
9749 return(-1); |
|
9750 } |
|
9751 xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]); |
|
9752 CHECK_TYPE0(XPATH_NODESET); |
|
9753 obj2 = valuePop(ctxt); |
|
9754 list = obj2->nodesetval; |
|
9755 obj2->nodesetval = NULL; |
|
9756 xmlXPathFreeObject(obj2); |
|
9757 } |
|
9758 if (ret == NULL) { |
|
9759 ret = list; |
|
9760 } else { |
|
9761 ret = mergeNodeSet(ret, list); |
|
9762 xmlXPathFreeNodeSet(list); |
|
9763 } |
|
9764 } // for (i = 0; i < nodelist->nodeNr; i++) |
|
9765 |
|
9766 ctxt->context->node = tmp; |
|
9767 |
|
9768 #ifdef DEBUG_STEP |
|
9769 xmlGenericError(xmlGenericErrorContext, "\nExamined %d nodes, found %d nodes at that step\n", t, n); |
|
9770 #endif |
|
9771 |
|
9772 valuePush(ctxt, xmlXPathWrapNodeSet(ret)); |
|
9773 if(OOM_FLAG) |
|
9774 { |
|
9775 xmlXPathFreeObject(obj); |
|
9776 xmlXPathFreeNodeSet(list); |
|
9777 return(-1); |
|
9778 } |
|
9779 if ((obj->boolval) && (obj->user != NULL)) { |
|
9780 ctxt->value->boolval = 1; |
|
9781 ctxt->value->user = obj->user; |
|
9782 obj->user = NULL; |
|
9783 obj->boolval = 0; |
|
9784 } |
|
9785 xmlXPathFreeObject(obj); |
|
9786 return(t); |
|
9787 } |
|
9788 |
|
9789 /** |
|
9790 * xmlXPathNodeCollectAndTestNth: |
|
9791 * @ctxt: the XPath Parser context |
|
9792 * @op: the XPath precompiled step operation |
|
9793 * @indx: the index to collect |
|
9794 * @first: pointer to the first element in document order |
|
9795 * @last: pointer to the last element in document order |
|
9796 * |
|
9797 * This is the function implementing a step: based on the current list |
|
9798 * of nodes, it builds up a new list, looking at all nodes under that |
|
9799 * axis and selecting them. It also does the predicate filtering |
|
9800 * |
|
9801 * Pushes the new NodeSet resulting from the search. |
|
9802 * Returns the number of node traversed or -1 when OOM |
|
9803 */ |
|
9804 static int |
|
9805 xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt, |
|
9806 xmlXPathStepOpPtr op, int indx, |
|
9807 xmlNodePtr * first, xmlNodePtr * last) |
|
9808 { |
|
9809 xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; |
|
9810 xmlXPathTestVal test = (xmlXPathTestVal) op->value2; |
|
9811 xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3; |
|
9812 const xmlChar *prefix = (xmlChar*)op->value4; |
|
9813 const xmlChar *name = (xmlChar*)op->value5; |
|
9814 const xmlChar *URI = NULL; |
|
9815 int n = 0, t = 0; |
|
9816 |
|
9817 int i; |
|
9818 xmlNodeSetPtr list; |
|
9819 xmlXPathTraversalFunction next = NULL; |
|
9820 void (*addNode) (xmlNodeSetPtr, xmlNodePtr); |
|
9821 xmlNodePtr cur = NULL; |
|
9822 xmlXPathObjectPtr obj; |
|
9823 xmlNodeSetPtr nodelist; |
|
9824 xmlNodePtr tmp; |
|
9825 |
|
9826 CHECK_TYPE0(XPATH_NODESET); |
|
9827 obj = valuePop(ctxt); |
|
9828 addNode = xmlXPathNodeSetAdd; |
|
9829 if (prefix != NULL) { |
|
9830 URI = xmlXPathNsLookup(ctxt->context, prefix); |
|
9831 if (URI == NULL) |
|
9832 XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR); |
|
9833 } |
|
9834 #ifdef DEBUG_STEP_NTH |
|
9835 xmlGenericError(xmlGenericErrorContext, "new step : "); |
|
9836 if (first != NULL) { |
|
9837 if (*first != NULL) |
|
9838 xmlGenericError(xmlGenericErrorContext, "first = %s ", |
|
9839 (*first)->name); |
|
9840 else |
|
9841 xmlGenericError(xmlGenericErrorContext, "first = NULL "); |
|
9842 } |
|
9843 if (last != NULL) { |
|
9844 if (*last != NULL) |
|
9845 xmlGenericError(xmlGenericErrorContext, "last = %s ", |
|
9846 (*last)->name); |
|
9847 else |
|
9848 xmlGenericError(xmlGenericErrorContext, "last = NULL "); |
|
9849 } |
|
9850 #endif |
|
9851 switch (axis) { |
|
9852 case AXIS_ANCESTOR: |
|
9853 #ifdef DEBUG_STEP_NTH |
|
9854 xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' "); |
|
9855 #endif |
|
9856 first = NULL; |
|
9857 next = xmlXPathNextAncestor; |
|
9858 break; |
|
9859 case AXIS_ANCESTOR_OR_SELF: |
|
9860 #ifdef DEBUG_STEP_NTH |
|
9861 xmlGenericError(xmlGenericErrorContext, |
|
9862 "axis 'ancestors-or-self' "); |
|
9863 #endif |
|
9864 first = NULL; |
|
9865 next = xmlXPathNextAncestorOrSelf; |
|
9866 break; |
|
9867 case AXIS_ATTRIBUTE: |
|
9868 #ifdef DEBUG_STEP_NTH |
|
9869 xmlGenericError(xmlGenericErrorContext, "axis 'attributes' "); |
|
9870 #endif |
|
9871 first = NULL; |
|
9872 last = NULL; |
|
9873 next = xmlXPathNextAttribute; |
|
9874 break; |
|
9875 case AXIS_CHILD: |
|
9876 #ifdef DEBUG_STEP_NTH |
|
9877 xmlGenericError(xmlGenericErrorContext, "axis 'child' "); |
|
9878 #endif |
|
9879 last = NULL; |
|
9880 next = xmlXPathNextChild; |
|
9881 break; |
|
9882 case AXIS_DESCENDANT: |
|
9883 #ifdef DEBUG_STEP_NTH |
|
9884 xmlGenericError(xmlGenericErrorContext, "axis 'descendant' "); |
|
9885 #endif |
|
9886 last = NULL; |
|
9887 next = xmlXPathNextDescendant; |
|
9888 break; |
|
9889 case AXIS_DESCENDANT_OR_SELF: |
|
9890 #ifdef DEBUG_STEP_NTH |
|
9891 xmlGenericError(xmlGenericErrorContext, |
|
9892 "axis 'descendant-or-self' "); |
|
9893 #endif |
|
9894 last = NULL; |
|
9895 next = xmlXPathNextDescendantOrSelf; |
|
9896 break; |
|
9897 case AXIS_FOLLOWING: |
|
9898 #ifdef DEBUG_STEP_NTH |
|
9899 xmlGenericError(xmlGenericErrorContext, "axis 'following' "); |
|
9900 #endif |
|
9901 last = NULL; |
|
9902 next = xmlXPathNextFollowing; |
|
9903 break; |
|
9904 case AXIS_FOLLOWING_SIBLING: |
|
9905 #ifdef DEBUG_STEP_NTH |
|
9906 xmlGenericError(xmlGenericErrorContext, |
|
9907 "axis 'following-siblings' "); |
|
9908 #endif |
|
9909 last = NULL; |
|
9910 next = xmlXPathNextFollowingSibling; |
|
9911 break; |
|
9912 case AXIS_NAMESPACE: |
|
9913 #ifdef DEBUG_STEP_NTH |
|
9914 xmlGenericError(xmlGenericErrorContext, "axis 'namespace' "); |
|
9915 #endif |
|
9916 last = NULL; |
|
9917 first = NULL; |
|
9918 next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; |
|
9919 break; |
|
9920 case AXIS_PARENT: |
|
9921 #ifdef DEBUG_STEP_NTH |
|
9922 xmlGenericError(xmlGenericErrorContext, "axis 'parent' "); |
|
9923 #endif |
|
9924 first = NULL; |
|
9925 next = xmlXPathNextParent; |
|
9926 break; |
|
9927 case AXIS_PRECEDING: |
|
9928 #ifdef DEBUG_STEP_NTH |
|
9929 xmlGenericError(xmlGenericErrorContext, "axis 'preceding' "); |
|
9930 #endif |
|
9931 first = NULL; |
|
9932 next = xmlXPathNextPrecedingInternal; |
|
9933 break; |
|
9934 case AXIS_PRECEDING_SIBLING: |
|
9935 #ifdef DEBUG_STEP_NTH |
|
9936 xmlGenericError(xmlGenericErrorContext, |
|
9937 "axis 'preceding-sibling' "); |
|
9938 #endif |
|
9939 first = NULL; |
|
9940 next = xmlXPathNextPrecedingSibling; |
|
9941 break; |
|
9942 case AXIS_SELF: |
|
9943 #ifdef DEBUG_STEP_NTH |
|
9944 xmlGenericError(xmlGenericErrorContext, "axis 'self' "); |
|
9945 #endif |
|
9946 first = NULL; |
|
9947 last = NULL; |
|
9948 next = xmlXPathNextSelf; |
|
9949 break; |
|
9950 } |
|
9951 if (next == NULL) |
|
9952 return(0); |
|
9953 |
|
9954 nodelist = obj->nodesetval; |
|
9955 if (nodelist == NULL) { |
|
9956 xmlXPathFreeObject(obj); |
|
9957 valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); |
|
9958 return(0); |
|
9959 } |
|
9960 addNode = xmlXPathNodeSetAddUnique; |
|
9961 #ifdef DEBUG_STEP_NTH |
|
9962 xmlGenericError(xmlGenericErrorContext, |
|
9963 " context contains %d nodes\n", nodelist->nodeNr); |
|
9964 switch (test) { |
|
9965 case NODE_TEST_NONE: |
|
9966 xmlGenericError(xmlGenericErrorContext, |
|
9967 " searching for none !!!\n"); |
|
9968 break; |
|
9969 case NODE_TEST_TYPE: |
|
9970 xmlGenericError(xmlGenericErrorContext, |
|
9971 " searching for type %d\n", type); |
|
9972 break; |
|
9973 case NODE_TEST_PI: |
|
9974 xmlGenericError(xmlGenericErrorContext, |
|
9975 " searching for PI !!!\n"); |
|
9976 break; |
|
9977 case NODE_TEST_ALL: |
|
9978 xmlGenericError(xmlGenericErrorContext, |
|
9979 " searching for *\n"); |
|
9980 break; |
|
9981 case NODE_TEST_NS: |
|
9982 xmlGenericError(xmlGenericErrorContext, |
|
9983 " searching for namespace %s\n", |
|
9984 prefix); |
|
9985 break; |
|
9986 case NODE_TEST_NAME: |
|
9987 xmlGenericError(xmlGenericErrorContext, |
|
9988 " searching for name %s\n", name); |
|
9989 if (prefix != NULL) |
|
9990 xmlGenericError(xmlGenericErrorContext, |
|
9991 " with namespace %s\n", prefix); |
|
9992 break; |
|
9993 } |
|
9994 xmlGenericError(xmlGenericErrorContext, "Testing : "); |
|
9995 #endif |
|
9996 /* |
|
9997 * 2.3 Node Tests |
|
9998 * - For the attribute axis, the principal node type is attribute. |
|
9999 * - For the namespace axis, the principal node type is namespace. |
|
10000 * - For other axes, the principal node type is element. |
|
10001 * |
|
10002 * A node test * is true for any node of the |
|
10003 * principal node type. For example, child::* will |
|
10004 * select all element children of the context node |
|
10005 */ |
|
10006 tmp = ctxt->context->node; |
|
10007 list = xmlXPathNodeSetCreate(NULL); |
|
10008 if(OOM_FLAG) |
|
10009 { |
|
10010 xmlXPathFreeObject(obj); |
|
10011 return(-1); |
|
10012 } |
|
10013 for (i = 0; i < nodelist->nodeNr; i++) { |
|
10014 ctxt->context->node = nodelist->nodeTab[i]; |
|
10015 |
|
10016 cur = NULL; |
|
10017 n = 0; |
|
10018 do { |
|
10019 cur = next(ctxt, cur); |
|
10020 if (cur == NULL) |
|
10021 break; |
|
10022 if ((first != NULL) && (*first == cur)) |
|
10023 break; |
|
10024 if (((t % 256) == 0) && |
|
10025 (first != NULL) && (*first != NULL) && |
|
10026 (xmlXPathCmpNodes(*first, cur) >= 0)) |
|
10027 break; |
|
10028 if ((last != NULL) && (*last == cur)) |
|
10029 break; |
|
10030 if (((t % 256) == 0) && |
|
10031 (last != NULL) && (*last != NULL) && |
|
10032 (xmlXPathCmpNodes(cur, *last) >= 0)) |
|
10033 break; |
|
10034 t++; |
|
10035 switch (test) { |
|
10036 case NODE_TEST_NONE: |
|
10037 ctxt->context->node = tmp; |
|
10038 STRANGE return(0); |
|
10039 case NODE_TEST_TYPE: |
|
10040 if ((cur->type == type) || |
|
10041 ((type == NODE_TYPE_NODE) && |
|
10042 ((cur->type == XML_DOCUMENT_NODE) || |
|
10043 (cur->type == XML_HTML_DOCUMENT_NODE) || |
|
10044 (cur->type == XML_ELEMENT_NODE) || |
|
10045 (cur->type == XML_PI_NODE) || |
|
10046 (cur->type == XML_COMMENT_NODE) || |
|
10047 (cur->type == XML_CDATA_SECTION_NODE) || |
|
10048 (cur->type == XML_TEXT_NODE))) || |
|
10049 ((type == NODE_TYPE_TEXT) && |
|
10050 (cur->type == XML_CDATA_SECTION_NODE))) { |
|
10051 n++; |
|
10052 if (n == indx) |
|
10053 addNode(list, cur); |
|
10054 } |
|
10055 break; |
|
10056 case NODE_TEST_PI: |
|
10057 if (cur->type == XML_PI_NODE) { |
|
10058 if ((name != NULL) && |
|
10059 (!xmlStrEqual(name, cur->name))) |
|
10060 break; |
|
10061 n++; |
|
10062 if (n == indx) |
|
10063 addNode(list, cur); |
|
10064 } |
|
10065 break; |
|
10066 case NODE_TEST_ALL: |
|
10067 if (axis == AXIS_ATTRIBUTE) { |
|
10068 if (cur->type == XML_ATTRIBUTE_NODE) { |
|
10069 n++; |
|
10070 if (n == indx) |
|
10071 addNode(list, cur); |
|
10072 } |
|
10073 } else if (axis == AXIS_NAMESPACE) { |
|
10074 if (cur->type == XML_NAMESPACE_DECL) { |
|
10075 n++; |
|
10076 if (n == indx) |
|
10077 xmlXPathNodeSetAddNs(list, ctxt->context->node, |
|
10078 (xmlNsPtr) cur); |
|
10079 } |
|
10080 } else { |
|
10081 if (cur->type == XML_ELEMENT_NODE) { |
|
10082 if (prefix == NULL) { |
|
10083 n++; |
|
10084 if (n == indx) |
|
10085 addNode(list, cur); |
|
10086 } else if ((cur->ns != NULL) && |
|
10087 (xmlStrEqual(URI, cur->ns->href))) { |
|
10088 n++; |
|
10089 if (n == indx) |
|
10090 addNode(list, cur); |
|
10091 } |
|
10092 } |
|
10093 } |
|
10094 break; |
|
10095 case NODE_TEST_NS:{ |
|
10096 TODO; |
|
10097 break; |
|
10098 } |
|
10099 case NODE_TEST_NAME: |
|
10100 switch (cur->type) { |
|
10101 case XML_ELEMENT_NODE: |
|
10102 if (xmlStrEqual(name, cur->name)) { |
|
10103 if (prefix == NULL) { |
|
10104 if (cur->ns == NULL) { |
|
10105 n++; |
|
10106 if (n == indx) |
|
10107 { |
|
10108 addNode(list, cur); |
|
10109 if(OOM_FLAG) |
|
10110 { |
|
10111 xmlXPathFreeObject(obj); |
|
10112 xmlXPathFreeNodeSet(list); |
|
10113 return(-1); |
|
10114 } |
|
10115 } |
|
10116 |
|
10117 } |
|
10118 } else { |
|
10119 if ((cur->ns != NULL) && |
|
10120 (xmlStrEqual(URI, |
|
10121 cur->ns->href))) { |
|
10122 n++; |
|
10123 if (n == indx) |
|
10124 addNode(list, cur); |
|
10125 } |
|
10126 } |
|
10127 } |
|
10128 break; |
|
10129 case XML_ATTRIBUTE_NODE:{ |
|
10130 xmlAttrPtr attr = (xmlAttrPtr) cur; |
|
10131 |
|
10132 if (xmlStrEqual(name, attr->name)) { |
|
10133 if (prefix == NULL) { |
|
10134 if ((attr->ns == NULL) || |
|
10135 (attr->ns->prefix == NULL)) { |
|
10136 n++; |
|
10137 if (n == indx) |
|
10138 addNode(list, cur); |
|
10139 } |
|
10140 } else { |
|
10141 if ((attr->ns != NULL) && |
|
10142 (xmlStrEqual(URI, |
|
10143 attr->ns-> |
|
10144 href))) { |
|
10145 n++; |
|
10146 if (n == indx) |
|
10147 addNode(list, cur); |
|
10148 } |
|
10149 } |
|
10150 } |
|
10151 break; |
|
10152 } |
|
10153 case XML_NAMESPACE_DECL: |
|
10154 if (cur->type == XML_NAMESPACE_DECL) { |
|
10155 xmlNsPtr ns = (xmlNsPtr) cur; |
|
10156 |
|
10157 if ((ns->prefix != NULL) && (name != NULL) |
|
10158 && (xmlStrEqual(ns->prefix, name))) { |
|
10159 n++; |
|
10160 if (n == indx) |
|
10161 xmlXPathNodeSetAddNs(list, |
|
10162 ctxt->context->node, (xmlNsPtr) cur); |
|
10163 } |
|
10164 } |
|
10165 break; |
|
10166 default: |
|
10167 break; |
|
10168 } |
|
10169 break; |
|
10170 break; |
|
10171 } |
|
10172 } while (n < indx); |
|
10173 } |
|
10174 ctxt->context->node = tmp; |
|
10175 #ifdef DEBUG_STEP_NTH |
|
10176 xmlGenericError(xmlGenericErrorContext, |
|
10177 "\nExamined %d nodes, found %d nodes at that step\n", |
|
10178 t, list->nodeNr); |
|
10179 #endif |
|
10180 valuePush(ctxt, xmlXPathWrapNodeSet(list)); |
|
10181 if(OOM_FLAG) |
|
10182 { |
|
10183 xmlXPathFreeObject(obj); |
|
10184 xmlXPathFreeNodeSet(list); |
|
10185 return(-1); |
|
10186 } |
|
10187 if ((obj->boolval) && (obj->user != NULL)) { |
|
10188 ctxt->value->boolval = 1; |
|
10189 ctxt->value->user = obj->user; |
|
10190 obj->user = NULL; |
|
10191 obj->boolval = 0; |
|
10192 } |
|
10193 xmlXPathFreeObject(obj); |
|
10194 return(t); |
|
10195 } |
|
10196 |
|
10197 /** |
|
10198 * xmlXPathCompOpEvalFirst: |
|
10199 * @ctxt: the XPath parser context with the compiled expression |
|
10200 * @op: an XPath compiled operation |
|
10201 * @first: the first elem found so far |
|
10202 * |
|
10203 * Evaluate the Precompiled XPath operation searching only the first |
|
10204 * element in document order |
|
10205 * |
|
10206 * Returns the number of examined objects. |
|
10207 */ |
|
10208 static int |
|
10209 xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, |
|
10210 xmlXPathStepOpPtr op, xmlNodePtr * first) |
|
10211 { |
|
10212 int total = 0, cur; |
|
10213 xmlXPathCompExprPtr comp; |
|
10214 xmlXPathObjectPtr arg1, arg2; |
|
10215 |
|
10216 CHECK_ERROR0; |
|
10217 comp = ctxt->comp; |
|
10218 switch (op->op) { |
|
10219 case XPATH_OP_END: |
|
10220 return (0); |
|
10221 case XPATH_OP_UNION: |
|
10222 total = |
|
10223 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], |
|
10224 first); |
|
10225 CHECK_ERROR0; |
|
10226 if ((ctxt->value != NULL) |
|
10227 && (ctxt->value->type == XPATH_NODESET) |
|
10228 && (ctxt->value->nodesetval != NULL) |
|
10229 && (ctxt->value->nodesetval->nodeNr >= 1)) { |
|
10230 /* |
|
10231 * limit tree traversing to first node in the result |
|
10232 */ |
|
10233 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10234 *first = ctxt->value->nodesetval->nodeTab[0]; |
|
10235 } |
|
10236 cur = |
|
10237 xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2], |
|
10238 first); |
|
10239 CHECK_ERROR0; |
|
10240 CHECK_TYPE0(XPATH_NODESET); |
|
10241 arg2 = valuePop(ctxt); |
|
10242 |
|
10243 CHECK_TYPE0(XPATH_NODESET); |
|
10244 arg1 = valuePop(ctxt); |
|
10245 |
|
10246 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, |
|
10247 arg2->nodesetval); |
|
10248 valuePush(ctxt, arg1); |
|
10249 xmlXPathFreeObject(arg2); |
|
10250 /* optimizer */ |
|
10251 if (total > cur) |
|
10252 xmlXPathCompSwap(op); |
|
10253 return (total + cur); |
|
10254 case XPATH_OP_ROOT: |
|
10255 xmlXPathRoot(ctxt); |
|
10256 return (0); |
|
10257 case XPATH_OP_NODE: |
|
10258 if (op->ch1 != -1) |
|
10259 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10260 CHECK_ERROR0; |
|
10261 if (op->ch2 != -1) |
|
10262 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10263 CHECK_ERROR0; |
|
10264 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
10265 return (total); |
|
10266 case XPATH_OP_RESET: |
|
10267 if (op->ch1 != -1) |
|
10268 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10269 CHECK_ERROR0; |
|
10270 if (op->ch2 != -1) |
|
10271 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10272 CHECK_ERROR0; |
|
10273 ctxt->context->node = NULL; |
|
10274 return (total); |
|
10275 case XPATH_OP_COLLECT:{ |
|
10276 if (op->ch1 == -1) |
|
10277 return (total); |
|
10278 |
|
10279 total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10280 CHECK_ERROR0; |
|
10281 |
|
10282 /* |
|
10283 * Optimization for [n] selection where n is a number |
|
10284 */ |
|
10285 if ((op->ch2 != -1) && |
|
10286 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && |
|
10287 (comp->steps[op->ch2].ch1 == -1) && |
|
10288 (comp->steps[op->ch2].ch2 != -1) && |
|
10289 (comp->steps[comp->steps[op->ch2].ch2].op == |
|
10290 XPATH_OP_VALUE)) { |
|
10291 xmlXPathObjectPtr val; |
|
10292 |
|
10293 val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4; |
|
10294 if ((val != NULL) && (val->type == XPATH_NUMBER)) { |
|
10295 int indx = (int) val->floatval; |
|
10296 |
|
10297 if (val->floatval == (float) indx) { |
|
10298 xmlXPathNodeCollectAndTestNth(ctxt, op, indx, |
|
10299 first, NULL); |
|
10300 return (total); |
|
10301 } |
|
10302 } |
|
10303 } |
|
10304 total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL); |
|
10305 return (total); |
|
10306 } |
|
10307 case XPATH_OP_VALUE: |
|
10308 valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); |
|
10309 return (0); |
|
10310 |
|
10311 case XPATH_OP_SORT: |
|
10312 if (op->ch1 != -1) |
|
10313 total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], first); |
|
10314 CHECK_ERROR0; |
|
10315 if ((ctxt->value != NULL) |
|
10316 && (ctxt->value->type == XPATH_NODESET) |
|
10317 && (ctxt->value->nodesetval != NULL)) |
|
10318 { |
|
10319 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10320 } |
|
10321 return (total); |
|
10322 |
|
10323 default: |
|
10324 return (xmlXPathCompOpEval(ctxt, op)); |
|
10325 } |
|
10326 } |
|
10327 |
|
10328 /** |
|
10329 * xmlXPathCompOpEvalLast: |
|
10330 * @ctxt: the XPath parser context with the compiled expression |
|
10331 * @op: an XPath compiled operation |
|
10332 * @last: the last elem found so far |
|
10333 * |
|
10334 * Evaluate the Precompiled XPath operation searching only the last |
|
10335 * element in document order |
|
10336 * |
|
10337 * Returns the number of nodes traversed |
|
10338 */ |
|
10339 static int |
|
10340 xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op, |
|
10341 xmlNodePtr * last) |
|
10342 { |
|
10343 int total = 0, cur; |
|
10344 xmlXPathCompExprPtr comp; |
|
10345 xmlXPathObjectPtr arg1, arg2; |
|
10346 xmlNodePtr bak; |
|
10347 xmlDocPtr bakd; |
|
10348 int pp; |
|
10349 int cs; |
|
10350 |
|
10351 CHECK_ERROR0; |
|
10352 comp = ctxt->comp; |
|
10353 switch (op->op) { |
|
10354 case XPATH_OP_END: |
|
10355 return (0); |
|
10356 case XPATH_OP_UNION: |
|
10357 bakd = ctxt->context->doc; |
|
10358 bak = ctxt->context->node; |
|
10359 pp = ctxt->context->proximityPosition; |
|
10360 cs = ctxt->context->contextSize; |
|
10361 total = |
|
10362 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last); |
|
10363 CHECK_ERROR0; |
|
10364 if ((ctxt->value != NULL) |
|
10365 && (ctxt->value->type == XPATH_NODESET) |
|
10366 && (ctxt->value->nodesetval != NULL) |
|
10367 && (ctxt->value->nodesetval->nodeNr >= 1)) { |
|
10368 /* |
|
10369 * limit tree traversing to first node in the result |
|
10370 */ |
|
10371 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10372 *last = |
|
10373 ctxt->value->nodesetval->nodeTab[ctxt->value-> |
|
10374 nodesetval->nodeNr - |
|
10375 1]; |
|
10376 } |
|
10377 ctxt->context->doc = bakd; |
|
10378 ctxt->context->node = bak; |
|
10379 ctxt->context->proximityPosition = pp; |
|
10380 ctxt->context->contextSize = cs; |
|
10381 cur = |
|
10382 xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last); |
|
10383 CHECK_ERROR0; |
|
10384 if ((ctxt->value != NULL) |
|
10385 && (ctxt->value->type == XPATH_NODESET) |
|
10386 && (ctxt->value->nodesetval != NULL) |
|
10387 && (ctxt->value->nodesetval->nodeNr >= 1)) { |
|
10388 } |
|
10389 CHECK_TYPE0(XPATH_NODESET); |
|
10390 arg2 = valuePop(ctxt); |
|
10391 |
|
10392 CHECK_TYPE0(XPATH_NODESET); |
|
10393 arg1 = valuePop(ctxt); |
|
10394 |
|
10395 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, |
|
10396 arg2->nodesetval); |
|
10397 valuePush(ctxt, arg1); |
|
10398 xmlXPathFreeObject(arg2); |
|
10399 /* optimizer */ |
|
10400 if (total > cur) |
|
10401 xmlXPathCompSwap(op); |
|
10402 return (total + cur); |
|
10403 case XPATH_OP_ROOT: |
|
10404 xmlXPathRoot(ctxt); |
|
10405 return (0); |
|
10406 case XPATH_OP_NODE: |
|
10407 if (op->ch1 != -1) |
|
10408 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10409 CHECK_ERROR0; |
|
10410 if (op->ch2 != -1) |
|
10411 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10412 CHECK_ERROR0; |
|
10413 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
10414 return (total); |
|
10415 case XPATH_OP_RESET: |
|
10416 if (op->ch1 != -1) |
|
10417 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10418 CHECK_ERROR0; |
|
10419 if (op->ch2 != -1) |
|
10420 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10421 CHECK_ERROR0; |
|
10422 ctxt->context->node = NULL; |
|
10423 return (total); |
|
10424 case XPATH_OP_COLLECT:{ |
|
10425 if (op->ch1 == -1) |
|
10426 return (0); |
|
10427 |
|
10428 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10429 CHECK_ERROR0; |
|
10430 |
|
10431 /* |
|
10432 * Optimization for [n] selection where n is a number |
|
10433 */ |
|
10434 if ((op->ch2 != -1) && |
|
10435 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && |
|
10436 (comp->steps[op->ch2].ch1 == -1) && |
|
10437 (comp->steps[op->ch2].ch2 != -1) && |
|
10438 (comp->steps[comp->steps[op->ch2].ch2].op == |
|
10439 XPATH_OP_VALUE)) { |
|
10440 xmlXPathObjectPtr val; |
|
10441 |
|
10442 val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4; |
|
10443 if ((val != NULL) && (val->type == XPATH_NUMBER)) { |
|
10444 int indx = (int) val->floatval; |
|
10445 |
|
10446 if (val->floatval == (float) indx) { |
|
10447 total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, last); |
|
10448 return (total); |
|
10449 } |
|
10450 } |
|
10451 } |
|
10452 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last); |
|
10453 return (total); |
|
10454 } |
|
10455 case XPATH_OP_VALUE: |
|
10456 valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); |
|
10457 return (0); |
|
10458 |
|
10459 case XPATH_OP_SORT: |
|
10460 if (op->ch1 != -1) |
|
10461 total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last); |
|
10462 CHECK_ERROR0; |
|
10463 if ((ctxt->value != NULL) |
|
10464 && (ctxt->value->type == XPATH_NODESET) |
|
10465 && (ctxt->value->nodesetval != NULL)) |
|
10466 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
10467 { |
|
10468 return (total); |
|
10469 } |
|
10470 |
|
10471 default: |
|
10472 return (xmlXPathCompOpEval(ctxt, op)); |
|
10473 } |
|
10474 } |
|
10475 |
|
10476 /** |
|
10477 * xmlXPathCompOpEval: |
|
10478 * @ctxt: the XPath parser context with the compiled expression |
|
10479 * @op: an XPath compiled operation |
|
10480 * |
|
10481 * Evaluate the Precompiled XPath operation |
|
10482 * Returns the number of nodes traversed or -1 when OOM |
|
10483 */ |
|
10484 static int |
|
10485 xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
|
10486 { |
|
10487 int total = 0; |
|
10488 int equal, ret; |
|
10489 xmlXPathCompExprPtr comp; |
|
10490 xmlXPathObjectPtr arg1, arg2; |
|
10491 xmlNodePtr bak; |
|
10492 xmlDocPtr bakd; |
|
10493 int pp; |
|
10494 int cs; |
|
10495 |
|
10496 CHECK_ERROR0; |
|
10497 |
|
10498 comp = ctxt->comp; |
|
10499 //TODO: OPTIMIZE: xmlXPathContextPtr xpctxt = ctxt->context; // and reuse it below |
|
10500 switch (op->op) |
|
10501 { |
|
10502 case XPATH_OP_END: return (0); |
|
10503 case XPATH_OP_AND: { |
|
10504 bakd = ctxt->context->doc; |
|
10505 bak = ctxt->context->node; |
|
10506 pp = ctxt->context->proximityPosition; |
|
10507 cs = ctxt->context->contextSize; |
|
10508 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10509 CHECK_ERROR0; |
|
10510 |
|
10511 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10512 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10513 #endif |
|
10514 |
|
10515 xmlXPathBooleanFunction(ctxt, 1); |
|
10516 if ((ctxt->value == NULL) || (ctxt->value->boolval == 0)) |
|
10517 return (total); |
|
10518 arg2 = valuePop(ctxt); |
|
10519 ctxt->context->doc = bakd; |
|
10520 ctxt->context->node = bak; |
|
10521 ctxt->context->proximityPosition = pp; |
|
10522 ctxt->context->contextSize = cs; |
|
10523 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10524 if (ctxt->error) { |
|
10525 xmlXPathFreeObject(arg2); |
|
10526 return(0); |
|
10527 } |
|
10528 |
|
10529 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10530 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10531 #endif |
|
10532 xmlXPathBooleanFunction(ctxt, 1); |
|
10533 arg1 = valuePop(ctxt); |
|
10534 arg1->boolval &= arg2->boolval; |
|
10535 valuePush(ctxt, arg1); |
|
10536 xmlXPathFreeObject(arg2); |
|
10537 return (total); |
|
10538 } |
|
10539 |
|
10540 case XPATH_OP_OR: { |
|
10541 bakd = ctxt->context->doc; |
|
10542 bak = ctxt->context->node; |
|
10543 pp = ctxt->context->proximityPosition; |
|
10544 cs = ctxt->context->contextSize; |
|
10545 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10546 CHECK_ERROR0; |
|
10547 |
|
10548 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10549 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10550 #endif |
|
10551 xmlXPathBooleanFunction(ctxt, 1); |
|
10552 /* |
|
10553 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10554 // in dependency evaluation mode we must always process both parts |
|
10555 // of OR operation in the XPath expression |
|
10556 if (!ctxt->context->dependencyList && (ctxt->value || (ctxt->value->boolval == 1))) |
|
10557 return (total); |
|
10558 #else |
|
10559 */ |
|
10560 if ((ctxt->value == NULL) || (ctxt->value->boolval == 1)) |
|
10561 return (total); |
|
10562 /* |
|
10563 #endif |
|
10564 */ |
|
10565 arg2 = valuePop(ctxt); |
|
10566 ctxt->context->doc = bakd; |
|
10567 ctxt->context->node = bak; |
|
10568 ctxt->context->proximityPosition = pp; |
|
10569 ctxt->context->contextSize = cs; |
|
10570 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10571 |
|
10572 if (ctxt->error) { |
|
10573 xmlXPathFreeObject(arg2); |
|
10574 return(0); |
|
10575 } |
|
10576 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10577 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10578 #endif |
|
10579 xmlXPathBooleanFunction(ctxt, 1); |
|
10580 arg1 = valuePop(ctxt); |
|
10581 arg1->boolval |= arg2->boolval; |
|
10582 valuePush(ctxt, arg1); |
|
10583 xmlXPathFreeObject(arg2); |
|
10584 return (total); |
|
10585 } |
|
10586 |
|
10587 case XPATH_OP_EQUAL: { |
|
10588 bakd = ctxt->context->doc; |
|
10589 bak = ctxt->context->node; |
|
10590 pp = ctxt->context->proximityPosition; |
|
10591 cs = ctxt->context->contextSize; |
|
10592 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10593 CHECK_ERROR0; |
|
10594 |
|
10595 ctxt->context->doc = bakd; |
|
10596 ctxt->context->node = bak; |
|
10597 ctxt->context->proximityPosition = pp; |
|
10598 ctxt->context->contextSize = cs; |
|
10599 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10600 CHECK_ERROR0; |
|
10601 |
|
10602 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10603 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10604 #endif |
|
10605 |
|
10606 if (op->value) |
|
10607 equal = xmlXPathEqualValues(ctxt); |
|
10608 else |
|
10609 equal = xmlXPathNotEqualValues(ctxt); |
|
10610 |
|
10611 valuePush(ctxt, xmlXPathNewBoolean(equal)); |
|
10612 return (total); |
|
10613 } |
|
10614 |
|
10615 case XPATH_OP_CMP: { |
|
10616 bakd = ctxt->context->doc; |
|
10617 bak = ctxt->context->node; |
|
10618 pp = ctxt->context->proximityPosition; |
|
10619 cs = ctxt->context->contextSize; |
|
10620 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10621 CHECK_ERROR0; |
|
10622 |
|
10623 ctxt->context->doc = bakd; |
|
10624 ctxt->context->node = bak; |
|
10625 ctxt->context->proximityPosition = pp; |
|
10626 ctxt->context->contextSize = cs; |
|
10627 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10628 CHECK_ERROR0; |
|
10629 |
|
10630 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10631 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10632 #endif |
|
10633 ret = xmlXPathCompareValues(ctxt, op->value, op->value2); |
|
10634 valuePush(ctxt, xmlXPathNewBoolean(ret)); |
|
10635 return (total); |
|
10636 } |
|
10637 |
|
10638 case XPATH_OP_PLUS: { |
|
10639 bakd = ctxt->context->doc; |
|
10640 bak = ctxt->context->node; |
|
10641 pp = ctxt->context->proximityPosition; |
|
10642 cs = ctxt->context->contextSize; |
|
10643 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10644 CHECK_ERROR0; |
|
10645 |
|
10646 if (op->ch2 != -1) { |
|
10647 ctxt->context->doc = bakd; |
|
10648 ctxt->context->node = bak; |
|
10649 ctxt->context->proximityPosition = pp; |
|
10650 ctxt->context->contextSize = cs; |
|
10651 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10652 } |
|
10653 CHECK_ERROR0; |
|
10654 |
|
10655 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10656 // TODO: OPTIMIZATION: Can we use (op->value!=2) here? / Is reordering required? |
|
10657 if (op->value == 0 || op->value == 1) |
|
10658 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10659 else if (op->value == 2) |
|
10660 addNodeSetsFromStackToDependencyList(ctxt, 1); |
|
10661 #endif |
|
10662 // TODO: OPTIMIZATION: Reordering accordig to value type frequency |
|
10663 if (op->value == 0) |
|
10664 xmlXPathSubValues(ctxt); |
|
10665 else if (op->value == 1) |
|
10666 xmlXPathAddValues(ctxt); |
|
10667 else if (op->value == 2) |
|
10668 xmlXPathValueFlipSign(ctxt); |
|
10669 else if (op->value == 3) { |
|
10670 CAST_TO_NUMBER; |
|
10671 CHECK_TYPE0(XPATH_NUMBER); |
|
10672 } |
|
10673 return (total); |
|
10674 } |
|
10675 |
|
10676 case XPATH_OP_MULT: { |
|
10677 bakd = ctxt->context->doc; |
|
10678 bak = ctxt->context->node; |
|
10679 pp = ctxt->context->proximityPosition; |
|
10680 cs = ctxt->context->contextSize; |
|
10681 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10682 CHECK_ERROR0; |
|
10683 |
|
10684 ctxt->context->doc = bakd; |
|
10685 ctxt->context->node = bak; |
|
10686 ctxt->context->proximityPosition = pp; |
|
10687 ctxt->context->contextSize = cs; |
|
10688 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10689 CHECK_ERROR0; |
|
10690 |
|
10691 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10692 addNodeSetsFromStackToDependencyList(ctxt, 2); |
|
10693 #endif |
|
10694 |
|
10695 if (op->value == 0) |
|
10696 xmlXPathMultValues(ctxt); |
|
10697 else if (op->value == 1) |
|
10698 xmlXPathDivValues(ctxt); |
|
10699 else if (op->value == 2) |
|
10700 xmlXPathModValues(ctxt); |
|
10701 return (total); |
|
10702 } |
|
10703 |
|
10704 case XPATH_OP_UNION: { |
|
10705 bakd = ctxt->context->doc; |
|
10706 bak = ctxt->context->node; |
|
10707 pp = ctxt->context->proximityPosition; |
|
10708 cs = ctxt->context->contextSize; |
|
10709 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10710 CHECK_ERROR0; |
|
10711 |
|
10712 ctxt->context->doc = bakd; |
|
10713 ctxt->context->node = bak; |
|
10714 ctxt->context->proximityPosition = pp; |
|
10715 ctxt->context->contextSize = cs; |
|
10716 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10717 CHECK_ERROR0; |
|
10718 |
|
10719 CHECK_TYPE0(XPATH_NODESET); |
|
10720 arg2 = valuePop(ctxt); |
|
10721 |
|
10722 CHECK_TYPE0(XPATH_NODESET); |
|
10723 arg1 = valuePop(ctxt); |
|
10724 |
|
10725 arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, arg2->nodesetval); |
|
10726 valuePush(ctxt, arg1); |
|
10727 xmlXPathFreeObject(arg2); |
|
10728 |
|
10729 return (total); |
|
10730 } |
|
10731 |
|
10732 case XPATH_OP_ROOT: { |
|
10733 xmlXPathRoot(ctxt); |
|
10734 return (total); |
|
10735 } |
|
10736 |
|
10737 case XPATH_OP_NODE: { |
|
10738 if (op->ch1 != -1) |
|
10739 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10740 CHECK_ERROR0; |
|
10741 |
|
10742 if (op->ch2 != -1) |
|
10743 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10744 CHECK_ERROR0; |
|
10745 |
|
10746 valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node)); |
|
10747 if(OOM_FLAG) return(-1); |
|
10748 return (total); |
|
10749 } |
|
10750 |
|
10751 case XPATH_OP_RESET: { |
|
10752 if (op->ch1 != -1) |
|
10753 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10754 CHECK_ERROR0; |
|
10755 |
|
10756 if (op->ch2 != -1) |
|
10757 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10758 CHECK_ERROR0; |
|
10759 |
|
10760 ctxt->context->node = NULL; |
|
10761 return (total); |
|
10762 } |
|
10763 |
|
10764 case XPATH_OP_COLLECT: { |
|
10765 if (op->ch1 == -1) |
|
10766 return (total); |
|
10767 |
|
10768 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10769 if(OOM_FLAG) return(-1); |
|
10770 CHECK_ERROR0; |
|
10771 |
|
10772 /* |
|
10773 * Optimization for [n] selection where n is a number |
|
10774 */ |
|
10775 if ((op->ch2 != -1) && |
|
10776 (comp->steps[op->ch2].op == XPATH_OP_PREDICATE) && |
|
10777 (comp->steps[op->ch2].ch1 == -1) && |
|
10778 (comp->steps[op->ch2].ch2 != -1) && |
|
10779 (comp->steps[comp->steps[op->ch2].ch2].op == XPATH_OP_VALUE)) |
|
10780 { |
|
10781 xmlXPathObjectPtr val; |
|
10782 |
|
10783 val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4; |
|
10784 if ((val != NULL) && (val->type == XPATH_NUMBER)) { |
|
10785 int indx = (int) val->floatval; |
|
10786 |
|
10787 if (val->floatval == (float) indx) { |
|
10788 total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, NULL); |
|
10789 if(OOM_FLAG) return(-1); |
|
10790 return (total); |
|
10791 } |
|
10792 } |
|
10793 } |
|
10794 |
|
10795 total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL); |
|
10796 if(OOM_FLAG) return(-1); |
|
10797 return (total); |
|
10798 } |
|
10799 |
|
10800 case XPATH_OP_VALUE: { |
|
10801 valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); |
|
10802 return (total); |
|
10803 } |
|
10804 |
|
10805 case XPATH_OP_VARIABLE: { |
|
10806 xmlXPathObjectPtr val; |
|
10807 |
|
10808 if (op->ch1 != -1) |
|
10809 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10810 |
|
10811 if (op->value5 == NULL) { |
|
10812 val = xmlXPathVariableLookup(ctxt->context, (xmlChar*) op->value4); |
|
10813 if (val == NULL) { |
|
10814 ctxt->error = XPATH_UNDEF_VARIABLE_ERROR; |
|
10815 return(0); |
|
10816 } |
|
10817 valuePush(ctxt, val); |
|
10818 } else { |
|
10819 const xmlChar *URI; |
|
10820 |
|
10821 URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5); |
|
10822 if (URI == NULL) { |
|
10823 xmlGenericError(xmlGenericErrorContext, |
|
10824 EMBED_ERRTXT("xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n"), |
|
10825 op->value4, op->value5); |
|
10826 return (total); |
|
10827 } |
|
10828 val = xmlXPathVariableLookupNS(ctxt->context, (xmlChar*) op->value4, URI); |
|
10829 if (val == NULL) { |
|
10830 ctxt->error = XPATH_UNDEF_VARIABLE_ERROR; |
|
10831 return(0); |
|
10832 } |
|
10833 valuePush(ctxt, val); |
|
10834 } |
|
10835 return (total); |
|
10836 } |
|
10837 |
|
10838 case XPATH_OP_FUNCTION: { |
|
10839 xmlXPathFunction func; |
|
10840 const xmlChar *oldFunc, *oldFuncURI; |
|
10841 int i; |
|
10842 |
|
10843 if (op->ch1 != -1) |
|
10844 { |
|
10845 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10846 if(OOM_FLAG) return(-1); |
|
10847 } |
|
10848 |
|
10849 if (ctxt->valueNr < op->value) { |
|
10850 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n")); |
|
10851 ctxt->error = XPATH_INVALID_OPERAND; |
|
10852 return (total); |
|
10853 } |
|
10854 |
|
10855 for (i = 0; i < op->value; i++) |
|
10856 if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) { |
|
10857 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n")); |
|
10858 ctxt->error = XPATH_INVALID_OPERAND; |
|
10859 return (total); |
|
10860 } |
|
10861 |
|
10862 if (op->cache != NULL){ |
|
10863 func = (xmlXPathFunction)op->cache;//(REINTERPRET_CAST(xmlXPathFunction,(op->cache))); |
|
10864 }else { |
|
10865 const xmlChar *URI = NULL; |
|
10866 |
|
10867 if (op->value5 == NULL) |
|
10868 func = xmlXPathFunctionLookup(ctxt->context, (xmlChar*)op->value4); |
|
10869 else { |
|
10870 URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5); |
|
10871 if (URI == NULL) { |
|
10872 xmlGenericError(xmlGenericErrorContext, |
|
10873 EMBED_ERRTXT("xmlXPathCompOpEval: function %s bound to undefined prefix %s\n"), |
|
10874 op->value4, op->value5); |
|
10875 return (total); |
|
10876 } |
|
10877 func = xmlXPathFunctionLookupNS(ctxt->context, (xmlChar*)op->value4, URI); |
|
10878 } |
|
10879 |
|
10880 if (func == NULL) { |
|
10881 xmlGenericError(xmlGenericErrorContext, |
|
10882 EMBED_ERRTXT("xmlXPathCompOpEval: function %s not found\n"), op->value4); |
|
10883 XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR); |
|
10884 } |
|
10885 |
|
10886 op->cache = (void *) func; |
|
10887 op->cacheURI = (void *) URI; |
|
10888 } // if (op->cache != NULL) |
|
10889 |
|
10890 oldFunc = ctxt->context->function; |
|
10891 oldFuncURI = ctxt->context->functionURI; |
|
10892 ctxt->context->function = (xmlChar*) op->value4; |
|
10893 ctxt->context->functionURI = (xmlChar*) op->cacheURI; |
|
10894 |
|
10895 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
10896 addNodeSetsFromStackToDependencyList(ctxt, op->value); |
|
10897 #endif |
|
10898 func(ctxt, op->value); |
|
10899 if(OOM_FLAG) return(-1); |
|
10900 ctxt->context->function = oldFunc; |
|
10901 ctxt->context->functionURI = oldFuncURI; |
|
10902 return (total); |
|
10903 } |
|
10904 |
|
10905 case XPATH_OP_ARG: { |
|
10906 bakd = ctxt->context->doc; |
|
10907 bak = ctxt->context->node; |
|
10908 if (op->ch1 != -1) |
|
10909 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
10910 ctxt->context->doc = bakd; |
|
10911 ctxt->context->node = bak; |
|
10912 CHECK_ERROR0; |
|
10913 |
|
10914 if (op->ch2 != -1) { |
|
10915 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
10916 if(OOM_FLAG) return(-1); |
|
10917 ctxt->context->doc = bakd; |
|
10918 ctxt->context->node = bak; |
|
10919 CHECK_ERROR0; |
|
10920 } |
|
10921 return (total); |
|
10922 } |
|
10923 |
|
10924 case XPATH_OP_PREDICATE: |
|
10925 case XPATH_OP_FILTER: { |
|
10926 xmlXPathObjectPtr res; |
|
10927 xmlXPathObjectPtr obj, tmp; |
|
10928 xmlNodeSetPtr newset = NULL; |
|
10929 xmlNodeSetPtr oldset; |
|
10930 xmlNodePtr oldnode; |
|
10931 int i; |
|
10932 |
|
10933 /* |
|
10934 * Optimization for ()[1] selection i.e. the first elem |
|
10935 */ |
|
10936 if ((op->ch1 != -1) && (op->ch2 != -1) && |
|
10937 (comp->steps[op->ch1].op == XPATH_OP_SORT) && |
|
10938 (comp->steps[op->ch2].op == XPATH_OP_VALUE)) |
|
10939 { |
|
10940 xmlXPathObjectPtr val; |
|
10941 |
|
10942 val = (xmlXPathObjectPtr)comp->steps[op->ch2].value4; |
|
10943 |
|
10944 if ((val != NULL) && (val->type == XPATH_NUMBER) && (val->floatval == 1.0)) |
|
10945 { |
|
10946 xmlNodePtr first = NULL; |
|
10947 |
|
10948 total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], &first); |
|
10949 CHECK_ERROR0; |
|
10950 /* |
|
10951 * The nodeset should be in document order, |
|
10952 * Keep only the first value |
|
10953 */ |
|
10954 if ((ctxt->value != NULL) && |
|
10955 (ctxt->value->type == XPATH_NODESET) && |
|
10956 (ctxt->value->nodesetval != NULL) && |
|
10957 (ctxt->value->nodesetval->nodeNr > 1)) |
|
10958 { |
|
10959 ctxt->value->nodesetval->nodeNr = 1; |
|
10960 } |
|
10961 return (total); |
|
10962 } |
|
10963 } |
|
10964 |
|
10965 /* |
|
10966 * Optimization for ()[last()] selection i.e. the last elem |
|
10967 */ |
|
10968 if ((op->ch1 != -1) && (op->ch2 != -1) && |
|
10969 (comp->steps[op->ch1].op == XPATH_OP_SORT) && |
|
10970 (comp->steps[op->ch2].op == XPATH_OP_SORT)) |
|
10971 { |
|
10972 int f = comp->steps[op->ch2].ch1; |
|
10973 |
|
10974 if ((f != -1) && |
|
10975 (comp->steps[f].op == XPATH_OP_FUNCTION) && |
|
10976 (comp->steps[f].value5 == NULL) && |
|
10977 (comp->steps[f].value == 0) && |
|
10978 (comp->steps[f].value4 != NULL) && |
|
10979 (xmlStrEqual((xmlChar*)comp->steps[f].value4, BAD_CAST "last"))) |
|
10980 { |
|
10981 xmlNodePtr last = NULL; |
|
10982 |
|
10983 total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], &last); |
|
10984 CHECK_ERROR0; |
|
10985 /* |
|
10986 * The nodeset should be in document order, |
|
10987 * Keep only the last value |
|
10988 */ |
|
10989 if ((ctxt->value != NULL) && |
|
10990 (ctxt->value->type == XPATH_NODESET) && |
|
10991 (ctxt->value->nodesetval != NULL) && |
|
10992 (ctxt->value->nodesetval->nodeTab != NULL) && |
|
10993 (ctxt->value->nodesetval->nodeNr > 1)) |
|
10994 { |
|
10995 ctxt->value->nodesetval->nodeTab[0] = |
|
10996 ctxt->value->nodesetval->nodeTab[ctxt->value->nodesetval->nodeNr - 1]; |
|
10997 ctxt->value->nodesetval->nodeNr = 1; |
|
10998 } |
|
10999 return (total); |
|
11000 } |
|
11001 } |
|
11002 |
|
11003 |
|
11004 if (op->ch1 != -1) |
|
11005 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11006 |
|
11007 CHECK_ERROR0; |
|
11008 |
|
11009 if (op->ch2 == -1) |
|
11010 return (total); |
|
11011 |
|
11012 if (ctxt->value == NULL) |
|
11013 return (total); |
|
11014 |
|
11015 oldnode = ctxt->context->node; |
|
11016 |
|
11017 #ifdef LIBXML_XPTR_ENABLED |
|
11018 /* |
|
11019 * Hum are we filtering the result of an XPointer expression |
|
11020 */ |
|
11021 if (ctxt->value->type == XPATH_LOCATIONSET) |
|
11022 { |
|
11023 xmlLocationSetPtr newlocset = NULL; |
|
11024 xmlLocationSetPtr oldlocset; |
|
11025 |
|
11026 /* |
|
11027 * Extract the old locset, and then evaluate the result of the |
|
11028 * expression for all the element in the locset. use it to grow |
|
11029 * up a new locset. |
|
11030 */ |
|
11031 CHECK_TYPE0(XPATH_LOCATIONSET); |
|
11032 |
|
11033 obj = valuePop(ctxt); |
|
11034 oldlocset = obj->user; |
|
11035 ctxt->context->node = NULL; |
|
11036 |
|
11037 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { |
|
11038 ctxt->context->contextSize = 0; |
|
11039 ctxt->context->proximityPosition = 0; |
|
11040 |
|
11041 if (op->ch2 != -1) |
|
11042 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11043 |
|
11044 res = valuePop(ctxt); |
|
11045 |
|
11046 if (res != NULL) |
|
11047 xmlXPathFreeObject(res); |
|
11048 |
|
11049 valuePush(ctxt, obj); |
|
11050 CHECK_ERROR0; |
|
11051 |
|
11052 return (total); |
|
11053 } |
|
11054 |
|
11055 newlocset = xmlXPtrLocationSetCreate(NULL); |
|
11056 |
|
11057 for (i = 0; i < oldlocset->locNr; i++) |
|
11058 { |
|
11059 /* |
|
11060 * Run the evaluation with a node list made of a |
|
11061 * single item in the nodelocset. |
|
11062 */ |
|
11063 ctxt->context->node = oldlocset->locTab[i]->user; |
|
11064 ctxt->context->contextSize = oldlocset->locNr; |
|
11065 ctxt->context->proximityPosition = i + 1; |
|
11066 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11067 valuePush(ctxt, tmp); |
|
11068 |
|
11069 if (op->ch2 != -1) |
|
11070 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11071 |
|
11072 CHECK_ERROR0; |
|
11073 /* |
|
11074 * The result of the evaluation need to be tested to |
|
11075 * decided whether the filter succeeded or not |
|
11076 */ |
|
11077 res = valuePop(ctxt); |
|
11078 if (xmlXPathEvaluatePredicateResult(ctxt, res)) |
|
11079 { |
|
11080 xmlXPtrLocationSetAdd(newlocset, xmlXPathObjectCopy(oldlocset->locTab[i])); |
|
11081 } |
|
11082 |
|
11083 /* |
|
11084 * Cleanup |
|
11085 */ |
|
11086 if (res != NULL) |
|
11087 xmlXPathFreeObject(res); |
|
11088 if (ctxt->value == tmp) { |
|
11089 res = valuePop(ctxt); |
|
11090 xmlXPathFreeObject(res); |
|
11091 } |
|
11092 |
|
11093 ctxt->context->node = NULL; |
|
11094 } // for (i = 0; i < oldlocset->locNr; i++) |
|
11095 |
|
11096 /* |
|
11097 * The result is used as the new evaluation locset. |
|
11098 */ |
|
11099 xmlXPathFreeObject(obj); |
|
11100 ctxt->context->node = NULL; |
|
11101 ctxt->context->contextSize = -1; |
|
11102 ctxt->context->proximityPosition = -1; |
|
11103 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); |
|
11104 ctxt->context->node = oldnode; |
|
11105 return (total); |
|
11106 } |
|
11107 #endif /* LIBXML_XPTR_ENABLED */ |
|
11108 |
|
11109 /* |
|
11110 * Extract the old set, and then evaluate the result of the |
|
11111 * expression for all the element in the set. use it to grow |
|
11112 * up a new set. |
|
11113 */ |
|
11114 CHECK_TYPE0(XPATH_NODESET); |
|
11115 |
|
11116 obj = valuePop(ctxt); |
|
11117 oldset = obj->nodesetval; |
|
11118 oldnode = ctxt->context->node; |
|
11119 ctxt->context->node = NULL; |
|
11120 |
|
11121 if ((oldset == NULL) || (oldset->nodeNr == 0)) |
|
11122 { |
|
11123 ctxt->context->contextSize = 0; |
|
11124 ctxt->context->proximityPosition = 0; |
|
11125 |
|
11126 if (op->ch2 != -1) |
|
11127 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11128 |
|
11129 CHECK_ERROR0; |
|
11130 |
|
11131 res = valuePop(ctxt); |
|
11132 if (res != NULL) |
|
11133 xmlXPathFreeObject(res); |
|
11134 |
|
11135 valuePush(ctxt, obj); |
|
11136 ctxt->context->node = oldnode; |
|
11137 CHECK_ERROR0; |
|
11138 } else { |
|
11139 /* |
|
11140 * Initialize the new set. |
|
11141 */ |
|
11142 newset = xmlXPathNodeSetCreate(NULL); |
|
11143 if(OOM_FLAG) return(-1); |
|
11144 |
|
11145 for (i = 0; i < oldset->nodeNr; i++) { |
|
11146 /* |
|
11147 * Run the evaluation with a node list made of |
|
11148 * a single item in the nodeset. |
|
11149 */ |
|
11150 ctxt->context->node = oldset->nodeTab[i]; |
|
11151 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11152 valuePush(ctxt, tmp); |
|
11153 ctxt->context->contextSize = oldset->nodeNr; |
|
11154 ctxt->context->proximityPosition = i + 1; |
|
11155 |
|
11156 if (op->ch2 != -1) |
|
11157 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11158 |
|
11159 CHECK_ERROR0; |
|
11160 |
|
11161 /* |
|
11162 * The result of the evaluation needs to be tested to |
|
11163 * decide whether the filter succeeded or not |
|
11164 */ |
|
11165 res = valuePop(ctxt); |
|
11166 if (xmlXPathEvaluatePredicateResult(ctxt, res)) |
|
11167 { |
|
11168 xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); |
|
11169 } |
|
11170 |
|
11171 /* |
|
11172 * Cleanup |
|
11173 */ |
|
11174 if (res != NULL) |
|
11175 xmlXPathFreeObject(res); |
|
11176 if (ctxt->value == tmp) { |
|
11177 res = valuePop(ctxt); |
|
11178 xmlXPathFreeObject(res); |
|
11179 } |
|
11180 |
|
11181 ctxt->context->node = NULL; |
|
11182 } // (i = 0; i < oldset->nodeNr; i++) |
|
11183 |
|
11184 /* |
|
11185 * The result is used as the new evaluation set. |
|
11186 */ |
|
11187 xmlXPathFreeObject(obj); |
|
11188 ctxt->context->node = NULL; |
|
11189 ctxt->context->contextSize = -1; |
|
11190 ctxt->context->proximityPosition = -1; |
|
11191 valuePush(ctxt, xmlXPathWrapNodeSet(newset)); |
|
11192 } |
|
11193 ctxt->context->node = oldnode; |
|
11194 return (total); |
|
11195 } |
|
11196 |
|
11197 case XPATH_OP_SORT: { |
|
11198 if (op->ch1 != -1) |
|
11199 { |
|
11200 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11201 if(OOM_FLAG) return(-1); |
|
11202 } |
|
11203 |
|
11204 CHECK_ERROR0; |
|
11205 |
|
11206 if ((ctxt->value != NULL) && |
|
11207 (ctxt->value->type == XPATH_NODESET) && |
|
11208 (ctxt->value->nodesetval != NULL)) |
|
11209 { |
|
11210 xmlXPathNodeSetSort(ctxt->value->nodesetval); |
|
11211 } |
|
11212 return (total); |
|
11213 } |
|
11214 |
|
11215 #ifdef LIBXML_XPTR_ENABLED |
|
11216 case XPATH_OP_RANGETO: { |
|
11217 xmlXPathObjectPtr range; |
|
11218 xmlXPathObjectPtr res, obj; |
|
11219 xmlXPathObjectPtr tmp; |
|
11220 xmlLocationSetPtr newlocset = NULL; |
|
11221 xmlLocationSetPtr oldlocset; |
|
11222 xmlNodeSetPtr oldset; |
|
11223 int i, j; |
|
11224 |
|
11225 if (op->ch1 != -1) |
|
11226 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
|
11227 if (op->ch2 == -1) |
|
11228 return (total); |
|
11229 |
|
11230 if (ctxt->value->type == XPATH_LOCATIONSET) |
|
11231 { |
|
11232 /* |
|
11233 * Extract the old locset, and then evaluate the result of the |
|
11234 * expression for all the element in the locset. use it to grow |
|
11235 * up a new locset. |
|
11236 */ |
|
11237 CHECK_TYPE0(XPATH_LOCATIONSET); |
|
11238 obj = valuePop(ctxt); |
|
11239 oldlocset = obj->user; |
|
11240 |
|
11241 if ((oldlocset == NULL) || (oldlocset->locNr == 0)) { |
|
11242 ctxt->context->node = NULL; |
|
11243 ctxt->context->contextSize = 0; |
|
11244 ctxt->context->proximityPosition = 0; |
|
11245 |
|
11246 total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]); |
|
11247 |
|
11248 res = valuePop(ctxt); |
|
11249 if (res != NULL) |
|
11250 xmlXPathFreeObject(res); |
|
11251 valuePush(ctxt, obj); |
|
11252 CHECK_ERROR0; |
|
11253 return (total); |
|
11254 } |
|
11255 newlocset = xmlXPtrLocationSetCreate(NULL); |
|
11256 |
|
11257 for (i = 0; i < oldlocset->locNr; i++) { |
|
11258 /* |
|
11259 * Run the evaluation with a node list made of a |
|
11260 * single item in the nodelocset. |
|
11261 */ |
|
11262 ctxt->context->node = oldlocset->locTab[i]->user; |
|
11263 ctxt->context->contextSize = oldlocset->locNr; |
|
11264 ctxt->context->proximityPosition = i + 1; |
|
11265 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11266 valuePush(ctxt, tmp); |
|
11267 |
|
11268 if (op->ch2 != -1) |
|
11269 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11270 |
|
11271 CHECK_ERROR0; |
|
11272 |
|
11273 res = valuePop(ctxt); |
|
11274 if (res->type == XPATH_LOCATIONSET) |
|
11275 { |
|
11276 xmlLocationSetPtr rloc = (xmlLocationSetPtr)res->user; |
|
11277 |
|
11278 for (j=0; j<rloc->locNr; j++) { |
|
11279 range = xmlXPtrNewRange( |
|
11280 oldlocset->locTab[i]->user, |
|
11281 oldlocset->locTab[i]->index, |
|
11282 rloc->locTab[j]->user2, |
|
11283 rloc->locTab[j]->index2); |
|
11284 |
|
11285 if (range != NULL) { |
|
11286 xmlXPtrLocationSetAdd(newlocset, range); |
|
11287 } |
|
11288 } |
|
11289 } else { |
|
11290 range = xmlXPtrNewRangeNodeObject((xmlNodePtr)oldlocset->locTab[i]->user, res); |
|
11291 if (range != NULL) { |
|
11292 xmlXPtrLocationSetAdd(newlocset,range); |
|
11293 } |
|
11294 } |
|
11295 |
|
11296 /* |
|
11297 * Cleanup |
|
11298 */ |
|
11299 if (res != NULL) |
|
11300 xmlXPathFreeObject(res); |
|
11301 if (ctxt->value == tmp) { |
|
11302 res = valuePop(ctxt); |
|
11303 xmlXPathFreeObject(res); |
|
11304 } |
|
11305 |
|
11306 ctxt->context->node = NULL; |
|
11307 } |
|
11308 |
|
11309 } |
|
11310 else |
|
11311 { /* Not a location set */ |
|
11312 CHECK_TYPE0(XPATH_NODESET); |
|
11313 obj = valuePop(ctxt); |
|
11314 oldset = obj->nodesetval; |
|
11315 ctxt->context->node = NULL; |
|
11316 |
|
11317 newlocset = xmlXPtrLocationSetCreate(NULL); |
|
11318 |
|
11319 if (oldset != NULL) { |
|
11320 for (i = 0; i < oldset->nodeNr; i++) { |
|
11321 /* |
|
11322 * Run the evaluation with a node list made of a single item |
|
11323 * in the nodeset. |
|
11324 */ |
|
11325 ctxt->context->node = oldset->nodeTab[i]; |
|
11326 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
11327 valuePush(ctxt, tmp); |
|
11328 |
|
11329 if (op->ch2 != -1) |
|
11330 total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
|
11331 |
|
11332 CHECK_ERROR0; |
|
11333 |
|
11334 res = valuePop(ctxt); |
|
11335 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res); |
|
11336 if (range != NULL) { |
|
11337 xmlXPtrLocationSetAdd(newlocset, range); |
|
11338 } |
|
11339 |
|
11340 /* |
|
11341 * Cleanup |
|
11342 */ |
|
11343 if (res != NULL) |
|
11344 xmlXPathFreeObject(res); |
|
11345 if (ctxt->value == tmp) { |
|
11346 res = valuePop(ctxt); |
|
11347 xmlXPathFreeObject(res); |
|
11348 } |
|
11349 |
|
11350 ctxt->context->node = NULL; |
|
11351 } // for |
|
11352 } |
|
11353 } |
|
11354 |
|
11355 /* |
|
11356 * The result is used as the new evaluation set. |
|
11357 */ |
|
11358 xmlXPathFreeObject(obj); |
|
11359 ctxt->context->node = NULL; |
|
11360 ctxt->context->contextSize = -1; |
|
11361 ctxt->context->proximityPosition = -1; |
|
11362 valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset)); |
|
11363 return (total); |
|
11364 } |
|
11365 #endif /* LIBXML_XPTR_ENABLED */ |
|
11366 } // switch(op->op) |
|
11367 |
|
11368 // None of options matched |
|
11369 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("XPath: unknown precompiled operation %d\n"), op->op); |
|
11370 return (total); |
|
11371 } |
|
11372 |
|
11373 /** |
|
11374 * xmlXPathRunEval: |
|
11375 * @ctxt: the XPath parser context with the compiled expression |
|
11376 * |
|
11377 * Evaluate the Precompiled XPath expression in the given context. |
|
11378 */ |
|
11379 static void |
|
11380 xmlXPathRunEval(xmlXPathParserContextPtr ctxt) { |
|
11381 xmlXPathCompExprPtr comp; |
|
11382 |
|
11383 if ((ctxt == NULL) || (ctxt->comp == NULL)) |
|
11384 return; |
|
11385 |
|
11386 if (ctxt->valueTab == NULL) { |
|
11387 /* Allocate the value stack */ |
|
11388 // TODO: Check what's the "magic" number here / Make a setup value |
|
11389 ctxt->valueTab = (xmlXPathObjectPtr *) |
|
11390 xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); |
|
11391 if (ctxt->valueTab == NULL) { |
|
11392 xmlXPathPErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n")); |
|
11393 xmlFree(ctxt); |
|
11394 } |
|
11395 ctxt->valueNr = 0; |
|
11396 ctxt->valueMax = 10; |
|
11397 ctxt->value = NULL; |
|
11398 } |
|
11399 comp = ctxt->comp; |
|
11400 if(comp->last < 0) { |
|
11401 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathRunEval: last is less than zero\n")); |
|
11402 return; |
|
11403 |
|
11404 } |
|
11405 xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]); |
|
11406 } |
|
11407 |
|
11408 /************************************************************************ |
|
11409 * * |
|
11410 * Public interfaces * |
|
11411 * * |
|
11412 ************************************************************************/ |
|
11413 |
|
11414 /** |
|
11415 * xmlXPathEvalPredicate: |
|
11416 * @ctxt: the XPath context |
|
11417 * @res: the Predicate Expression evaluation result |
|
11418 * |
|
11419 * Evaluate a predicate result for the current node. |
|
11420 * A PredicateExpr is evaluated by evaluating the Expr and converting |
|
11421 * the result to a boolean. If the result is a number, the result will |
|
11422 * be converted to true if the number is equal to the position of the |
|
11423 * context node in the context node list (as returned by the position |
|
11424 * function) and will be converted to false otherwise; if the result |
|
11425 * is not a number, then the result will be converted as if by a call |
|
11426 * to the boolean function. |
|
11427 * |
|
11428 * Returns 1 if predicate is true, 0 otherwise |
|
11429 */ |
|
11430 int |
|
11431 xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) { |
|
11432 if (res == NULL) return(0); |
|
11433 switch (res->type) { |
|
11434 case XPATH_BOOLEAN: |
|
11435 return(res->boolval); |
|
11436 case XPATH_NUMBER: |
|
11437 return(res->floatval == ctxt->proximityPosition); |
|
11438 case XPATH_NODESET: |
|
11439 case XPATH_XSLT_TREE: |
|
11440 return res->nodesetval |
|
11441 ? |
|
11442 res->nodesetval->nodeNr != 0 |
|
11443 : 0; |
|
11444 case XPATH_STRING: |
|
11445 return |
|
11446 res->stringval && |
|
11447 xmlStrlen(res->stringval) != 0; |
|
11448 default: |
|
11449 STRANGE |
|
11450 } |
|
11451 return(0); |
|
11452 } |
|
11453 |
|
11454 /** |
|
11455 * xmlXPathEvaluatePredicateResult: |
|
11456 * @ctxt: the XPath Parser context |
|
11457 * @res: the Predicate Expression evaluation result |
|
11458 * |
|
11459 * Evaluate a predicate result for the current node. |
|
11460 * A PredicateExpr is evaluated by evaluating the Expr and converting |
|
11461 * the result to a boolean. If the result is a number, the result will |
|
11462 * be converted to true if the number is equal to the position of the |
|
11463 * context node in the context node list (as returned by the position |
|
11464 * function) and will be converted to false otherwise; if the result |
|
11465 * is not a number, then the result will be converted as if by a call |
|
11466 * to the boolean function. |
|
11467 * |
|
11468 * Returns 1 if predicate is true, 0 otherwise |
|
11469 */ |
|
11470 int |
|
11471 xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, |
|
11472 xmlXPathObjectPtr res) { |
|
11473 if (res == NULL) return(0); |
|
11474 switch (res->type) { |
|
11475 case XPATH_BOOLEAN: |
|
11476 return(res->boolval); |
|
11477 case XPATH_NUMBER: |
|
11478 #if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200)) |
|
11479 return((res->floatval == ctxt->context->proximityPosition) && |
|
11480 (!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/ |
|
11481 #else |
|
11482 return(res->floatval == ctxt->context->proximityPosition); |
|
11483 #endif |
|
11484 case XPATH_NODESET: |
|
11485 case XPATH_XSLT_TREE: |
|
11486 if (res->nodesetval == NULL) |
|
11487 return(0); |
|
11488 return(res->nodesetval->nodeNr != 0); |
|
11489 case XPATH_STRING: |
|
11490 return((res->stringval != NULL) && |
|
11491 (xmlStrlen(res->stringval) != 0)); |
|
11492 #ifdef LIBXML_XPTR_ENABLED |
|
11493 case XPATH_LOCATIONSET:{ |
|
11494 xmlLocationSetPtr ptr = res->user; |
|
11495 if (ptr == NULL) |
|
11496 return(0); |
|
11497 return (ptr->locNr != 0); |
|
11498 } |
|
11499 #endif |
|
11500 default: |
|
11501 STRANGE |
|
11502 } |
|
11503 return(0); |
|
11504 } |
|
11505 |
|
11506 /** |
|
11507 * xmlXPathCtxtCompile: |
|
11508 * @ctxt: an XPath context |
|
11509 * @str: the XPath expression |
|
11510 * |
|
11511 * Compile an XPath expression |
|
11512 * |
|
11513 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. |
|
11514 * the caller has to free the object. |
|
11515 */ |
|
11516 xmlXPathCompExprPtr |
|
11517 |
|
11518 xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { |
|
11519 xmlXPathParserContextPtr pctxt; |
|
11520 xmlXPathCompExprPtr comp; |
|
11521 |
|
11522 xmlXPathInit(); |
|
11523 |
|
11524 pctxt = xmlXPathNewParserContext(str, ctxt); |
|
11525 if(OOM_FLAG) return(NULL); |
|
11526 xmlXPathCompileExpr(pctxt); |
|
11527 if(OOM_FLAG) |
|
11528 { |
|
11529 xmlXPathFreeParserContext(pctxt); |
|
11530 return(NULL); |
|
11531 } |
|
11532 if( pctxt->error != XPATH_EXPRESSION_OK ) |
|
11533 { |
|
11534 xmlXPathFreeParserContext(pctxt); |
|
11535 return (0); |
|
11536 } |
|
11537 |
|
11538 if (*pctxt->cur != 0) { |
|
11539 /* |
|
11540 * aleksey: in some cases this line prints *second* error message |
|
11541 * (see bug #78858) and probably this should be fixed. |
|
11542 * However, we are not sure that all error messages are printed |
|
11543 * out in other places. It's not critical so we leave it as-is for now |
|
11544 */ |
|
11545 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); |
|
11546 comp = NULL; |
|
11547 } else { |
|
11548 comp = pctxt->comp; |
|
11549 pctxt->comp = NULL; |
|
11550 } |
|
11551 xmlXPathFreeParserContext(pctxt); |
|
11552 if (comp != NULL) { |
|
11553 comp->expr = xmlStrdup(str); |
|
11554 #ifdef DEBUG_EVAL_COUNTS |
|
11555 comp->string = xmlStrdup(str); |
|
11556 comp->nb = 0; |
|
11557 #endif |
|
11558 } |
|
11559 return(comp); |
|
11560 } |
|
11561 |
|
11562 /** |
|
11563 * xmlXPathCompile: |
|
11564 * @str: the XPath expression |
|
11565 * |
|
11566 * Compile an XPath expression |
|
11567 * |
|
11568 * Returns the xmlXPathCompExprPtr resulting from the compilation or NULL. |
|
11569 * the caller has to free the object. |
|
11570 */ |
|
11571 xmlXPathCompExprPtr |
|
11572 xmlXPathCompile(const xmlChar *str) { |
|
11573 return(xmlXPathCtxtCompile(NULL, str)); |
|
11574 } |
|
11575 |
|
11576 /** |
|
11577 * xmlXPathCompiledEval: |
|
11578 * @comp: the compiled XPath expression |
|
11579 * @ctx: the XPath context |
|
11580 * |
|
11581 * Evaluate the Precompiled XPath expression in the given context. |
|
11582 * |
|
11583 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
11584 * the caller has to free the object. |
|
11585 */ |
|
11586 xmlXPathObjectPtr |
|
11587 xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) { |
|
11588 xmlXPathParserContextPtr ctxt; |
|
11589 xmlXPathObjectPtr res, tmp, init = NULL; |
|
11590 int stack = 0; |
|
11591 #ifndef LIBXML_THREAD_ENABLED |
|
11592 //FIXIT |
|
11593 //static |
|
11594 int reentance = 0; |
|
11595 #endif |
|
11596 |
|
11597 if ((comp == NULL) || (ctx == NULL)) |
|
11598 return(NULL); |
|
11599 xmlXPathInit(); |
|
11600 // TODO: CHECK_CONTEXT -- Replace macro with a function (too big for reuse more then once) |
|
11601 CHECK_CONTEXT(ctx) |
|
11602 |
|
11603 #ifndef LIBXML_THREAD_ENABLED |
|
11604 reentance++; |
|
11605 if (reentance > 1) |
|
11606 xmlXPathDisableOptimizer = 1; |
|
11607 #endif |
|
11608 |
|
11609 #ifdef DEBUG_EVAL_COUNTS |
|
11610 comp->nb++; |
|
11611 if ((comp->string != NULL) && (comp->nb > 100)) { |
|
11612 fprintf(stderr, "100 x %s\n", comp->string); |
|
11613 comp->nb = 0; |
|
11614 } |
|
11615 #endif |
|
11616 ctxt = xmlXPathCompParserContext(comp, ctx); |
|
11617 xmlXPathRunEval(ctxt); //might set OOM flag |
|
11618 if(OOM_FLAG) |
|
11619 { |
|
11620 if(ctxt) |
|
11621 { |
|
11622 ctxt->comp = NULL; |
|
11623 xmlXPathFreeParserContext(ctxt); |
|
11624 } |
|
11625 return(NULL); |
|
11626 } |
|
11627 |
|
11628 if (ctxt->value == NULL) { |
|
11629 xmlGenericError(xmlGenericErrorContext, |
|
11630 EMBED_ERRTXT("xmlXPathCompiledEval: evaluation failed\n")); |
|
11631 res = NULL; |
|
11632 } else { |
|
11633 res = valuePop(ctxt); |
|
11634 } |
|
11635 |
|
11636 |
|
11637 do { |
|
11638 tmp = valuePop(ctxt); |
|
11639 if (tmp != NULL) { |
|
11640 if (tmp != init) |
|
11641 stack++; |
|
11642 xmlXPathFreeObject(tmp); |
|
11643 } |
|
11644 } while (tmp != NULL); |
|
11645 if ((stack != 0) && (res != NULL)) { |
|
11646 xmlGenericError(xmlGenericErrorContext, |
|
11647 EMBED_ERRTXT("xmlXPathCompiledEval: %d object left on the stack\n"), |
|
11648 stack); |
|
11649 } |
|
11650 if (ctxt->error != XPATH_EXPRESSION_OK) { |
|
11651 xmlXPathFreeObject(res); |
|
11652 res = NULL; |
|
11653 } |
|
11654 |
|
11655 |
|
11656 ctxt->comp = NULL; |
|
11657 xmlXPathFreeParserContext(ctxt); |
|
11658 #ifndef LIBXML_THREAD_ENABLED |
|
11659 reentance--; |
|
11660 #endif |
|
11661 return(res); |
|
11662 } |
|
11663 |
|
11664 /** |
|
11665 * xmlXPathEvalExpr: |
|
11666 * @ctxt: the XPath Parser context |
|
11667 * |
|
11668 * Parse and evaluate an XPath expression in the given context, |
|
11669 * then push the result on the context stack |
|
11670 * |
|
11671 * OOM: possible --> check OOM flag |
|
11672 */ |
|
11673 void |
|
11674 xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { |
|
11675 xmlXPathCompileExpr(ctxt); |
|
11676 CHECK_ERROR; |
|
11677 xmlXPathRunEval(ctxt); |
|
11678 } |
|
11679 |
|
11680 /** |
|
11681 * xmlXPathEval: |
|
11682 * @str: the XPath expression |
|
11683 * @ctx: the XPath context |
|
11684 * |
|
11685 * Evaluate the XPath Location Path in the given context. |
|
11686 * |
|
11687 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
11688 * the caller has to free the object. |
|
11689 */ |
|
11690 xmlXPathObjectPtr |
|
11691 xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) { |
|
11692 xmlXPathParserContextPtr ctxt; |
|
11693 xmlXPathObjectPtr res, tmp, init = NULL; |
|
11694 int stack = 0; |
|
11695 |
|
11696 xmlXPathInit(); |
|
11697 |
|
11698 CHECK_CONTEXT(ctx) |
|
11699 |
|
11700 ctxt = xmlXPathNewParserContext(str, ctx); |
|
11701 xmlXPathEvalExpr(ctxt); |
|
11702 |
|
11703 if (ctxt->value == NULL) { |
|
11704 xmlGenericError(xmlGenericErrorContext, |
|
11705 EMBED_ERRTXT("xmlXPathEval: evaluation failed\n")); |
|
11706 res = NULL; |
|
11707 } else if (*ctxt->cur != 0) { |
|
11708 xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); |
|
11709 res = NULL; |
|
11710 } else { |
|
11711 res = valuePop(ctxt); |
|
11712 } |
|
11713 |
|
11714 do { |
|
11715 tmp = valuePop(ctxt); |
|
11716 if (tmp != NULL) { |
|
11717 if (tmp != init) |
|
11718 stack++; |
|
11719 xmlXPathFreeObject(tmp); |
|
11720 } |
|
11721 } while (tmp != NULL); |
|
11722 if ((stack != 0) && (res != NULL)) { |
|
11723 xmlGenericError(xmlGenericErrorContext, |
|
11724 EMBED_ERRTXT("xmlXPathEval: %d object left on the stack\n"), |
|
11725 stack); |
|
11726 } |
|
11727 if (ctxt->error != XPATH_EXPRESSION_OK) { |
|
11728 xmlXPathFreeObject(res); |
|
11729 res = NULL; |
|
11730 } |
|
11731 |
|
11732 xmlXPathFreeParserContext(ctxt); |
|
11733 return(res); |
|
11734 } |
|
11735 |
|
11736 /** |
|
11737 * xmlXPathEvalExpression: |
|
11738 * @str: the XPath expression |
|
11739 * @ctxt: the XPath context |
|
11740 * |
|
11741 * Evaluate the XPath expression in the given context. |
|
11742 * |
|
11743 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
11744 * the caller has to free the object. |
|
11745 */ |
|
11746 xmlXPathObjectPtr |
|
11747 xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) { |
|
11748 xmlXPathParserContextPtr pctxt; |
|
11749 xmlXPathObjectPtr res, tmp; |
|
11750 int stack = 0; |
|
11751 |
|
11752 xmlXPathInit(); |
|
11753 |
|
11754 CHECK_CONTEXT(ctxt) |
|
11755 |
|
11756 pctxt = xmlXPathNewParserContext(str, ctxt); |
|
11757 xmlXPathEvalExpr(pctxt); |
|
11758 |
|
11759 if (*pctxt->cur != 0) { |
|
11760 xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR); |
|
11761 res = NULL; |
|
11762 } else { |
|
11763 res = valuePop(pctxt); |
|
11764 } |
|
11765 |
|
11766 do { |
|
11767 tmp = valuePop(pctxt); |
|
11768 if (tmp != NULL) { |
|
11769 xmlXPathFreeObject(tmp); |
|
11770 stack++; |
|
11771 } |
|
11772 } while (tmp != NULL); |
|
11773 |
|
11774 if ((stack != 0) && (res != NULL)) { |
|
11775 xmlGenericError(xmlGenericErrorContext, |
|
11776 EMBED_ERRTXT("xmlXPathEvalExpression: %d object left on the stack\n"), |
|
11777 stack); |
|
11778 } |
|
11779 xmlXPathFreeParserContext(pctxt); |
|
11780 return(res); |
|
11781 } |
|
11782 |
|
11783 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
11784 |
|
11785 /* First initialises static nodeset variable from deplist (argument). |
|
11786 * Calls xmlXPathCompiledEval. If the result is a nodeset it is copied |
|
11787 * to the deplist. Otherwise modifications in xmlXPathCompOpEval have |
|
11788 * already filled deplist. |
|
11789 */ |
|
11790 xmlXPathObjectPtr |
|
11791 xmlXPathCompiledEvalWithDependencies(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx, xmlNodeSetPtr deplist) |
|
11792 { |
|
11793 xmlXPathObjectPtr res = NULL; |
|
11794 ctx->dependencyList = deplist; |
|
11795 res = xmlXPathCompiledEval(comp, ctx); |
|
11796 |
|
11797 /* if the result is nodeset there are no other dependencies */ |
|
11798 if (res != NULL && res->type == XPATH_NODESET) { |
|
11799 if (!xmlXPathNodeSetIsEmpty(deplist)) { |
|
11800 ;/* this should not happen */ |
|
11801 } |
|
11802 xmlXPathNodeSetMerge(deplist, res->nodesetval); |
|
11803 // KO: moved out of IF block // xmlXPathNodeSetSort(deplist); |
|
11804 } |
|
11805 // KO: moved out of IF block //else xmlXPathNodeSetSort(deplist); |
|
11806 |
|
11807 xmlXPathNodeSetSort(deplist); /* dep has been filled with nodes */ |
|
11808 |
|
11809 ctx->dependencyList = NULL; |
|
11810 |
|
11811 return res; |
|
11812 } |
|
11813 |
|
11814 /* First pops of nargs values from stack (ctxt) and stores them in temporary |
|
11815 * array. Adds all values of type nodeset to deplist and pushes everything back |
|
11816 * on the stack. |
|
11817 */ |
|
11818 void addNodeSetsFromStackToDependencyList(xmlXPathParserContextPtr ctxt, int nargs) |
|
11819 { |
|
11820 xmlNodeSetPtr deplist = ctxt->context->dependencyList; |
|
11821 #define MAXTMPSTACK 10 |
|
11822 xmlXPathObjectPtr tmpStack[MAXTMPSTACK] = {0,0,0,0,0,0,0,0,0,0}; |
|
11823 int i=0; |
|
11824 |
|
11825 if(!deplist) return; |
|
11826 |
|
11827 // TODO: improve code |
|
11828 // KO: If to decide number of popped elements first (min(nargs,MAXTMPSTACK)) |
|
11829 // then no need to prefill tmpStack with zeros and make checks in the second loop |
|
11830 /* copy all arguments of type nodeset to deplist */ |
|
11831 // KO: it is better not to pop and push arguments back, but rather PEEK the values from |
|
11832 // the internal stack: PEEK(i) = ctxt->valueTab[ctxt->valueNr - i - 1]; |
|
11833 // where 'i=1' is the index of the top-most element on the stack |
|
11834 for (i=0; (i < nargs) && (i < MAXTMPSTACK); i++) { |
|
11835 tmpStack[i] = valuePop(ctxt); |
|
11836 } |
|
11837 |
|
11838 for (i= MAXTMPSTACK-1; i>=0 ; i--) { |
|
11839 if (tmpStack[i] != NULL) { |
|
11840 if (tmpStack[i]->type == XPATH_NODESET) |
|
11841 xmlXPathNodeSetMerge(deplist, tmpStack[i]->nodesetval); |
|
11842 valuePush(ctxt, tmpStack[i]); |
|
11843 } |
|
11844 } |
|
11845 |
|
11846 #define p__enable_this_test |
|
11847 #ifdef __enable_this_test |
|
11848 // TODO: OPTIMIZE: try how this new version of addNodeSetsFromStackToDependencyList works |
|
11849 xmlXPathObjectPtr* stack = ctxt->valueTab; |
|
11850 int index = ctxt->valueNr - 1; |
|
11851 for (i= 0; i < nargs ; i++) { |
|
11852 xmlXPathObjectPtr obj = stack[index--]; |
|
11853 if (obj->type == XPATH_NODESET) |
|
11854 xmlXPathNodeSetMerge(deplist, obj->nodesetval); |
|
11855 } |
|
11856 #endif |
|
11857 |
|
11858 #undef __enable_this_test |
|
11859 } |
|
11860 |
|
11861 /* |
|
11862 * xmlXFormsInstanceFunction: |
|
11863 * @ctxt: the XPath Parser context |
|
11864 * @nargs: the number of arguments |
|
11865 * |
|
11866 * Implement the instance() XForms function |
|
11867 * nodeset instance(string) |
|
11868 */ |
|
11869 void |
|
11870 xmlXFormsInstanceFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
11871 xmlXPathObjectPtr cur = NULL; |
|
11872 xmlXPathObjectPtr ret = NULL; |
|
11873 xmlDocPtr doc = NULL; |
|
11874 |
|
11875 CHECK_ARITY(1); |
|
11876 cur = valuePop(ctxt); |
|
11877 if (cur == NULL) |
|
11878 XP_ERROR(XPATH_INVALID_OPERAND); |
|
11879 |
|
11880 /* convert argument to string */ |
|
11881 cur = xmlXPathConvertString(cur); |
|
11882 |
|
11883 /* TODO: get document from store */ |
|
11884 if (ctxt->context->instanceDocs) |
|
11885 { |
|
11886 doc = (xmlDocPtr)xmlHashLookup(ctxt->context->instanceDocs, cur->stringval); |
|
11887 } |
|
11888 |
|
11889 /* create a node set and change context, which will be reset by |
|
11890 the operations: AND, OR, EQUAL, CMP, MULT, UNION, ARG */ |
|
11891 if (doc) |
|
11892 { |
|
11893 ctxt->context->doc = doc; |
|
11894 ctxt->context->node = xmlDocGetRootElement(doc); |
|
11895 ret = xmlXPathNewNodeSet(ctxt->context->node); |
|
11896 } |
|
11897 else |
|
11898 { |
|
11899 ret = xmlXPathNewNodeSet(NULL); |
|
11900 } |
|
11901 |
|
11902 /* push the instance on the stack */ |
|
11903 valuePush(ctxt, ret); |
|
11904 |
|
11905 /* free cur*/ |
|
11906 xmlXPathFreeObject(cur); |
|
11907 } |
|
11908 |
|
11909 |
|
11910 #endif // XMLENGINE_XFROMS_EXTENSIONS |
|
11911 |
|
11912 /************************************************************************ |
|
11913 * * |
|
11914 * Extra functions not pertaining to the XPath spec * |
|
11915 * * |
|
11916 ************************************************************************/ |
|
11917 /** |
|
11918 * xmlXPathEscapeUriFunction: |
|
11919 * @ctxt: the XPath Parser context |
|
11920 * @nargs: the number of arguments |
|
11921 * |
|
11922 * Implement the escape-uri() XPath function |
|
11923 * string escape-uri(string $str, bool $escape-reserved) |
|
11924 * |
|
11925 * This function applies the URI escaping rules defined in section 2 of [RFC |
|
11926 * 2396] to the string supplied as $uri-part, which typically represents all |
|
11927 * or part of a URI. The effect of the function is to replace any special |
|
11928 * character in the string by an escape sequence of the form %xx%yy..., |
|
11929 * where xxyy... is the hexadecimal representation of the octets used to |
|
11930 * represent the character in UTF-8. |
|
11931 * |
|
11932 * The set of characters that are escaped depends on the setting of the |
|
11933 * boolean argument $escape-reserved. |
|
11934 * |
|
11935 * If $escape-reserved is true, all characters are escaped other than lower |
|
11936 * case letters a-z, upper case letters A-Z, digits 0-9, and the characters |
|
11937 * referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!" |
|
11938 * | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only |
|
11939 * if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and |
|
11940 * A-F). |
|
11941 * |
|
11942 * If $escape-reserved is false, the behavior differs in that characters |
|
11943 * referred to in [RFC 2396] as reserved characters are not escaped. These |
|
11944 * characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",". |
|
11945 * |
|
11946 * [RFC 2396] does not define whether escaped URIs should use lower case or |
|
11947 * upper case for hexadecimal digits. To ensure that escaped URIs can be |
|
11948 * compared using string comparison functions, this function must always use |
|
11949 * the upper-case letters A-F. |
|
11950 * |
|
11951 * Generally, $escape-reserved should be set to true when escaping a string |
|
11952 * that is to form a single part of a URI, and to false when escaping an |
|
11953 * entire URI or URI reference. |
|
11954 * |
|
11955 * In the case of non-ascii characters, the string is encoded according to |
|
11956 * utf-8 and then converted according to RFC 2396. |
|
11957 * |
|
11958 * Examples |
|
11959 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true()) |
|
11960 * returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean" |
|
11961 * xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false()) |
|
11962 * returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean" |
|
11963 * |
|
11964 */ |
|
11965 static void |
|
11966 xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
11967 xmlXPathObjectPtr str; |
|
11968 int escape_reserved; |
|
11969 xmlBufferPtr target; |
|
11970 xmlChar *cptr; |
|
11971 xmlChar escape[4]; |
|
11972 |
|
11973 CHECK_ARITY(2); |
|
11974 |
|
11975 escape_reserved = xmlXPathPopBoolean(ctxt); |
|
11976 |
|
11977 CAST_TO_STRING; |
|
11978 str = valuePop(ctxt); |
|
11979 |
|
11980 target = xmlBufferCreate(); |
|
11981 |
|
11982 escape[0] = '%'; |
|
11983 escape[3] = 0; |
|
11984 |
|
11985 if (target) { |
|
11986 for (cptr = str->stringval; *cptr; cptr++) { |
|
11987 if ((*cptr >= 'A' && *cptr <= 'Z') || |
|
11988 (*cptr >= 'a' && *cptr <= 'z') || |
|
11989 (*cptr >= '0' && *cptr <= '9') || |
|
11990 *cptr == '-' || *cptr == '_' || *cptr == '.' || |
|
11991 *cptr == '!' || *cptr == '~' || *cptr == '*' || |
|
11992 *cptr == '\''|| *cptr == '(' || *cptr == ')' || |
|
11993 (*cptr == '%' && |
|
11994 ((cptr[1] >= 'A' && cptr[1] <= 'F') || |
|
11995 (cptr[1] >= 'a' && cptr[1] <= 'f') || |
|
11996 (cptr[1] >= '0' && cptr[1] <= '9')) && |
|
11997 ((cptr[2] >= 'A' && cptr[2] <= 'F') || |
|
11998 (cptr[2] >= 'a' && cptr[2] <= 'f') || |
|
11999 (cptr[2] >= '0' && cptr[2] <= '9'))) || |
|
12000 (!escape_reserved && |
|
12001 (*cptr == ';' || *cptr == '/' || *cptr == '?' || |
|
12002 *cptr == ':' || *cptr == '@' || *cptr == '&' || |
|
12003 *cptr == '=' || *cptr == '+' || *cptr == '$' || |
|
12004 *cptr == ','))) |
|
12005 { |
|
12006 xmlBufferAdd(target, cptr, 1); |
|
12007 } else { |
|
12008 if ((*cptr >> 4) < 10) |
|
12009 escape[1] = '0' + (*cptr >> 4); |
|
12010 else |
|
12011 escape[1] = 'A' - 10 + (*cptr >> 4); |
|
12012 if ((*cptr & 0xF) < 10) |
|
12013 escape[2] = '0' + (*cptr & 0xF); |
|
12014 else |
|
12015 escape[2] = 'A' - 10 + (*cptr & 0xF); |
|
12016 |
|
12017 xmlBufferAdd(target, &escape[0], 3); |
|
12018 } |
|
12019 } |
|
12020 } |
|
12021 valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target))); |
|
12022 xmlBufferFree(target); |
|
12023 xmlXPathFreeObject(str); |
|
12024 } |
|
12025 |
|
12026 /** |
|
12027 * xmlXPathRegisterAllFunctions: |
|
12028 * @ctxt: the XPath context |
|
12029 * |
|
12030 * Registers all default XPath functions in this context |
|
12031 * |
|
12032 * Returns 0 in case of success, -1 in case of error |
|
12033 */ |
|
12034 int |
|
12035 xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt) |
|
12036 { // TODO: CHECK OOM after all these and cleanup funcHash if needed |
|
12037 // TODO: return error code OR setup global OOM flag |
|
12038 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"boolean", xmlXPathBooleanFunction) < 0) return(-1); |
|
12039 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"ceiling", xmlXPathCeilingFunction) < 0) return(-1); |
|
12040 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"count", xmlXPathCountFunction) < 0) return(-1); |
|
12041 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"concat", xmlXPathConcatFunction) < 0) return(-1); |
|
12042 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"contains",xmlXPathContainsFunction) < 0) return(-1); |
|
12043 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"id", xmlXPathIdFunction) < 0) return(-1); |
|
12044 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"false", xmlXPathFalseFunction) < 0) return(-1); |
|
12045 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"floor", xmlXPathFloorFunction) < 0) return(-1); |
|
12046 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"last", xmlXPathLastFunction) < 0) return(-1); |
|
12047 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"lang", xmlXPathLangFunction) < 0) return(-1); |
|
12048 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"local-name", xmlXPathLocalNameFunction) < 0) return(-1); |
|
12049 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"not", xmlXPathNotFunction) < 0) return(-1); |
|
12050 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"name", xmlXPathNameFunction) < 0) return(-1); |
|
12051 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"namespace-uri", xmlXPathNamespaceURIFunction) < 0) return(-1); |
|
12052 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"normalize-space", xmlXPathNormalizeFunction) < 0) return(-1); |
|
12053 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"number", xmlXPathNumberFunction) < 0) return(-1); |
|
12054 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"position", xmlXPathPositionFunction) < 0) return(-1); |
|
12055 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"round", xmlXPathRoundFunction) < 0) return(-1); |
|
12056 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string", xmlXPathStringFunction) < 0) return(-1); |
|
12057 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string-length", xmlXPathStringLengthFunction) < 0) return(-1); |
|
12058 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"starts-with", xmlXPathStartsWithFunction) < 0) return(-1); |
|
12059 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring", xmlXPathSubstringFunction) < 0) return(-1); |
|
12060 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-before", xmlXPathSubstringBeforeFunction) < 0) return(-1); |
|
12061 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-after", xmlXPathSubstringAfterFunction) < 0) return(-1); |
|
12062 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"sum", xmlXPathSumFunction) < 0) return(-1); |
|
12063 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"true", xmlXPathTrueFunction) < 0) return(-1); |
|
12064 if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"translate", xmlXPathTranslateFunction) < 0) return(-1); |
|
12065 |
|
12066 if( xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri", |
|
12067 (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions", xmlXPathEscapeUriFunction) < 0) return(-1); |
|
12068 |
|
12069 // TODO: define special function that will register instance() function for XForms support |
|
12070 #ifdef XMLENGINE_XFORMS_EXTENSIONS |
|
12071 if( xmlXPathRegisterFunc(ctxt, (const xmlChar *)"instance", xmlXFormsInstanceFunction) < 0) return(-1); |
|
12072 #endif |
|
12073 return(0); |
|
12074 } |
|
12075 |
|
12076 #endif /* LIBXML_XPATH_ENABLED */ |
|
12077 |