|
1 /* |
|
2 * libxml2_xpointer.c : Code to handle XML Pointer |
|
3 * |
|
4 * Base implementation was made accordingly to |
|
5 * W3C Candidate Recommendation 7 June 2000 |
|
6 * http://www.w3.org/TR/2000/CR-xptr-20000607 |
|
7 * |
|
8 * Added support for the element() scheme described in: |
|
9 * W3C Proposed Recommendation 13 November 2002 |
|
10 * http://www.w3.org/TR/2002/PR-xptr-element-20021113/ |
|
11 * |
|
12 * See Copyright for the status of this software. |
|
13 * |
|
14 * daniel@veillard.com |
|
15 * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
16 */ |
|
17 |
|
18 #define IN_LIBXML |
|
19 #include "xmlenglibxml.h" |
|
20 |
|
21 /* |
|
22 * |
|
23 * |
|
24 * |
|
25 * |
|
26 * |
|
27 */ |
|
28 |
|
29 #include <string.h> |
|
30 #include <stdapis/libxml2/libxml2_xpointer.h> |
|
31 #include <stdapis/libxml2/libxml2_xmlmemory.h> |
|
32 #include <stdapis/libxml2/libxml2_parserinternals.h> |
|
33 #include <stdapis/libxml2/libxml2_uri.h> |
|
34 #include <stdapis/libxml2/libxml2_xpath.h> |
|
35 #include <stdapis/libxml2/libxml2_xpathinternals.h> |
|
36 #include <stdapis/libxml2/libxml2_xmlerror.h> |
|
37 #include "libxml2_xmlerror2.h" |
|
38 #include <stdapis/libxml2/libxml2_globals.h> |
|
39 |
|
40 #ifdef LIBXML_XPTR_ENABLED |
|
41 |
|
42 /* Add support of the xmlns() xpointer scheme to initialize the namespaces */ |
|
43 #define XPTR_XMLNS_SCHEME |
|
44 |
|
45 /* #define DEBUG_RANGES */ |
|
46 #ifdef DEBUG_RANGES |
|
47 #ifdef LIBXML_DEBUG_ENABLED |
|
48 #include "libxml2_debugxml.h" |
|
49 #endif |
|
50 #endif |
|
51 |
|
52 #define TODO \ |
|
53 xmlGenericError(xmlGenericErrorContext, \ |
|
54 "Unimplemented block at %s:%d\n", \ |
|
55 __FILE__, __LINE__); |
|
56 |
|
57 #define STRANGE \ |
|
58 xmlGenericError(xmlGenericErrorContext, \ |
|
59 "Internal error at %s:%d\n", \ |
|
60 __FILE__, __LINE__); |
|
61 |
|
62 /************************************************************************ |
|
63 * * |
|
64 * Some factorized error routines * |
|
65 * * |
|
66 ************************************************************************/ |
|
67 |
|
68 /** |
|
69 * xmlXPtrErrMemory: |
|
70 * @param extra extra informations |
|
71 * |
|
72 * Handle a redefinition of attribute error |
|
73 */ |
|
74 static void |
|
75 xmlXPtrErrMemory(const char *extra) |
|
76 { |
|
77 __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_XPOINTER, |
|
78 XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, extra, |
|
79 NULL, NULL, 0, 0, |
|
80 "Memory allocation failed : %s\n", extra); |
|
81 } |
|
82 |
|
83 /** |
|
84 * xmlXPtrErr: |
|
85 * @param ctxt an XPTR evaluation context |
|
86 * @param extra extra informations |
|
87 * |
|
88 * Handle a redefinition of attribute error |
|
89 */ |
|
90 static void |
|
91 xmlXPtrErr(xmlXPathParserContextPtr ctxt, int error, |
|
92 const char * msg, const xmlChar *extra) |
|
93 { |
|
94 if (ctxt != NULL) |
|
95 ctxt->error = error; |
|
96 if ((ctxt == NULL) || (ctxt->context == NULL)) { |
|
97 __xmlRaiseError(NULL, NULL, NULL, |
|
98 NULL, NULL, XML_FROM_XPOINTER, error, |
|
99 XML_ERR_ERROR, NULL, 0, |
|
100 (const char *) extra, NULL, NULL, 0, 0, |
|
101 msg, extra); |
|
102 return; |
|
103 } |
|
104 ctxt->context->lastError.domain = XML_FROM_XPOINTER; |
|
105 ctxt->context->lastError.code = error; |
|
106 ctxt->context->lastError.level = XML_ERR_ERROR; |
|
107 ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base); |
|
108 ctxt->context->lastError.int1 = ctxt->cur - ctxt->base; |
|
109 ctxt->context->lastError.node = ctxt->context->debugNode; |
|
110 if (ctxt->context->error != NULL) { |
|
111 ctxt->context->error(ctxt->context->userData, |
|
112 &ctxt->context->lastError); |
|
113 } else { |
|
114 __xmlRaiseError(NULL, NULL, NULL, |
|
115 NULL, ctxt->context->debugNode, XML_FROM_XPOINTER, |
|
116 error, XML_ERR_ERROR, NULL, 0, |
|
117 (const char *) extra, (const char *) ctxt->base, NULL, |
|
118 ctxt->cur - ctxt->base, 0, |
|
119 msg, extra); |
|
120 } |
|
121 } |
|
122 |
|
123 /************************************************************************ |
|
124 * * |
|
125 * A few helper functions for child sequences * |
|
126 * * |
|
127 ************************************************************************/ |
|
128 /* xmlXPtrAdvanceNode is a private function, but used by xinclude.c */ |
|
129 xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level); |
|
130 /** |
|
131 * xmlXPtrGetArity: |
|
132 * @param cur the node |
|
133 * |
|
134 * Returns the number of child for an element, -1 in case of error |
|
135 */ |
|
136 static int |
|
137 xmlXPtrGetArity(xmlNodePtr cur) { |
|
138 int i; |
|
139 if (cur == NULL) |
|
140 return(-1); |
|
141 cur = cur->children; |
|
142 for (i = 0;cur != NULL;cur = cur->next) { |
|
143 if ((cur->type == XML_ELEMENT_NODE) || |
|
144 (cur->type == XML_DOCUMENT_NODE) || |
|
145 (cur->type == XML_HTML_DOCUMENT_NODE)) { |
|
146 i++; |
|
147 } |
|
148 } |
|
149 return(i); |
|
150 } |
|
151 |
|
152 /** |
|
153 * xmlXPtrGetIndex: |
|
154 * @param cur the node |
|
155 * |
|
156 * Returns the index of the node in its parent children list, -1 |
|
157 * in case of error |
|
158 */ |
|
159 static int |
|
160 xmlXPtrGetIndex(xmlNodePtr cur) { |
|
161 int i; |
|
162 if (cur == NULL) |
|
163 return(-1); |
|
164 for (i = 1;cur != NULL;cur = cur->prev) { |
|
165 if ((cur->type == XML_ELEMENT_NODE) || |
|
166 (cur->type == XML_DOCUMENT_NODE) || |
|
167 (cur->type == XML_HTML_DOCUMENT_NODE)) { |
|
168 i++; |
|
169 } |
|
170 } |
|
171 return(i); |
|
172 } |
|
173 |
|
174 /** |
|
175 * xmlXPtrGetNthChild: |
|
176 * @param cur the node |
|
177 * @param no the child number |
|
178 * |
|
179 * Returns the no'th element child of cur or NULL |
|
180 */ |
|
181 static xmlNodePtr |
|
182 xmlXPtrGetNthChild(xmlNodePtr cur, int no) { |
|
183 int i; |
|
184 if (cur == NULL) |
|
185 return(cur); |
|
186 cur = cur->children; |
|
187 for (i = 0;i <= no;cur = cur->next) { |
|
188 if (cur == NULL) |
|
189 return(cur); |
|
190 if ((cur->type == XML_ELEMENT_NODE) || |
|
191 (cur->type == XML_DOCUMENT_NODE) || |
|
192 (cur->type == XML_HTML_DOCUMENT_NODE)) { |
|
193 i++; |
|
194 if (i == no) |
|
195 break; |
|
196 } |
|
197 } |
|
198 return(cur); |
|
199 } |
|
200 |
|
201 /************************************************************************ |
|
202 * * |
|
203 * Handling of XPointer specific types * |
|
204 * * |
|
205 ************************************************************************/ |
|
206 |
|
207 /** |
|
208 * xmlXPtrCmpPoints: |
|
209 * @param node1 the first node |
|
210 * @param index1 the first index |
|
211 * @param node2 the second node |
|
212 * @param index2 the second index |
|
213 * |
|
214 * Compare two points w.r.t document order |
|
215 * |
|
216 * Returns -2 in case of error 1 if first point < second point, 0 if |
|
217 * that's the same point, -1 otherwise |
|
218 */ |
|
219 static int |
|
220 xmlXPtrCmpPoints(xmlNodePtr node1, int index1, xmlNodePtr node2, int index2) { |
|
221 if ((node1 == NULL) || (node2 == NULL)) |
|
222 return(-2); |
|
223 /* |
|
224 * a couple of optimizations which will avoid computations in most cases |
|
225 */ |
|
226 if (node1 == node2) { |
|
227 if (index1 < index2) |
|
228 return(1); |
|
229 if (index1 > index2) |
|
230 return(-1); |
|
231 return(0); |
|
232 } |
|
233 return(xmlXPathCmpNodes(node1, node2)); |
|
234 } |
|
235 |
|
236 /** |
|
237 * xmlXPtrNewPoint: |
|
238 * @param node the xmlNodePtr |
|
239 * @param indx the indx within the node |
|
240 * |
|
241 * Create a new xmlXPathObjectPtr of type point |
|
242 * |
|
243 * Returns the newly created object. |
|
244 */ |
|
245 static xmlXPathObjectPtr |
|
246 xmlXPtrNewPoint(xmlNodePtr node, int indx) { |
|
247 xmlXPathObjectPtr ret; |
|
248 |
|
249 if (node == NULL) |
|
250 return(NULL); |
|
251 if (indx < 0) |
|
252 return(NULL); |
|
253 |
|
254 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
255 if (ret == NULL) { |
|
256 xmlXPtrErrMemory("allocating point"); |
|
257 return(NULL); |
|
258 } |
|
259 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
260 ret->type = XPATH_POINT; |
|
261 ret->user = (void *) node; |
|
262 ret->index = indx; |
|
263 return(ret); |
|
264 } |
|
265 |
|
266 /** |
|
267 * xmlXPtrRangeCheckOrder: |
|
268 * @param range an object range |
|
269 * |
|
270 * Make sure the points in the range are in the right order |
|
271 */ |
|
272 static void |
|
273 xmlXPtrRangeCheckOrder(xmlXPathObjectPtr range) { |
|
274 int tmp; |
|
275 xmlNodePtr tmp2; |
|
276 if (range == NULL) |
|
277 return; |
|
278 if (range->type != XPATH_RANGE) |
|
279 return; |
|
280 if (range->user2 == NULL) |
|
281 return; |
|
282 tmp = xmlXPtrCmpPoints(range->user, range->index, |
|
283 range->user2, range->index2); |
|
284 if (tmp == -1) { |
|
285 tmp2 = range->user; |
|
286 range->user = range->user2; |
|
287 range->user2 = tmp2; |
|
288 tmp = range->index; |
|
289 range->index = range->index2; |
|
290 range->index2 = tmp; |
|
291 } |
|
292 } |
|
293 |
|
294 /** |
|
295 * xmlXPtrRangesEqual: |
|
296 * @param range1 the first range |
|
297 * @param range2 the second range |
|
298 * |
|
299 * Compare two ranges |
|
300 * |
|
301 * Returns 1 if equal, 0 otherwise |
|
302 */ |
|
303 static int |
|
304 xmlXPtrRangesEqual(xmlXPathObjectPtr range1, xmlXPathObjectPtr range2) { |
|
305 if (range1 == range2) |
|
306 return(1); |
|
307 if ((range1 == NULL) || (range2 == NULL)) |
|
308 return(0); |
|
309 if (range1->type != range2->type) |
|
310 return(0); |
|
311 if (range1->type != XPATH_RANGE) |
|
312 return(0); |
|
313 if (range1->user != range2->user) |
|
314 return(0); |
|
315 if (range1->index != range2->index) |
|
316 return(0); |
|
317 if (range1->user2 != range2->user2) |
|
318 return(0); |
|
319 if (range1->index2 != range2->index2) |
|
320 return(0); |
|
321 return(1); |
|
322 } |
|
323 |
|
324 /** |
|
325 * xmlXPtrNewRange: |
|
326 * @param start the starting node |
|
327 * @param startindex the start index |
|
328 * @param end the ending point |
|
329 * @param endindex the ending index |
|
330 * |
|
331 * Create a new xmlXPathObjectPtr of type range |
|
332 * |
|
333 * Returns the newly created object. |
|
334 */ |
|
335 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
336 xmlXPtrNewRange(xmlNodePtr start, int startindex, |
|
337 xmlNodePtr end, int endindex) { |
|
338 xmlXPathObjectPtr ret; |
|
339 |
|
340 if (start == NULL) |
|
341 return(NULL); |
|
342 if (end == NULL) |
|
343 return(NULL); |
|
344 if (startindex < 0) |
|
345 return(NULL); |
|
346 if (endindex < 0) |
|
347 return(NULL); |
|
348 |
|
349 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
350 if (ret == NULL) { |
|
351 xmlXPtrErrMemory("allocating range"); |
|
352 return(NULL); |
|
353 } |
|
354 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
355 ret->type = XPATH_RANGE; |
|
356 ret->user = start; |
|
357 ret->index = startindex; |
|
358 ret->user2 = end; |
|
359 ret->index2 = endindex; |
|
360 xmlXPtrRangeCheckOrder(ret); |
|
361 return(ret); |
|
362 } |
|
363 |
|
364 /** |
|
365 * xmlXPtrNewRangePoints: |
|
366 * @param start the starting point |
|
367 * @param end the ending point |
|
368 * |
|
369 * Create a new xmlXPathObjectPtr of type range using 2 Points |
|
370 * |
|
371 * Returns the newly created object. |
|
372 */ |
|
373 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
374 xmlXPtrNewRangePoints(xmlXPathObjectPtr start, xmlXPathObjectPtr end) { |
|
375 xmlXPathObjectPtr ret; |
|
376 |
|
377 if (start == NULL) |
|
378 return(NULL); |
|
379 if (end == NULL) |
|
380 return(NULL); |
|
381 if (start->type != XPATH_POINT) |
|
382 return(NULL); |
|
383 if (end->type != XPATH_POINT) |
|
384 return(NULL); |
|
385 |
|
386 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
387 if (ret == NULL) { |
|
388 xmlXPtrErrMemory("allocating range"); |
|
389 return(NULL); |
|
390 } |
|
391 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
392 ret->type = XPATH_RANGE; |
|
393 ret->user = start->user; |
|
394 ret->index = start->index; |
|
395 ret->user2 = end->user; |
|
396 ret->index2 = end->index; |
|
397 xmlXPtrRangeCheckOrder(ret); |
|
398 return(ret); |
|
399 } |
|
400 |
|
401 /** |
|
402 * xmlXPtrNewRangePointNode: |
|
403 * @param start the starting point |
|
404 * @param end the ending node |
|
405 * |
|
406 * Create a new xmlXPathObjectPtr of type range from a point to a node |
|
407 * |
|
408 * Returns the newly created object. |
|
409 */ |
|
410 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
411 xmlXPtrNewRangePointNode(xmlXPathObjectPtr start, xmlNodePtr end) { |
|
412 xmlXPathObjectPtr ret; |
|
413 |
|
414 if (start == NULL) |
|
415 return(NULL); |
|
416 if (end == NULL) |
|
417 return(NULL); |
|
418 if (start->type != XPATH_POINT) |
|
419 return(NULL); |
|
420 |
|
421 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
422 if (ret == NULL) { |
|
423 xmlXPtrErrMemory("allocating range"); |
|
424 return(NULL); |
|
425 } |
|
426 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
427 ret->type = XPATH_RANGE; |
|
428 ret->user = start->user; |
|
429 ret->index = start->index; |
|
430 ret->user2 = end; |
|
431 ret->index2 = -1; |
|
432 xmlXPtrRangeCheckOrder(ret); |
|
433 return(ret); |
|
434 } |
|
435 |
|
436 /** |
|
437 * xmlXPtrNewRangeNodePoint: |
|
438 * @param start the starting node |
|
439 * @param end the ending point |
|
440 * |
|
441 * Create a new xmlXPathObjectPtr of type range from a node to a point |
|
442 * |
|
443 * Returns the newly created object. |
|
444 */ |
|
445 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
446 xmlXPtrNewRangeNodePoint(xmlNodePtr start, xmlXPathObjectPtr end) { |
|
447 xmlXPathObjectPtr ret; |
|
448 |
|
449 if (start == NULL) |
|
450 return(NULL); |
|
451 if (end == NULL) |
|
452 return(NULL); |
|
453 if (start->type != XPATH_POINT) |
|
454 return(NULL); |
|
455 if (end->type != XPATH_POINT) |
|
456 return(NULL); |
|
457 |
|
458 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
459 if (ret == NULL) { |
|
460 xmlXPtrErrMemory("allocating range"); |
|
461 return(NULL); |
|
462 } |
|
463 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
464 ret->type = XPATH_RANGE; |
|
465 ret->user = start; |
|
466 ret->index = -1; |
|
467 ret->user2 = end->user; |
|
468 ret->index2 = end->index; |
|
469 xmlXPtrRangeCheckOrder(ret); |
|
470 return(ret); |
|
471 } |
|
472 |
|
473 /** |
|
474 * xmlXPtrNewRangeNodes: |
|
475 * @param start the starting node |
|
476 * @param end the ending node |
|
477 * |
|
478 * Create a new xmlXPathObjectPtr of type range using 2 nodes |
|
479 * |
|
480 * Returns the newly created object. |
|
481 */ |
|
482 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
483 xmlXPtrNewRangeNodes(xmlNodePtr start, xmlNodePtr end) { |
|
484 xmlXPathObjectPtr ret; |
|
485 |
|
486 if (start == NULL) |
|
487 return(NULL); |
|
488 if (end == NULL) |
|
489 return(NULL); |
|
490 |
|
491 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
492 if (ret == NULL) { |
|
493 xmlXPtrErrMemory("allocating range"); |
|
494 return(NULL); |
|
495 } |
|
496 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
497 ret->type = XPATH_RANGE; |
|
498 ret->user = start; |
|
499 ret->index = -1; |
|
500 ret->user2 = end; |
|
501 ret->index2 = -1; |
|
502 xmlXPtrRangeCheckOrder(ret); |
|
503 return(ret); |
|
504 } |
|
505 |
|
506 /** |
|
507 * xmlXPtrNewCollapsedRange: |
|
508 * @param start the starting and ending node |
|
509 * |
|
510 * Create a new xmlXPathObjectPtr of type range using a single nodes |
|
511 * |
|
512 * Returns the newly created object. |
|
513 */ |
|
514 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
515 xmlXPtrNewCollapsedRange(xmlNodePtr start) |
|
516 { |
|
517 xmlXPathObjectPtr ret; |
|
518 |
|
519 if (start == NULL) |
|
520 return(NULL); |
|
521 |
|
522 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
523 if (!ret) { |
|
524 xmlXPtrErrMemory("allocating range"); |
|
525 return(NULL); |
|
526 } |
|
527 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
528 ret->type = XPATH_RANGE; |
|
529 ret->user = start; |
|
530 ret->index = -1; |
|
531 ret->user2 = NULL; |
|
532 ret->index2 = -1; |
|
533 return(ret); |
|
534 } |
|
535 |
|
536 /** |
|
537 * xmlXPtrNewRangeNodeObject: |
|
538 * @param start the starting node |
|
539 * @param end the ending object |
|
540 * |
|
541 * Create a new xmlXPathObjectPtr of type range from a not to an object |
|
542 * |
|
543 * Returns the newly created object. |
|
544 */ |
|
545 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
546 xmlXPtrNewRangeNodeObject(xmlNodePtr start, xmlXPathObjectPtr end) |
|
547 { |
|
548 xmlXPathObjectPtr ret; |
|
549 |
|
550 if (!start || !end) |
|
551 return(NULL); |
|
552 |
|
553 switch (end->type) { |
|
554 case XPATH_POINT: |
|
555 case XPATH_RANGE: |
|
556 break; |
|
557 case XPATH_NODESET: |
|
558 /* |
|
559 * Empty set ... |
|
560 */ |
|
561 if (end->nodesetval->nodeNr <= 0) |
|
562 return(NULL); |
|
563 break; |
|
564 default: |
|
565 TODO |
|
566 return(NULL); |
|
567 } |
|
568 |
|
569 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
570 if (ret == NULL) { |
|
571 xmlXPtrErrMemory("allocating range"); |
|
572 return(NULL); |
|
573 } |
|
574 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
575 ret->type = XPATH_RANGE; |
|
576 ret->user = start; |
|
577 ret->index = -1; |
|
578 switch (end->type) { |
|
579 case XPATH_POINT: |
|
580 ret->user2 = end->user; |
|
581 ret->index2 = end->index; |
|
582 break; |
|
583 case XPATH_RANGE: |
|
584 ret->user2 = end->user2; |
|
585 ret->index2 = end->index2; |
|
586 break; |
|
587 case XPATH_NODESET: { |
|
588 ret->user2 = end->nodesetval->nodeTab[end->nodesetval->nodeNr - 1]; |
|
589 ret->index2 = -1; |
|
590 break; |
|
591 } |
|
592 default: |
|
593 STRANGE |
|
594 return(NULL); |
|
595 } |
|
596 xmlXPtrRangeCheckOrder(ret); |
|
597 return(ret); |
|
598 } |
|
599 |
|
600 #define XML_RANGESET_DEFAULT 10 |
|
601 |
|
602 /** |
|
603 * xmlXPtrLocationSetCreate: |
|
604 * @param val an initial xmlXPathObjectPtr, or NULL |
|
605 * |
|
606 * Create a new xmlLocationSetPtr of type double and of value val |
|
607 * |
|
608 * Returns the newly created object. |
|
609 */ |
|
610 XMLPUBFUNEXPORT xmlLocationSetPtr |
|
611 xmlXPtrLocationSetCreate(xmlXPathObjectPtr val) { |
|
612 xmlLocationSetPtr ret; |
|
613 |
|
614 ret = (xmlLocationSetPtr) xmlMalloc(sizeof(xmlLocationSet)); |
|
615 if (ret == NULL) { |
|
616 xmlXPtrErrMemory("allocating locationset"); |
|
617 return(NULL); |
|
618 } |
|
619 memset(ret, 0 , (size_t) sizeof(xmlLocationSet)); |
|
620 if (val != NULL) { |
|
621 ret->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * |
|
622 sizeof(xmlXPathObjectPtr)); |
|
623 if (ret->locTab == NULL) { |
|
624 xmlXPtrErrMemory("allocating locationset"); |
|
625 xmlFree(ret); |
|
626 return(NULL); |
|
627 } |
|
628 memset(ret->locTab, 0 , |
|
629 XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); |
|
630 ret->locMax = XML_RANGESET_DEFAULT; |
|
631 ret->locTab[ret->locNr++] = val; |
|
632 } |
|
633 return(ret); |
|
634 } |
|
635 |
|
636 /** |
|
637 * xmlXPtrLocationSetAdd: |
|
638 * @param cur the initial range set |
|
639 * @param val a new xmlXPathObjectPtr |
|
640 * |
|
641 * add a new xmlXPathObjectPtr to an existing LocationSet |
|
642 * If the location already exist in the set val is freed. |
|
643 */ |
|
644 XMLPUBFUNEXPORT void |
|
645 xmlXPtrLocationSetAdd(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { |
|
646 int i; |
|
647 |
|
648 if (val == NULL) return; |
|
649 |
|
650 /* |
|
651 * check against doublons |
|
652 */ |
|
653 for (i = 0;i < cur->locNr;i++) { |
|
654 if (xmlXPtrRangesEqual(cur->locTab[i], val)) { |
|
655 xmlXPathFreeObject(val); |
|
656 return; |
|
657 } |
|
658 } |
|
659 |
|
660 /* |
|
661 * grow the locTab if needed |
|
662 */ |
|
663 if (cur->locMax == 0) { |
|
664 cur->locTab = (xmlXPathObjectPtr *) xmlMalloc(XML_RANGESET_DEFAULT * |
|
665 sizeof(xmlXPathObjectPtr)); |
|
666 if (cur->locTab == NULL) { |
|
667 xmlXPtrErrMemory("adding location to set"); |
|
668 return; |
|
669 } |
|
670 memset(cur->locTab, 0 , |
|
671 XML_RANGESET_DEFAULT * (size_t) sizeof(xmlXPathObjectPtr)); |
|
672 cur->locMax = XML_RANGESET_DEFAULT; |
|
673 } else if (cur->locNr == cur->locMax) { |
|
674 xmlXPathObjectPtr *temp; |
|
675 |
|
676 cur->locMax *= 2; |
|
677 temp = (xmlXPathObjectPtr *) xmlRealloc(cur->locTab, cur->locMax * |
|
678 sizeof(xmlXPathObjectPtr)); |
|
679 if (temp == NULL) { |
|
680 xmlXPtrErrMemory("adding location to set"); |
|
681 return; |
|
682 } |
|
683 cur->locTab = temp; |
|
684 } |
|
685 cur->locTab[cur->locNr++] = val; |
|
686 } |
|
687 |
|
688 /** |
|
689 * xmlXPtrLocationSetMerge: |
|
690 * @param val1 the first LocationSet |
|
691 * @param val2 the second LocationSet |
|
692 * |
|
693 * Merges two rangesets, all ranges from val2 are added to val1 |
|
694 * |
|
695 * Returns val1 once extended or NULL in case of error. |
|
696 */ |
|
697 XMLPUBFUNEXPORT xmlLocationSetPtr |
|
698 xmlXPtrLocationSetMerge(xmlLocationSetPtr val1, xmlLocationSetPtr val2) { |
|
699 int i; |
|
700 |
|
701 if (val1 == NULL) return(NULL); |
|
702 if (val2 == NULL) return(val1); |
|
703 |
|
704 /* |
|
705 * !!!!! this can be optimized a lot, knowing that both |
|
706 * val1 and val2 already have unicity of their values. |
|
707 */ |
|
708 |
|
709 for (i = 0;i < val2->locNr;i++) |
|
710 xmlXPtrLocationSetAdd(val1, val2->locTab[i]); |
|
711 |
|
712 return(val1); |
|
713 } |
|
714 |
|
715 /** |
|
716 * xmlXPtrLocationSetDel: |
|
717 * @param cur the initial range set |
|
718 * @param val an xmlXPathObjectPtr |
|
719 * |
|
720 * Removes an xmlXPathObjectPtr from an existing LocationSet |
|
721 */ |
|
722 XMLPUBFUNEXPORT void |
|
723 xmlXPtrLocationSetDel(xmlLocationSetPtr cur, xmlXPathObjectPtr val) { |
|
724 int i; |
|
725 |
|
726 if (cur == NULL) return; |
|
727 if (val == NULL) return; |
|
728 |
|
729 /* |
|
730 * check against doublons |
|
731 */ |
|
732 for (i = 0;i < cur->locNr;i++) |
|
733 if (cur->locTab[i] == val) break; |
|
734 |
|
735 if (i >= cur->locNr) { |
|
736 #ifdef DEBUG |
|
737 xmlGenericError(xmlGenericErrorContext, |
|
738 "xmlXPtrLocationSetDel: Range wasn't found in RangeList\n"); |
|
739 #endif |
|
740 return; |
|
741 } |
|
742 cur->locNr--; |
|
743 for (;i < cur->locNr;i++) |
|
744 cur->locTab[i] = cur->locTab[i + 1]; |
|
745 cur->locTab[cur->locNr] = NULL; |
|
746 } |
|
747 |
|
748 /** |
|
749 * xmlXPtrLocationSetRemove: |
|
750 * @param cur the initial range set |
|
751 * @param val the index to remove |
|
752 * |
|
753 * Removes an entry from an existing LocationSet list. |
|
754 */ |
|
755 XMLPUBFUNEXPORT void |
|
756 xmlXPtrLocationSetRemove(xmlLocationSetPtr cur, int val) { |
|
757 if (cur == NULL) return; |
|
758 if (val >= cur->locNr) return; |
|
759 cur->locNr--; |
|
760 for (;val < cur->locNr;val++) |
|
761 cur->locTab[val] = cur->locTab[val + 1]; |
|
762 cur->locTab[cur->locNr] = NULL; |
|
763 } |
|
764 |
|
765 /** |
|
766 * xmlXPtrFreeLocationSet: |
|
767 * @param obj the xmlLocationSetPtr to free |
|
768 * |
|
769 * Free the LocationSet compound (not the actual ranges !). |
|
770 */ |
|
771 XMLPUBFUNEXPORT void |
|
772 xmlXPtrFreeLocationSet(xmlLocationSetPtr obj) { |
|
773 int i; |
|
774 |
|
775 if (obj == NULL) return; |
|
776 if (obj->locTab != NULL) { |
|
777 for (i = 0;i < obj->locNr; i++) { |
|
778 xmlXPathFreeObject(obj->locTab[i]); |
|
779 } |
|
780 xmlFree(obj->locTab); |
|
781 } |
|
782 xmlFree(obj); |
|
783 } |
|
784 |
|
785 /** |
|
786 * xmlXPtrNewLocationSetNodes: |
|
787 * @param start the start NodePtr value |
|
788 * @param end the end NodePtr value or NULL |
|
789 * |
|
790 * Create a new xmlXPathObjectPtr of type LocationSet and initialize |
|
791 * it with the single range made of the two nodes start and end |
|
792 * |
|
793 * Returns the newly created object. |
|
794 */ |
|
795 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
796 xmlXPtrNewLocationSetNodes(xmlNodePtr start, xmlNodePtr end) { |
|
797 xmlXPathObjectPtr ret; |
|
798 |
|
799 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
800 if (ret == NULL) { |
|
801 xmlXPtrErrMemory("allocating locationset"); |
|
802 return(NULL); |
|
803 } |
|
804 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
805 ret->type = XPATH_LOCATIONSET; |
|
806 if (end == NULL) |
|
807 ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewCollapsedRange(start)); |
|
808 else |
|
809 ret->user = xmlXPtrLocationSetCreate(xmlXPtrNewRangeNodes(start,end)); |
|
810 return(ret); |
|
811 } |
|
812 |
|
813 /** |
|
814 * xmlXPtrNewLocationSetNodeSet: |
|
815 * @param set a node set |
|
816 * |
|
817 * Create a new xmlXPathObjectPtr of type LocationSet and initialize |
|
818 * it with all the nodes from set |
|
819 * |
|
820 * Returns the newly created object. |
|
821 */ |
|
822 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
823 xmlXPtrNewLocationSetNodeSet(xmlNodeSetPtr set) { |
|
824 xmlXPathObjectPtr ret; |
|
825 |
|
826 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
827 if (ret == NULL) { |
|
828 xmlXPtrErrMemory("allocating locationset"); |
|
829 return(NULL); |
|
830 } |
|
831 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
832 ret->type = XPATH_LOCATIONSET; |
|
833 if (set != NULL) { |
|
834 int i; |
|
835 xmlLocationSetPtr newset; |
|
836 |
|
837 newset = xmlXPtrLocationSetCreate(NULL); |
|
838 if (newset == NULL) |
|
839 return(ret); |
|
840 |
|
841 for (i = 0;i < set->nodeNr;i++) |
|
842 xmlXPtrLocationSetAdd(newset, |
|
843 xmlXPtrNewCollapsedRange(set->nodeTab[i])); |
|
844 |
|
845 ret->user = (void *) newset; |
|
846 } |
|
847 return(ret); |
|
848 } |
|
849 |
|
850 /** |
|
851 * xmlXPtrWrapLocationSet: |
|
852 * @param val the LocationSet value |
|
853 * |
|
854 * Wrap the LocationSet val in a new xmlXPathObjectPtr |
|
855 * |
|
856 * Returns the newly created object. |
|
857 */ |
|
858 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
859 xmlXPtrWrapLocationSet(xmlLocationSetPtr val) { |
|
860 xmlXPathObjectPtr ret; |
|
861 |
|
862 ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject)); |
|
863 if (ret == NULL) { |
|
864 xmlXPtrErrMemory("allocating locationset"); |
|
865 return(NULL); |
|
866 } |
|
867 memset(ret, 0 , (size_t) sizeof(xmlXPathObject)); |
|
868 ret->type = XPATH_LOCATIONSET; |
|
869 ret->user = (void *) val; |
|
870 return(ret); |
|
871 } |
|
872 |
|
873 /************************************************************************ |
|
874 * * |
|
875 * The parser * |
|
876 * * |
|
877 ************************************************************************/ |
|
878 |
|
879 static void xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name); |
|
880 |
|
881 /* |
|
882 * Macros for accessing the content. Those should be used only by the parser, |
|
883 * and not exported. |
|
884 * |
|
885 * Dirty macros, i.e. one need to make assumption on the context to use them |
|
886 * |
|
887 * CUR_PTR return the current pointer to the xmlChar to be parsed. |
|
888 * CUR returns the current xmlChar value, i.e. a 8 bit value |
|
889 * in ISO-Latin or UTF-8. |
|
890 * This should be used internally by the parser |
|
891 * only to compare to ASCII values otherwise it would break when |
|
892 * running with UTF-8 encoding. |
|
893 * NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only |
|
894 * to compare on ASCII based substring. |
|
895 * SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined |
|
896 * strings within the parser. |
|
897 * CURRENT Returns the current char value, with the full decoding of |
|
898 * UTF-8 if we are using this mode. It returns an int. |
|
899 * NEXT Skip to the next character, this does the proper decoding |
|
900 * in UTF-8 mode. It also pop-up unfinished entities on the fly. |
|
901 * It returns the pointer to the current xmlChar. |
|
902 */ |
|
903 |
|
904 #define CUR (*ctxt->cur) |
|
905 #define SKIP(val) ctxt->cur += (val) |
|
906 #define NXT(val) ctxt->cur[(val)] |
|
907 #define CUR_PTR ctxt->cur |
|
908 |
|
909 #define SKIP_BLANKS \ |
|
910 while (IS_BLANK_CH(*(ctxt->cur))) NEXT |
|
911 |
|
912 #define CURRENT (*ctxt->cur) |
|
913 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) |
|
914 |
|
915 /* |
|
916 * xmlXPtrGetChildNo: |
|
917 * @param ctxt the XPointer Parser context |
|
918 * @param index the child number |
|
919 * |
|
920 * Move the current node of the nodeset on the stack to the |
|
921 * given child if found |
|
922 */ |
|
923 static void |
|
924 xmlXPtrGetChildNo(xmlXPathParserContextPtr ctxt, int indx) { |
|
925 xmlNodePtr cur = NULL; |
|
926 xmlXPathObjectPtr obj; |
|
927 xmlNodeSetPtr oldset; |
|
928 |
|
929 CHECK_TYPE(XPATH_NODESET); |
|
930 obj = valuePop(ctxt); |
|
931 oldset = obj->nodesetval; |
|
932 if ((indx <= 0) || (oldset == NULL) || (oldset->nodeNr != 1)) { |
|
933 xmlXPathFreeObject(obj); |
|
934 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
|
935 return; |
|
936 } |
|
937 cur = xmlXPtrGetNthChild(oldset->nodeTab[0], indx); |
|
938 if (cur == NULL) { |
|
939 xmlXPathFreeObject(obj); |
|
940 valuePush(ctxt, xmlXPathNewNodeSet(NULL)); |
|
941 return; |
|
942 } |
|
943 oldset->nodeTab[0] = cur; |
|
944 valuePush(ctxt, obj); |
|
945 } |
|
946 |
|
947 /** |
|
948 * xmlXPtrEvalXPtrPart: |
|
949 * @param ctxt the XPointer Parser context |
|
950 * @param name the preparsed Scheme for the XPtrPart |
|
951 * |
|
952 * XPtrPart ::= 'xpointer' '(' XPtrExpr ')' |
|
953 * | Scheme '(' SchemeSpecificExpr ')' |
|
954 * |
|
955 * Scheme ::= NCName - 'xpointer' [VC: Non-XPointer schemes] |
|
956 * |
|
957 * SchemeSpecificExpr ::= StringWithBalancedParens |
|
958 * |
|
959 * StringWithBalancedParens ::= |
|
960 * [^()]* ('(' StringWithBalancedParens ')' [^()]*)* |
|
961 * [VC: Parenthesis escaping] |
|
962 * |
|
963 * XPtrExpr ::= Expr [VC: Parenthesis escaping] |
|
964 * |
|
965 * VC: Parenthesis escaping: |
|
966 * The end of an XPointer part is signaled by the right parenthesis ")" |
|
967 * character that is balanced with the left parenthesis "(" character |
|
968 * that began the part. Any unbalanced parenthesis character inside the |
|
969 * expression, even within literals, must be escaped with a circumflex (^) |
|
970 * character preceding it. If the expression contains any literal |
|
971 * occurrences of the circumflex, each must be escaped with an additional |
|
972 * circumflex (that is, ^^). If the unescaped parentheses in the expression |
|
973 * are not balanced, a syntax error results. |
|
974 * |
|
975 * Parse and evaluate an XPtrPart. Basically it generates the unescaped |
|
976 * string and if the scheme is 'xpointer' it will call the XPath interpreter. |
|
977 * |
|
978 * |
|
979 */ |
|
980 |
|
981 static void |
|
982 xmlXPtrEvalXPtrPart(xmlXPathParserContextPtr ctxt, xmlChar *name) { |
|
983 xmlChar *buffer, *cur; |
|
984 int len; |
|
985 int level; |
|
986 |
|
987 if (name == NULL) |
|
988 name = xmlXPathParseName(ctxt); |
|
989 if (name == NULL) |
|
990 XP_ERROR(XPATH_EXPR_ERROR); |
|
991 |
|
992 if (CUR != '(') |
|
993 XP_ERROR(XPATH_EXPR_ERROR); |
|
994 NEXT; |
|
995 level = 1; |
|
996 |
|
997 len = xmlStrlen(ctxt->cur); |
|
998 len++; |
|
999 buffer = (xmlChar *) xmlMallocAtomic(len * sizeof (xmlChar)); |
|
1000 if (buffer == NULL) { |
|
1001 xmlXPtrErrMemory("allocating buffer"); |
|
1002 xmlFree( name ); |
|
1003 return; |
|
1004 } |
|
1005 |
|
1006 cur = buffer; |
|
1007 while (CUR != 0) { |
|
1008 if (CUR == ')') { |
|
1009 level--; |
|
1010 if (level == 0) { |
|
1011 NEXT; |
|
1012 break; |
|
1013 } |
|
1014 *cur++ = CUR; |
|
1015 } else if (CUR == '(') { |
|
1016 level++; |
|
1017 *cur++ = CUR; |
|
1018 } else if (CUR == '^') { |
|
1019 NEXT; |
|
1020 if ((CUR == ')') || (CUR == '(') || (CUR == '^')) { |
|
1021 *cur++ = CUR; |
|
1022 } else { |
|
1023 *cur++ = '^'; |
|
1024 *cur++ = CUR; |
|
1025 } |
|
1026 } else { |
|
1027 *cur++ = CUR; |
|
1028 } |
|
1029 NEXT; |
|
1030 } |
|
1031 *cur = 0; |
|
1032 |
|
1033 if ((level != 0) && (CUR == 0)) { |
|
1034 xmlFree(buffer); |
|
1035 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1036 } |
|
1037 |
|
1038 if (xmlStrEqual(name, (xmlChar *) "xpointer")) { |
|
1039 const xmlChar *left = CUR_PTR; |
|
1040 |
|
1041 CUR_PTR = buffer; |
|
1042 /* |
|
1043 * To evaluate an xpointer scheme element (4.3) we need: |
|
1044 * context initialized to the root |
|
1045 * context position initalized to 1 |
|
1046 * context size initialized to 1 |
|
1047 */ |
|
1048 ctxt->context->node = (xmlNodePtr)ctxt->context->doc; |
|
1049 ctxt->context->proximityPosition = 1; |
|
1050 ctxt->context->contextSize = 1; |
|
1051 xmlXPathEvalExpr(ctxt); |
|
1052 CUR_PTR=left; |
|
1053 } else if (xmlStrEqual(name, (xmlChar *) "element")) { |
|
1054 const xmlChar *left = CUR_PTR; |
|
1055 xmlChar *name2; |
|
1056 |
|
1057 CUR_PTR = buffer; |
|
1058 if (buffer[0] == '/') { |
|
1059 xmlXPathRoot(ctxt); |
|
1060 xmlXPtrEvalChildSeq(ctxt, NULL); |
|
1061 } else { |
|
1062 name2 = xmlXPathParseName(ctxt); |
|
1063 if (name2 == NULL) { |
|
1064 CUR_PTR = left; |
|
1065 xmlFree(buffer); |
|
1066 XP_ERROR(XPATH_EXPR_ERROR); |
|
1067 } |
|
1068 xmlXPtrEvalChildSeq(ctxt, name2); |
|
1069 } |
|
1070 CUR_PTR = left; |
|
1071 #ifdef XPTR_XMLNS_SCHEME |
|
1072 } else if (xmlStrEqual(name, (xmlChar *) "xmlns")) { |
|
1073 const xmlChar *left = CUR_PTR; |
|
1074 xmlChar *prefix; |
|
1075 xmlChar *URI; |
|
1076 xmlURIPtr value; |
|
1077 |
|
1078 CUR_PTR = buffer; |
|
1079 prefix = xmlXPathParseNCName(ctxt); |
|
1080 if (prefix == NULL) { |
|
1081 xmlFree(buffer); |
|
1082 xmlFree(name); |
|
1083 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1084 } |
|
1085 SKIP_BLANKS; |
|
1086 if (CUR != '=') { |
|
1087 xmlFree(prefix); |
|
1088 xmlFree(buffer); |
|
1089 xmlFree(name); |
|
1090 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1091 } |
|
1092 NEXT; |
|
1093 SKIP_BLANKS; |
|
1094 /* @@ check escaping in the XPointer WD */ |
|
1095 |
|
1096 value = xmlParseURI((const char *)ctxt->cur); |
|
1097 if (value == NULL) { |
|
1098 xmlFree(prefix); |
|
1099 xmlFree(buffer); |
|
1100 xmlFree(name); |
|
1101 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1102 } |
|
1103 URI = xmlSaveUri(value); |
|
1104 xmlFreeURI(value); |
|
1105 if (URI == NULL) { |
|
1106 xmlFree(prefix); |
|
1107 xmlFree(buffer); |
|
1108 xmlFree(name); |
|
1109 XP_ERROR(XPATH_MEMORY_ERROR); |
|
1110 } |
|
1111 |
|
1112 xmlXPathRegisterNs(ctxt->context, prefix, URI); |
|
1113 CUR_PTR = left; |
|
1114 xmlFree(URI); |
|
1115 xmlFree(prefix); |
|
1116 #endif /* XPTR_XMLNS_SCHEME */ |
|
1117 } else { |
|
1118 xmlXPtrErr(ctxt, XML_XPTR_UNKNOWN_SCHEME, |
|
1119 "unsupported scheme '%s'\n", name); |
|
1120 } |
|
1121 xmlFree(buffer); |
|
1122 xmlFree(name); |
|
1123 } |
|
1124 |
|
1125 /** |
|
1126 * xmlXPtrEvalFullXPtr: |
|
1127 * @param ctxt the XPointer Parser context |
|
1128 * @param name the preparsed Scheme for the first XPtrPart |
|
1129 * |
|
1130 * FullXPtr ::= XPtrPart (S? XPtrPart)* |
|
1131 * |
|
1132 * As the specs says: |
|
1133 * ----------- |
|
1134 * When multiple XPtrParts are provided, they must be evaluated in |
|
1135 * left-to-right order. If evaluation of one part fails, the nexti |
|
1136 * is evaluated. The following conditions cause XPointer part failure: |
|
1137 * |
|
1138 * - An unknown scheme |
|
1139 * - A scheme that does not locate any sub-resource present in the resource |
|
1140 * - A scheme that is not applicable to the media type of the resource |
|
1141 * |
|
1142 * The XPointer application must consume a failed XPointer part and |
|
1143 * attempt to evaluate the next one, if any. The result of the first |
|
1144 * XPointer part whose evaluation succeeds is taken to be the fragment |
|
1145 * located by the XPointer as a whole. If all the parts fail, the result |
|
1146 * for the XPointer as a whole is a sub-resource error. |
|
1147 * ----------- |
|
1148 * |
|
1149 * Parse and evaluate a Full XPtr i.e. possibly a cascade of XPath based |
|
1150 * expressions or other schemes. |
|
1151 */ |
|
1152 static void |
|
1153 xmlXPtrEvalFullXPtr(xmlXPathParserContextPtr ctxt, xmlChar *name) { |
|
1154 if (name == NULL) |
|
1155 name = xmlXPathParseName(ctxt); |
|
1156 if (name == NULL) |
|
1157 XP_ERROR(XPATH_EXPR_ERROR); |
|
1158 while (name != NULL) { |
|
1159 xmlXPtrEvalXPtrPart(ctxt, name); |
|
1160 |
|
1161 /* in case of syntax error, break here */ |
|
1162 if (ctxt->error != XPATH_EXPRESSION_OK) |
|
1163 return; |
|
1164 |
|
1165 /* |
|
1166 * If the returned value is a non-empty nodeset |
|
1167 * or location set, return here. |
|
1168 */ |
|
1169 if (ctxt->value != NULL) { |
|
1170 xmlXPathObjectPtr obj = ctxt->value; |
|
1171 |
|
1172 switch (obj->type) { |
|
1173 case XPATH_LOCATIONSET: { |
|
1174 xmlLocationSetPtr loc = ctxt->value->user; |
|
1175 if ((loc != NULL) && (loc->locNr > 0)) |
|
1176 return; |
|
1177 break; |
|
1178 } |
|
1179 case XPATH_NODESET: { |
|
1180 xmlNodeSetPtr loc = ctxt->value->nodesetval; |
|
1181 if ((loc != NULL) && (loc->nodeNr > 0)) |
|
1182 return; |
|
1183 break; |
|
1184 } |
|
1185 default: |
|
1186 break; |
|
1187 } |
|
1188 |
|
1189 /* |
|
1190 * Evaluating to improper values is equivalent to |
|
1191 * a sub-resource error, clean-up the stack |
|
1192 */ |
|
1193 do { |
|
1194 obj = valuePop(ctxt); |
|
1195 if (obj != NULL) { |
|
1196 xmlXPathFreeObject(obj); |
|
1197 } |
|
1198 } while (obj != NULL); |
|
1199 } |
|
1200 |
|
1201 /* |
|
1202 * Is there another XPointer part. |
|
1203 */ |
|
1204 SKIP_BLANKS; |
|
1205 name = xmlXPathParseName(ctxt); |
|
1206 } |
|
1207 } |
|
1208 |
|
1209 /** |
|
1210 * xmlXPtrEvalChildSeq: |
|
1211 * @param ctxt the XPointer Parser context |
|
1212 * @param name a possible ID name of the child sequence |
|
1213 * |
|
1214 * ChildSeq ::= '/1' ('/' [0-9]*)* |
|
1215 * | Name ('/' [0-9]*)+ |
|
1216 * |
|
1217 * Parse and evaluate a Child Sequence. This routine also handle the |
|
1218 * case of a Bare Name used to get a document ID. |
|
1219 */ |
|
1220 static void |
|
1221 xmlXPtrEvalChildSeq(xmlXPathParserContextPtr ctxt, xmlChar *name) { |
|
1222 /* |
|
1223 * XPointer don't allow by syntax to address in mutirooted trees |
|
1224 * this might prove useful in some cases, warn about it. |
|
1225 */ |
|
1226 if ((name == NULL) && (CUR == '/') && (NXT(1) != '1')) { |
|
1227 xmlXPtrErr(ctxt, XML_XPTR_CHILDSEQ_START, |
|
1228 "warning: ChildSeq not starting by /1\n", NULL); |
|
1229 } |
|
1230 |
|
1231 if (name != NULL) { |
|
1232 valuePush(ctxt, xmlXPathNewString(name)); |
|
1233 xmlFree(name); |
|
1234 xmlXPathIdFunction(ctxt, 1); |
|
1235 CHECK_ERROR; |
|
1236 } |
|
1237 |
|
1238 while (CUR == '/') { |
|
1239 int child = 0; |
|
1240 NEXT; |
|
1241 |
|
1242 while ((CUR >= '0') && (CUR <= '9')) { |
|
1243 child = child * 10 + (CUR - '0'); |
|
1244 NEXT; |
|
1245 } |
|
1246 xmlXPtrGetChildNo(ctxt, child); |
|
1247 } |
|
1248 } |
|
1249 |
|
1250 |
|
1251 /** |
|
1252 * xmlXPtrEvalXPointer: |
|
1253 * @param ctxt the XPointer Parser context |
|
1254 * |
|
1255 * XPointer ::= Name |
|
1256 * | ChildSeq |
|
1257 * | FullXPtr |
|
1258 * |
|
1259 * Parse and evaluate an XPointer |
|
1260 */ |
|
1261 static void |
|
1262 xmlXPtrEvalXPointer(xmlXPathParserContextPtr ctxt) { |
|
1263 if (ctxt->valueTab == NULL) { |
|
1264 /* Allocate the value stack */ |
|
1265 ctxt->valueTab = (xmlXPathObjectPtr *) |
|
1266 xmlMalloc(10 * sizeof(xmlXPathObjectPtr)); |
|
1267 if (ctxt->valueTab == NULL) { |
|
1268 xmlXPtrErrMemory("allocating evaluation context"); |
|
1269 return; |
|
1270 } |
|
1271 ctxt->valueNr = 0; |
|
1272 ctxt->valueMax = 10; |
|
1273 ctxt->value = NULL; |
|
1274 } |
|
1275 SKIP_BLANKS; |
|
1276 if (CUR == '/') { |
|
1277 xmlXPathRoot(ctxt); |
|
1278 xmlXPtrEvalChildSeq(ctxt, NULL); |
|
1279 } else { |
|
1280 xmlChar *name; |
|
1281 |
|
1282 name = xmlXPathParseName(ctxt); |
|
1283 if (name == NULL) |
|
1284 XP_ERROR(XPATH_EXPR_ERROR); |
|
1285 if (CUR == '(') { |
|
1286 xmlXPtrEvalFullXPtr(ctxt, name); |
|
1287 /* Short evaluation */ |
|
1288 return; |
|
1289 } else { |
|
1290 /* this handle both Bare Names and Child Sequences */ |
|
1291 xmlXPtrEvalChildSeq(ctxt, name); |
|
1292 } |
|
1293 } |
|
1294 SKIP_BLANKS; |
|
1295 if (CUR != 0) |
|
1296 XP_ERROR(XPATH_EXPR_ERROR); |
|
1297 } |
|
1298 |
|
1299 |
|
1300 /************************************************************************ |
|
1301 * * |
|
1302 * General routines * |
|
1303 * * |
|
1304 ************************************************************************/ |
|
1305 |
|
1306 void xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1307 void xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1308 void xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1309 void xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1310 void xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1311 void xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1312 void xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs); |
|
1313 |
|
1314 /** |
|
1315 * xmlXPtrNewContext: |
|
1316 * @param doc the XML document |
|
1317 * @param here the node that directly contains the XPointer being evaluated or NULL |
|
1318 * @param origin the element from which a user or program initiated traversal of |
|
1319 * the link, or NULL. |
|
1320 * |
|
1321 * Create a new XPointer context |
|
1322 * |
|
1323 * Returns the xmlXPathContext just allocated. |
|
1324 */ |
|
1325 XMLPUBFUNEXPORT xmlXPathContextPtr |
|
1326 xmlXPtrNewContext(xmlDocPtr doc, xmlNodePtr here, xmlNodePtr origin) { |
|
1327 xmlXPathContextPtr ret; |
|
1328 |
|
1329 ret = xmlXPathNewContext(doc); |
|
1330 if (ret == NULL) |
|
1331 return(ret); |
|
1332 ret->xptr = 1; |
|
1333 ret->here = here; |
|
1334 ret->origin = origin; |
|
1335 |
|
1336 xmlXPathRegisterFunc(ret, (xmlChar *)"range-to", |
|
1337 xmlXPtrRangeToFunction); |
|
1338 xmlXPathRegisterFunc(ret, (xmlChar *)"range", |
|
1339 xmlXPtrRangeFunction); |
|
1340 xmlXPathRegisterFunc(ret, (xmlChar *)"range-inside", |
|
1341 xmlXPtrRangeInsideFunction); |
|
1342 xmlXPathRegisterFunc(ret, (xmlChar *)"string-range", |
|
1343 xmlXPtrStringRangeFunction); |
|
1344 xmlXPathRegisterFunc(ret, (xmlChar *)"start-point", |
|
1345 xmlXPtrStartPointFunction); |
|
1346 xmlXPathRegisterFunc(ret, (xmlChar *)"end-point", |
|
1347 xmlXPtrEndPointFunction); |
|
1348 xmlXPathRegisterFunc(ret, (xmlChar *)"here", |
|
1349 xmlXPtrHereFunction); |
|
1350 xmlXPathRegisterFunc(ret, (xmlChar *)" origin", |
|
1351 xmlXPtrOriginFunction); |
|
1352 |
|
1353 return(ret); |
|
1354 } |
|
1355 |
|
1356 /** |
|
1357 * xmlXPtrEval: |
|
1358 * @param str the XPointer expression |
|
1359 * @param ctx the XPointer context |
|
1360 * |
|
1361 * Evaluate the XPath Location Path in the given context. |
|
1362 * |
|
1363 * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
|
1364 * the caller has to free the object. |
|
1365 */ |
|
1366 XMLPUBFUNEXPORT xmlXPathObjectPtr |
|
1367 xmlXPtrEval(const xmlChar *str, xmlXPathContextPtr ctx) { |
|
1368 xmlXPathParserContextPtr ctxt; |
|
1369 xmlXPathObjectPtr res = NULL, tmp; |
|
1370 xmlXPathObjectPtr init = NULL; |
|
1371 int stack = 0; |
|
1372 |
|
1373 xmlXPathInit(); |
|
1374 |
|
1375 if ((ctx == NULL) || (str == NULL)) |
|
1376 return(NULL); |
|
1377 |
|
1378 ctxt = xmlXPathNewParserContext(str, ctx); |
|
1379 if ( ctxt == NULL) |
|
1380 return(NULL); |
|
1381 ctxt->xptr = 1; |
|
1382 xmlXPtrEvalXPointer(ctxt); |
|
1383 |
|
1384 if ((ctxt->value != NULL) && |
|
1385 (ctxt->value->type != XPATH_NODESET) && |
|
1386 (ctxt->value->type != XPATH_LOCATIONSET)) { |
|
1387 xmlXPtrErr(ctxt, XML_XPTR_EVAL_FAILED, |
|
1388 "xmlXPtrEval: evaluation failed to return a node set\n", |
|
1389 NULL); |
|
1390 } else { |
|
1391 res = valuePop(ctxt); |
|
1392 } |
|
1393 |
|
1394 do { |
|
1395 tmp = valuePop(ctxt); |
|
1396 if (tmp != NULL) { |
|
1397 if (tmp != init) { |
|
1398 if (tmp->type == XPATH_NODESET) { |
|
1399 /* |
|
1400 * Evaluation may push a root nodeset which is unused |
|
1401 */ |
|
1402 xmlNodeSetPtr set; |
|
1403 set = tmp->nodesetval; |
|
1404 if ((set->nodeNr != 1) || |
|
1405 (set->nodeTab[0] != (xmlNodePtr) ctx->doc)) |
|
1406 stack++; |
|
1407 } else |
|
1408 stack++; |
|
1409 } |
|
1410 xmlXPathFreeObject(tmp); |
|
1411 } |
|
1412 } while (tmp != NULL); |
|
1413 if (stack != 0) { |
|
1414 xmlXPtrErr(ctxt, XML_XPTR_EXTRA_OBJECTS, |
|
1415 "xmlXPtrEval: object(s) left on the eval stack\n", |
|
1416 NULL); |
|
1417 } |
|
1418 if (ctxt->error != XPATH_EXPRESSION_OK) { |
|
1419 xmlXPathFreeObject(res); |
|
1420 res = NULL; |
|
1421 } |
|
1422 |
|
1423 xmlXPathFreeParserContext(ctxt); |
|
1424 return(res); |
|
1425 } |
|
1426 |
|
1427 /** |
|
1428 * xmlXPtrBuildRangeNodeList: |
|
1429 * @param range a range object |
|
1430 * |
|
1431 * Build a node list tree copy of the range |
|
1432 * |
|
1433 * Returns an xmlNodePtr list or NULL. |
|
1434 * the caller has to free the node tree. |
|
1435 */ |
|
1436 static xmlNodePtr |
|
1437 xmlXPtrBuildRangeNodeList(xmlXPathObjectPtr range) { |
|
1438 /* pointers to generated nodes */ |
|
1439 xmlNodePtr list = NULL, last = NULL, parent = NULL, tmp; |
|
1440 /* pointers to traversal nodes */ |
|
1441 xmlNodePtr start, cur, end; |
|
1442 int index1, index2; |
|
1443 |
|
1444 if (range == NULL) |
|
1445 return(NULL); |
|
1446 if (range->type != XPATH_RANGE) |
|
1447 return(NULL); |
|
1448 start = (xmlNodePtr) range->user; |
|
1449 |
|
1450 if (start == NULL) |
|
1451 return(NULL); |
|
1452 end = range->user2; |
|
1453 if (end == NULL) |
|
1454 return(xmlCopyNode(start, 1)); |
|
1455 |
|
1456 cur = start; |
|
1457 index1 = range->index; |
|
1458 index2 = range->index2; |
|
1459 while (cur != NULL) { |
|
1460 if (cur == end) { |
|
1461 if (cur->type == XML_TEXT_NODE) { |
|
1462 const xmlChar *content = cur->content; |
|
1463 int len; |
|
1464 |
|
1465 if (content == NULL) { |
|
1466 tmp = xmlNewTextLen(NULL, 0); |
|
1467 } else { |
|
1468 len = index2; |
|
1469 if ((cur == start) && (index1 > 1)) { |
|
1470 content += (index1 - 1); |
|
1471 len -= (index1 - 1); |
|
1472 index1 = 0; |
|
1473 } else { |
|
1474 len = index2; |
|
1475 } |
|
1476 tmp = xmlNewTextLen(content, len); |
|
1477 } |
|
1478 /* single sub text node selection */ |
|
1479 if (list == NULL) |
|
1480 return(tmp); |
|
1481 /* prune and return full set */ |
|
1482 if (last != NULL) |
|
1483 xmlAddNextSibling(last, tmp); |
|
1484 else |
|
1485 xmlAddChild(parent, tmp); |
|
1486 return(list); |
|
1487 } else { |
|
1488 tmp = xmlCopyNode(cur, 0); |
|
1489 if (list == NULL) |
|
1490 list = tmp; |
|
1491 else { |
|
1492 if (last != NULL) |
|
1493 xmlAddNextSibling(last, tmp); |
|
1494 else |
|
1495 xmlAddChild(parent, tmp); |
|
1496 } |
|
1497 last = NULL; |
|
1498 parent = tmp; |
|
1499 |
|
1500 if (index2 > 1) { |
|
1501 end = xmlXPtrGetNthChild(cur, index2 - 1); |
|
1502 index2 = 0; |
|
1503 } |
|
1504 if ((cur == start) && (index1 > 1)) { |
|
1505 cur = xmlXPtrGetNthChild(cur, index1 - 1); |
|
1506 index1 = 0; |
|
1507 } else { |
|
1508 cur = cur->children; |
|
1509 } |
|
1510 /* |
|
1511 * Now gather the remaining nodes from cur to end |
|
1512 */ |
|
1513 continue; /* while */ |
|
1514 } |
|
1515 } else if ((cur == start) && |
|
1516 (list == NULL) /* looks superfluous but ... */ ) { |
|
1517 if ((cur->type == XML_TEXT_NODE) || |
|
1518 (cur->type == XML_CDATA_SECTION_NODE)) { |
|
1519 const xmlChar *content = cur->content; |
|
1520 |
|
1521 if (content == NULL) { |
|
1522 tmp = xmlNewTextLen(NULL, 0); |
|
1523 } else { |
|
1524 if (index1 > 1) { |
|
1525 content += (index1 - 1); |
|
1526 } |
|
1527 tmp = xmlNewText(content); |
|
1528 } |
|
1529 last = list = tmp; |
|
1530 } else { |
|
1531 if ((cur == start) && (index1 > 1)) { |
|
1532 tmp = xmlCopyNode(cur, 0); |
|
1533 list = tmp; |
|
1534 parent = tmp; |
|
1535 last = NULL; |
|
1536 cur = xmlXPtrGetNthChild(cur, index1 - 1); |
|
1537 index1 = 0; |
|
1538 /* |
|
1539 * Now gather the remaining nodes from cur to end |
|
1540 */ |
|
1541 continue; /* while */ |
|
1542 } |
|
1543 tmp = xmlCopyNode(cur, 1); |
|
1544 list = tmp; |
|
1545 parent = NULL; |
|
1546 last = tmp; |
|
1547 } |
|
1548 } else { |
|
1549 tmp = NULL; |
|
1550 switch (cur->type) { |
|
1551 case XML_DTD_NODE: |
|
1552 case XML_ELEMENT_DECL: |
|
1553 case XML_ATTRIBUTE_DECL: |
|
1554 case XML_ENTITY_NODE: |
|
1555 /* Do not copy DTD informations */ |
|
1556 break; |
|
1557 case XML_ENTITY_DECL: |
|
1558 TODO /* handle crossing entities -> stack needed */ |
|
1559 break; |
|
1560 case XML_XINCLUDE_START: |
|
1561 case XML_XINCLUDE_END: |
|
1562 /* don't consider it part of the tree content */ |
|
1563 break; |
|
1564 case XML_ATTRIBUTE_NODE: |
|
1565 /* Humm, should not happen ! */ |
|
1566 STRANGE |
|
1567 break; |
|
1568 default: |
|
1569 tmp = xmlCopyNode(cur, 1); |
|
1570 break; |
|
1571 } |
|
1572 if (tmp != NULL) { |
|
1573 if ((list == NULL) || ((last == NULL) && (parent == NULL))) { |
|
1574 STRANGE |
|
1575 return(NULL); |
|
1576 } |
|
1577 if (last != NULL) |
|
1578 xmlAddNextSibling(last, tmp); |
|
1579 else { |
|
1580 xmlAddChild(parent, tmp); |
|
1581 last = tmp; |
|
1582 } |
|
1583 } |
|
1584 } |
|
1585 /* |
|
1586 * Skip to next node in document order |
|
1587 */ |
|
1588 if ((list == NULL) || ((last == NULL) && (parent == NULL))) { |
|
1589 STRANGE |
|
1590 return(NULL); |
|
1591 } |
|
1592 cur = xmlXPtrAdvanceNode(cur, NULL); |
|
1593 } |
|
1594 return(list); |
|
1595 } |
|
1596 |
|
1597 /** |
|
1598 * xmlXPtrBuildNodeList: |
|
1599 * @param obj the XPointer result from the evaluation. |
|
1600 * |
|
1601 * Build a node list tree copy of the XPointer result. |
|
1602 * This will drop Attributes and Namespace declarations. |
|
1603 * |
|
1604 * Returns an xmlNodePtr list or NULL. |
|
1605 * the caller has to free the node tree. |
|
1606 */ |
|
1607 XMLPUBFUNEXPORT xmlNodePtr |
|
1608 xmlXPtrBuildNodeList(xmlXPathObjectPtr obj) { |
|
1609 xmlNodePtr list = NULL, last = NULL; |
|
1610 int i; |
|
1611 |
|
1612 if (obj == NULL) |
|
1613 return(NULL); |
|
1614 switch (obj->type) { |
|
1615 case XPATH_NODESET: { |
|
1616 xmlNodeSetPtr set = obj->nodesetval; |
|
1617 if (set == NULL) |
|
1618 return(NULL); |
|
1619 for (i = 0;i < set->nodeNr;i++) { |
|
1620 if (set->nodeTab[i] == NULL) |
|
1621 continue; |
|
1622 switch (set->nodeTab[i]->type) { |
|
1623 case XML_TEXT_NODE: |
|
1624 case XML_CDATA_SECTION_NODE: |
|
1625 case XML_ELEMENT_NODE: |
|
1626 case XML_ENTITY_REF_NODE: |
|
1627 case XML_ENTITY_NODE: |
|
1628 case XML_PI_NODE: |
|
1629 case XML_COMMENT_NODE: |
|
1630 case XML_DOCUMENT_NODE: |
|
1631 case XML_HTML_DOCUMENT_NODE: |
|
1632 #ifdef LIBXML_DOCB_ENABLED |
|
1633 case XML_DOCB_DOCUMENT_NODE: |
|
1634 #endif |
|
1635 case XML_XINCLUDE_START: |
|
1636 case XML_XINCLUDE_END: |
|
1637 break; |
|
1638 case XML_ATTRIBUTE_NODE: |
|
1639 case XML_NAMESPACE_DECL: |
|
1640 case XML_DOCUMENT_TYPE_NODE: |
|
1641 case XML_DOCUMENT_FRAG_NODE: |
|
1642 case XML_NOTATION_NODE: |
|
1643 case XML_DTD_NODE: |
|
1644 case XML_ELEMENT_DECL: |
|
1645 case XML_ATTRIBUTE_DECL: |
|
1646 case XML_ENTITY_DECL: |
|
1647 continue; /* for */ |
|
1648 } |
|
1649 if (last == NULL) |
|
1650 list = last = xmlCopyNode(set->nodeTab[i], 1); |
|
1651 else { |
|
1652 xmlAddNextSibling(last, xmlCopyNode(set->nodeTab[i], 1)); |
|
1653 if (last->next != NULL) |
|
1654 last = last->next; |
|
1655 } |
|
1656 } |
|
1657 break; |
|
1658 } |
|
1659 case XPATH_LOCATIONSET: { |
|
1660 xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user; |
|
1661 if (set == NULL) |
|
1662 return(NULL); |
|
1663 for (i = 0;i < set->locNr;i++) { |
|
1664 if (last == NULL) |
|
1665 list = last = xmlXPtrBuildNodeList(set->locTab[i]); |
|
1666 else |
|
1667 xmlAddNextSibling(last, |
|
1668 xmlXPtrBuildNodeList(set->locTab[i])); |
|
1669 if (last != NULL) { |
|
1670 while (last->next != NULL) |
|
1671 last = last->next; |
|
1672 } |
|
1673 } |
|
1674 break; |
|
1675 } |
|
1676 case XPATH_RANGE: |
|
1677 return(xmlXPtrBuildRangeNodeList(obj)); |
|
1678 case XPATH_POINT: |
|
1679 return(xmlCopyNode(obj->user, 0)); |
|
1680 default: |
|
1681 break; |
|
1682 } |
|
1683 return(list); |
|
1684 } |
|
1685 |
|
1686 /************************************************************************ |
|
1687 * * |
|
1688 * XPointer functions * |
|
1689 * * |
|
1690 ************************************************************************/ |
|
1691 |
|
1692 /** |
|
1693 * xmlXPtrNbLocChildren: |
|
1694 * @param node an xmlNodePtr |
|
1695 * |
|
1696 * Count the number of location children of node or the length of the |
|
1697 * string value in case of text/PI/Comments nodes |
|
1698 * |
|
1699 * Returns the number of location children |
|
1700 */ |
|
1701 static int |
|
1702 xmlXPtrNbLocChildren(xmlNodePtr node) { |
|
1703 int ret = 0; |
|
1704 if (node == NULL) |
|
1705 return(-1); |
|
1706 switch (node->type) { |
|
1707 case XML_HTML_DOCUMENT_NODE: |
|
1708 case XML_DOCUMENT_NODE: |
|
1709 case XML_ELEMENT_NODE: |
|
1710 node = node->children; |
|
1711 while (node != NULL) { |
|
1712 if (node->type == XML_ELEMENT_NODE) |
|
1713 ret++; |
|
1714 node = node->next; |
|
1715 } |
|
1716 break; |
|
1717 case XML_ATTRIBUTE_NODE: |
|
1718 return(-1); |
|
1719 |
|
1720 case XML_PI_NODE: |
|
1721 case XML_COMMENT_NODE: |
|
1722 case XML_TEXT_NODE: |
|
1723 case XML_CDATA_SECTION_NODE: |
|
1724 case XML_ENTITY_REF_NODE: |
|
1725 ret = xmlStrlen(node->content); |
|
1726 break; |
|
1727 default: |
|
1728 return(-1); |
|
1729 } |
|
1730 return(ret); |
|
1731 } |
|
1732 |
|
1733 /** |
|
1734 * xmlXPtrHereFunction: |
|
1735 * @param ctxt the XPointer Parser context |
|
1736 * @param nargs the number of args |
|
1737 * |
|
1738 * Function implementing here() operation |
|
1739 * as described in 5.4.3 |
|
1740 */ |
|
1741 void |
|
1742 xmlXPtrHereFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
1743 CHECK_ARITY(0); |
|
1744 |
|
1745 if (ctxt->context->here == NULL) |
|
1746 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1747 |
|
1748 valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->here, NULL)); |
|
1749 } |
|
1750 |
|
1751 /** |
|
1752 * xmlXPtrOriginFunction: |
|
1753 * @param ctxt the XPointer Parser context |
|
1754 * @param nargs the number of args |
|
1755 * |
|
1756 * Function implementing origin() operation |
|
1757 * as described in 5.4.3 |
|
1758 */ |
|
1759 void |
|
1760 xmlXPtrOriginFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
1761 CHECK_ARITY(0); |
|
1762 |
|
1763 if (ctxt->context->origin == NULL) |
|
1764 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1765 |
|
1766 valuePush(ctxt, xmlXPtrNewLocationSetNodes(ctxt->context->origin, NULL)); |
|
1767 } |
|
1768 |
|
1769 /** |
|
1770 * xmlXPtrStartPointFunction: |
|
1771 * @param ctxt the XPointer Parser context |
|
1772 * @param nargs the number of args |
|
1773 * |
|
1774 * Function implementing start-point() operation |
|
1775 * as described in 5.4.3 |
|
1776 * ---------------- |
|
1777 * location-set start-point(location-set) |
|
1778 * |
|
1779 * For each location x in the argument location-set, start-point adds a |
|
1780 * location of type point to the result location-set. That point represents |
|
1781 * the start point of location x and is determined by the following rules: |
|
1782 * |
|
1783 * - If x is of type point, the start point is x. |
|
1784 * - If x is of type range, the start point is the start point of x. |
|
1785 * - If x is of type root, element, text, comment, or processing instruction, |
|
1786 * - the container node of the start point is x and the index is 0. |
|
1787 * - If x is of type attribute or namespace, the function must signal a |
|
1788 * syntax error. |
|
1789 * ---------------- |
|
1790 * |
|
1791 */ |
|
1792 void |
|
1793 xmlXPtrStartPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
1794 xmlXPathObjectPtr tmp, obj, point; |
|
1795 xmlLocationSetPtr newset = NULL; |
|
1796 xmlLocationSetPtr oldset = NULL; |
|
1797 |
|
1798 CHECK_ARITY(1); |
|
1799 if ((ctxt->value == NULL) || |
|
1800 ((ctxt->value->type != XPATH_LOCATIONSET) && |
|
1801 (ctxt->value->type != XPATH_NODESET))) |
|
1802 XP_ERROR(XPATH_INVALID_TYPE) |
|
1803 |
|
1804 obj = valuePop(ctxt); |
|
1805 if (obj->type == XPATH_NODESET) { |
|
1806 /* |
|
1807 * First convert to a location set |
|
1808 */ |
|
1809 tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); |
|
1810 xmlXPathFreeObject(obj); |
|
1811 obj = tmp; |
|
1812 } |
|
1813 |
|
1814 newset = xmlXPtrLocationSetCreate(NULL); |
|
1815 if (newset == NULL) { |
|
1816 xmlXPathFreeObject(obj); |
|
1817 XP_ERROR(XPATH_MEMORY_ERROR); |
|
1818 } |
|
1819 oldset = (xmlLocationSetPtr) obj->user; |
|
1820 if (oldset != NULL) { |
|
1821 int i; |
|
1822 |
|
1823 for (i = 0; i < oldset->locNr; i++) { |
|
1824 tmp = oldset->locTab[i]; |
|
1825 if (tmp == NULL) |
|
1826 continue; |
|
1827 point = NULL; |
|
1828 switch (tmp->type) { |
|
1829 case XPATH_POINT: |
|
1830 point = xmlXPtrNewPoint(tmp->user, tmp->index); |
|
1831 break; |
|
1832 case XPATH_RANGE: { |
|
1833 xmlNodePtr node = tmp->user; |
|
1834 if (node != NULL) { |
|
1835 if (node->type == XML_ATTRIBUTE_NODE) { |
|
1836 |
|
1837 xmlXPathFreeObject(obj); |
|
1838 xmlXPtrFreeLocationSet(newset); |
|
1839 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1840 } |
|
1841 point = xmlXPtrNewPoint(node, tmp->index); |
|
1842 } |
|
1843 break; |
|
1844 } |
|
1845 default: |
|
1846 /*** Should we raise an error ? |
|
1847 xmlXPathFreeObject(obj); |
|
1848 xmlXPathFreeObject(newset); |
|
1849 XP_ERROR(XPATH_INVALID_TYPE) |
|
1850 ***/ |
|
1851 break; |
|
1852 } |
|
1853 if (point != NULL) |
|
1854 xmlXPtrLocationSetAdd(newset, point); |
|
1855 } |
|
1856 } |
|
1857 xmlXPathFreeObject(obj); |
|
1858 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
1859 } |
|
1860 |
|
1861 /** |
|
1862 * xmlXPtrEndPointFunction: |
|
1863 * @param ctxt the XPointer Parser context |
|
1864 * @param nargs the number of args |
|
1865 * |
|
1866 * Function implementing end-point() operation |
|
1867 * as described in 5.4.3 |
|
1868 * ---------------------------- |
|
1869 * location-set end-point(location-set) |
|
1870 * |
|
1871 * For each location x in the argument location-set, end-point adds a |
|
1872 * location of type point to the result location-set. That point represents |
|
1873 * the end point of location x and is determined by the following rules: |
|
1874 * |
|
1875 * - If x is of type point, the resulting point is x. |
|
1876 * - If x is of type range, the resulting point is the end point of x. |
|
1877 * - If x is of type root or element, the container node of the resulting |
|
1878 * point is x and the index is the number of location children of x. |
|
1879 * - If x is of type text, comment, or processing instruction, the container |
|
1880 * node of the resulting point is x and the index is the length of the |
|
1881 * string-value of x. |
|
1882 * - If x is of type attribute or namespace, the function must signal a |
|
1883 * syntax error. |
|
1884 * ---------------------------- |
|
1885 */ |
|
1886 void |
|
1887 xmlXPtrEndPointFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
1888 xmlXPathObjectPtr tmp, obj, point; |
|
1889 xmlLocationSetPtr newset = NULL; |
|
1890 xmlLocationSetPtr oldset = NULL; |
|
1891 |
|
1892 CHECK_ARITY(1); |
|
1893 if ((ctxt->value == NULL) || |
|
1894 ((ctxt->value->type != XPATH_LOCATIONSET) && |
|
1895 (ctxt->value->type != XPATH_NODESET))) |
|
1896 XP_ERROR(XPATH_INVALID_TYPE) |
|
1897 |
|
1898 obj = valuePop(ctxt); |
|
1899 if (obj->type == XPATH_NODESET) { |
|
1900 /* |
|
1901 * First convert to a location set |
|
1902 */ |
|
1903 tmp = xmlXPtrNewLocationSetNodeSet(obj->nodesetval); |
|
1904 xmlXPathFreeObject(obj); |
|
1905 obj = tmp; |
|
1906 } |
|
1907 |
|
1908 newset = xmlXPtrLocationSetCreate(NULL); |
|
1909 oldset = (xmlLocationSetPtr) obj->user; |
|
1910 if (oldset != NULL) { |
|
1911 int i; |
|
1912 |
|
1913 for (i = 0; i < oldset->locNr; i++) { |
|
1914 tmp = oldset->locTab[i]; |
|
1915 if (tmp == NULL) |
|
1916 continue; |
|
1917 point = NULL; |
|
1918 switch (tmp->type) { |
|
1919 case XPATH_POINT: |
|
1920 point = xmlXPtrNewPoint(tmp->user, tmp->index); |
|
1921 break; |
|
1922 case XPATH_RANGE: { |
|
1923 xmlNodePtr node = tmp->user2; |
|
1924 if (node != NULL) { |
|
1925 if (node->type == XML_ATTRIBUTE_NODE) { |
|
1926 |
|
1927 xmlXPathFreeObject(obj); |
|
1928 xmlXPtrFreeLocationSet(newset); |
|
1929 XP_ERROR(XPTR_SYNTAX_ERROR); |
|
1930 } |
|
1931 point = xmlXPtrNewPoint(node, tmp->index2); |
|
1932 } else if (tmp->user == NULL) { |
|
1933 point = xmlXPtrNewPoint(node, |
|
1934 xmlXPtrNbLocChildren(node)); |
|
1935 } |
|
1936 break; |
|
1937 } |
|
1938 default: |
|
1939 /*** Should we raise an error ? |
|
1940 xmlXPathFreeObject(obj); |
|
1941 xmlXPathFreeObject(newset); |
|
1942 XP_ERROR(XPATH_INVALID_TYPE) |
|
1943 ***/ |
|
1944 break; |
|
1945 } |
|
1946 if (point != NULL) |
|
1947 xmlXPtrLocationSetAdd(newset, point); |
|
1948 } |
|
1949 } |
|
1950 xmlXPathFreeObject(obj); |
|
1951 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
1952 } |
|
1953 |
|
1954 |
|
1955 /** |
|
1956 * xmlXPtrCoveringRange: |
|
1957 * @param ctxt the XPointer Parser context |
|
1958 * @param loc the location for which the covering range must be computed |
|
1959 * |
|
1960 * A covering range is a range that wholly encompasses a location |
|
1961 * Section 5.3.3. Covering Ranges for All Location Types |
|
1962 * http://www.w3.org/TR/xptr#N2267 |
|
1963 * |
|
1964 * Returns a new location or NULL in case of error |
|
1965 */ |
|
1966 static xmlXPathObjectPtr |
|
1967 xmlXPtrCoveringRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) { |
|
1968 if (loc == NULL) |
|
1969 return(NULL); |
|
1970 if ((ctxt == NULL) || (ctxt->context == NULL) || |
|
1971 (ctxt->context->doc == NULL)) |
|
1972 return(NULL); |
|
1973 switch (loc->type) { |
|
1974 case XPATH_POINT: |
|
1975 return(xmlXPtrNewRange(loc->user, loc->index, |
|
1976 loc->user, loc->index)); |
|
1977 case XPATH_RANGE: |
|
1978 if (loc->user2 != NULL) { |
|
1979 return(xmlXPtrNewRange(loc->user, loc->index, |
|
1980 loc->user2, loc->index2)); |
|
1981 } else { |
|
1982 xmlNodePtr node = (xmlNodePtr) loc->user; |
|
1983 if (node == (xmlNodePtr) ctxt->context->doc) { |
|
1984 return(xmlXPtrNewRange(node, 0, node, |
|
1985 xmlXPtrGetArity(node))); |
|
1986 } else { |
|
1987 switch (node->type) { |
|
1988 case XML_ATTRIBUTE_NODE: |
|
1989 /* !!! our model is slightly different than XPath */ |
|
1990 return(xmlXPtrNewRange(node, 0, node, |
|
1991 xmlXPtrGetArity(node))); |
|
1992 case XML_ELEMENT_NODE: |
|
1993 case XML_TEXT_NODE: |
|
1994 case XML_CDATA_SECTION_NODE: |
|
1995 case XML_ENTITY_REF_NODE: |
|
1996 case XML_PI_NODE: |
|
1997 case XML_COMMENT_NODE: |
|
1998 case XML_DOCUMENT_NODE: |
|
1999 case XML_NOTATION_NODE: |
|
2000 case XML_HTML_DOCUMENT_NODE: { |
|
2001 int indx = xmlXPtrGetIndex(node); |
|
2002 |
|
2003 node = node->parent; |
|
2004 return(xmlXPtrNewRange(node, indx - 1, |
|
2005 node, indx + 1)); |
|
2006 } |
|
2007 default: |
|
2008 return(NULL); |
|
2009 } |
|
2010 } |
|
2011 } |
|
2012 default: |
|
2013 TODO /* missed one case ??? */ |
|
2014 } |
|
2015 return(NULL); |
|
2016 } |
|
2017 |
|
2018 /** |
|
2019 * xmlXPtrRangeFunction: |
|
2020 * @param ctxt the XPointer Parser context |
|
2021 * @param nargs the number of args |
|
2022 * |
|
2023 * Function implementing the range() function 5.4.3 |
|
2024 * location-set range(location-set ) |
|
2025 * |
|
2026 * The range function returns ranges covering the locations in |
|
2027 * the argument location-set. For each location x in the argument |
|
2028 * location-set, a range location representing the covering range of |
|
2029 * x is added to the result location-set. |
|
2030 */ |
|
2031 void |
|
2032 xmlXPtrRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
2033 int i; |
|
2034 xmlXPathObjectPtr set; |
|
2035 xmlLocationSetPtr oldset; |
|
2036 xmlLocationSetPtr newset; |
|
2037 |
|
2038 CHECK_ARITY(1); |
|
2039 if ((ctxt->value == NULL) || |
|
2040 ((ctxt->value->type != XPATH_LOCATIONSET) && |
|
2041 (ctxt->value->type != XPATH_NODESET))) |
|
2042 XP_ERROR(XPATH_INVALID_TYPE) |
|
2043 |
|
2044 set = valuePop(ctxt); |
|
2045 if (set->type == XPATH_NODESET) { |
|
2046 xmlXPathObjectPtr tmp; |
|
2047 |
|
2048 /* |
|
2049 * First convert to a location set |
|
2050 */ |
|
2051 tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); |
|
2052 xmlXPathFreeObject(set); |
|
2053 set = tmp; |
|
2054 } |
|
2055 oldset = (xmlLocationSetPtr) set->user; |
|
2056 |
|
2057 /* |
|
2058 * The loop is to compute the covering range for each item and add it |
|
2059 */ |
|
2060 newset = xmlXPtrLocationSetCreate(NULL); |
|
2061 for (i = 0;i < oldset->locNr;i++) { |
|
2062 xmlXPtrLocationSetAdd(newset, |
|
2063 xmlXPtrCoveringRange(ctxt, oldset->locTab[i])); |
|
2064 } |
|
2065 |
|
2066 /* |
|
2067 * Save the new value and cleanup |
|
2068 */ |
|
2069 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
2070 xmlXPathFreeObject(set); |
|
2071 } |
|
2072 |
|
2073 /** |
|
2074 * xmlXPtrInsideRange: |
|
2075 * @param ctxt the XPointer Parser context |
|
2076 * @param loc the location for which the inside range must be computed |
|
2077 * |
|
2078 * A inside range is a range described in the range-inside() description |
|
2079 * |
|
2080 * Returns a new location or NULL in case of error |
|
2081 */ |
|
2082 static xmlXPathObjectPtr |
|
2083 xmlXPtrInsideRange(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr loc) { |
|
2084 if (loc == NULL) |
|
2085 return(NULL); |
|
2086 if ((ctxt == NULL) || (ctxt->context == NULL) || |
|
2087 (ctxt->context->doc == NULL)) |
|
2088 return(NULL); |
|
2089 switch (loc->type) { |
|
2090 case XPATH_POINT: { |
|
2091 xmlNodePtr node = (xmlNodePtr) loc->user; |
|
2092 switch (node->type) { |
|
2093 case XML_PI_NODE: |
|
2094 case XML_COMMENT_NODE: |
|
2095 case XML_TEXT_NODE: |
|
2096 case XML_CDATA_SECTION_NODE: { |
|
2097 if (node->content == NULL) { |
|
2098 return(xmlXPtrNewRange(node, 0, node, 0)); |
|
2099 } else { |
|
2100 return(xmlXPtrNewRange(node, 0, node, |
|
2101 xmlStrlen(node->content))); |
|
2102 } |
|
2103 } |
|
2104 case XML_ATTRIBUTE_NODE: |
|
2105 case XML_ELEMENT_NODE: |
|
2106 case XML_ENTITY_REF_NODE: |
|
2107 case XML_DOCUMENT_NODE: |
|
2108 case XML_NOTATION_NODE: |
|
2109 case XML_HTML_DOCUMENT_NODE: { |
|
2110 return(xmlXPtrNewRange(node, 0, node, |
|
2111 xmlXPtrGetArity(node))); |
|
2112 } |
|
2113 default: |
|
2114 break; |
|
2115 } |
|
2116 return(NULL); |
|
2117 } |
|
2118 case XPATH_RANGE: { |
|
2119 xmlNodePtr node = (xmlNodePtr) loc->user; |
|
2120 if (loc->user2 != NULL) { |
|
2121 return(xmlXPtrNewRange(node, loc->index, |
|
2122 loc->user2, loc->index2)); |
|
2123 } else { |
|
2124 switch (node->type) { |
|
2125 case XML_PI_NODE: |
|
2126 case XML_COMMENT_NODE: |
|
2127 case XML_TEXT_NODE: |
|
2128 case XML_CDATA_SECTION_NODE: { |
|
2129 if (node->content == NULL) { |
|
2130 return(xmlXPtrNewRange(node, 0, node, 0)); |
|
2131 } else { |
|
2132 return(xmlXPtrNewRange(node, 0, node, |
|
2133 xmlStrlen(node->content))); |
|
2134 } |
|
2135 } |
|
2136 case XML_ATTRIBUTE_NODE: |
|
2137 case XML_ELEMENT_NODE: |
|
2138 case XML_ENTITY_REF_NODE: |
|
2139 case XML_DOCUMENT_NODE: |
|
2140 case XML_NOTATION_NODE: |
|
2141 case XML_HTML_DOCUMENT_NODE: { |
|
2142 return(xmlXPtrNewRange(node, 0, node, |
|
2143 xmlXPtrGetArity(node))); |
|
2144 } |
|
2145 default: |
|
2146 break; |
|
2147 } |
|
2148 return(NULL); |
|
2149 } |
|
2150 } |
|
2151 default: |
|
2152 TODO /* missed one case ??? */ |
|
2153 } |
|
2154 return(NULL); |
|
2155 } |
|
2156 |
|
2157 /** |
|
2158 * xmlXPtrRangeInsideFunction: |
|
2159 * @param ctxt the XPointer Parser context |
|
2160 * @param nargs the number of args |
|
2161 * |
|
2162 * Function implementing the range-inside() function 5.4.3 |
|
2163 * location-set range-inside(location-set ) |
|
2164 * |
|
2165 * The range-inside function returns ranges covering the contents of |
|
2166 * the locations in the argument location-set. For each location x in |
|
2167 * the argument location-set, a range location is added to the result |
|
2168 * location-set. If x is a range location, then x is added to the |
|
2169 * result location-set. If x is not a range location, then x is used |
|
2170 * as the container location of the start and end points of the range |
|
2171 * location to be added; the index of the start point of the range is |
|
2172 * zero; if the end point is a character point then its index is the |
|
2173 * length of the string-value of x, and otherwise is the number of |
|
2174 * location children of x. |
|
2175 * |
|
2176 */ |
|
2177 void |
|
2178 xmlXPtrRangeInsideFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
2179 int i; |
|
2180 xmlXPathObjectPtr set; |
|
2181 xmlLocationSetPtr oldset; |
|
2182 xmlLocationSetPtr newset; |
|
2183 |
|
2184 CHECK_ARITY(1); |
|
2185 if ((ctxt->value == NULL) || |
|
2186 ((ctxt->value->type != XPATH_LOCATIONSET) && |
|
2187 (ctxt->value->type != XPATH_NODESET))) |
|
2188 XP_ERROR(XPATH_INVALID_TYPE) |
|
2189 |
|
2190 set = valuePop(ctxt); |
|
2191 if (set->type == XPATH_NODESET) { |
|
2192 xmlXPathObjectPtr tmp; |
|
2193 |
|
2194 /* |
|
2195 * First convert to a location set |
|
2196 */ |
|
2197 tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); |
|
2198 xmlXPathFreeObject(set); |
|
2199 set = tmp; |
|
2200 } |
|
2201 oldset = (xmlLocationSetPtr) set->user; |
|
2202 |
|
2203 /* |
|
2204 * The loop is to compute the covering range for each item and add it |
|
2205 */ |
|
2206 newset = xmlXPtrLocationSetCreate(NULL); |
|
2207 for (i = 0;i < oldset->locNr;i++) { |
|
2208 xmlXPtrLocationSetAdd(newset, |
|
2209 xmlXPtrInsideRange(ctxt, oldset->locTab[i])); |
|
2210 } |
|
2211 |
|
2212 /* |
|
2213 * Save the new value and cleanup |
|
2214 */ |
|
2215 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
2216 xmlXPathFreeObject(set); |
|
2217 } |
|
2218 |
|
2219 /** |
|
2220 * xmlXPtrRangeToFunction: |
|
2221 * @param ctxt the XPointer Parser context |
|
2222 * @param nargs the number of args |
|
2223 * |
|
2224 * Implement the range-to() XPointer function |
|
2225 */ |
|
2226 XMLPUBFUNEXPORT void |
|
2227 xmlXPtrRangeToFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
2228 xmlXPathObjectPtr range; |
|
2229 const xmlChar *cur; |
|
2230 xmlXPathObjectPtr res, obj; |
|
2231 xmlXPathObjectPtr tmp; |
|
2232 xmlLocationSetPtr newset = NULL; |
|
2233 xmlNodeSetPtr oldset; |
|
2234 int i; |
|
2235 |
|
2236 CHECK_ARITY(1); |
|
2237 /* |
|
2238 * Save the expression pointer since we will have to evaluate |
|
2239 * it multiple times. Initialize the new set. |
|
2240 */ |
|
2241 CHECK_TYPE(XPATH_NODESET); |
|
2242 obj = valuePop(ctxt); |
|
2243 oldset = obj->nodesetval; |
|
2244 ctxt->context->node = NULL; |
|
2245 |
|
2246 cur = ctxt->cur; |
|
2247 newset = xmlXPtrLocationSetCreate(NULL); |
|
2248 |
|
2249 for (i = 0; i < oldset->nodeNr; i++) { |
|
2250 ctxt->cur = cur; |
|
2251 |
|
2252 /* |
|
2253 * Run the evaluation with a node list made of a single item |
|
2254 * in the nodeset. |
|
2255 */ |
|
2256 ctxt->context->node = oldset->nodeTab[i]; |
|
2257 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
2258 valuePush(ctxt, tmp); |
|
2259 |
|
2260 xmlXPathEvalExpr(ctxt); |
|
2261 CHECK_ERROR; |
|
2262 |
|
2263 /* |
|
2264 * The result of the evaluation need to be tested to |
|
2265 * decided whether the filter succeeded or not |
|
2266 */ |
|
2267 res = valuePop(ctxt); |
|
2268 range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res); |
|
2269 if (range != NULL) { |
|
2270 xmlXPtrLocationSetAdd(newset, range); |
|
2271 } |
|
2272 |
|
2273 /* |
|
2274 * Cleanup |
|
2275 */ |
|
2276 if (res != NULL) |
|
2277 xmlXPathFreeObject(res); |
|
2278 if (ctxt->value == tmp) { |
|
2279 res = valuePop(ctxt); |
|
2280 xmlXPathFreeObject(res); |
|
2281 } |
|
2282 |
|
2283 ctxt->context->node = NULL; |
|
2284 } |
|
2285 |
|
2286 /* |
|
2287 * The result is used as the new evaluation set. |
|
2288 */ |
|
2289 xmlXPathFreeObject(obj); |
|
2290 ctxt->context->node = NULL; |
|
2291 ctxt->context->contextSize = -1; |
|
2292 ctxt->context->proximityPosition = -1; |
|
2293 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
2294 } |
|
2295 |
|
2296 /** |
|
2297 * xmlXPtrAdvanceNode: |
|
2298 * @param cur the node |
|
2299 * @param level incremented/decremented to show level in tree |
|
2300 * |
|
2301 * Advance to the next element or text node in document order |
|
2302 * |
|
2303 * |
|
2304 * Returns -1 in case of failure, 0 otherwise |
|
2305 */ |
|
2306 xmlNodePtr |
|
2307 xmlXPtrAdvanceNode(xmlNodePtr cur, int *level) { |
|
2308 next: |
|
2309 if (cur == NULL) |
|
2310 return(NULL); |
|
2311 if (cur->children != NULL) { |
|
2312 cur = cur->children ; |
|
2313 if (level != NULL) |
|
2314 (*level)++; |
|
2315 goto found; |
|
2316 } |
|
2317 skip: /* This label should only be needed if something is wrong! */ |
|
2318 if (cur->next != NULL) { |
|
2319 cur = cur->next; |
|
2320 goto found; |
|
2321 } |
|
2322 do { |
|
2323 cur = cur->parent; |
|
2324 if (level != NULL) |
|
2325 (*level)--; |
|
2326 if (cur == NULL) return(NULL); |
|
2327 if (cur->next != NULL) { |
|
2328 cur = cur->next; |
|
2329 goto found; |
|
2330 } |
|
2331 } while (cur != NULL); |
|
2332 |
|
2333 found: |
|
2334 if ((cur->type != XML_ELEMENT_NODE) && |
|
2335 (cur->type != XML_TEXT_NODE) && |
|
2336 (cur->type != XML_DOCUMENT_NODE) && |
|
2337 (cur->type != XML_HTML_DOCUMENT_NODE) && |
|
2338 (cur->type != XML_CDATA_SECTION_NODE)) { |
|
2339 if (cur->type == XML_ENTITY_REF_NODE) { /* Shouldn't happen */ |
|
2340 TODO |
|
2341 goto skip; |
|
2342 } |
|
2343 goto next; |
|
2344 } |
|
2345 return(cur); |
|
2346 } |
|
2347 |
|
2348 /** |
|
2349 * xmlXPtrAdvanceChar: |
|
2350 * @param node the node |
|
2351 * @param indx the indx |
|
2352 * @param bytes the number of bytes |
|
2353 * |
|
2354 * Advance a point of the associated number of bytes (not UTF8 chars) |
|
2355 * |
|
2356 * Returns -1 in case of failure, 0 otherwise |
|
2357 */ |
|
2358 static int |
|
2359 xmlXPtrAdvanceChar(xmlNodePtr *node, int *indx, int bytes) { |
|
2360 xmlNodePtr cur; |
|
2361 int pos; |
|
2362 int len; |
|
2363 |
|
2364 if ((node == NULL) || (indx == NULL)) |
|
2365 return(-1); |
|
2366 cur = *node; |
|
2367 if (cur == NULL) |
|
2368 return(-1); |
|
2369 pos = *indx; |
|
2370 |
|
2371 while (bytes >= 0) { |
|
2372 /* |
|
2373 * First position to the beginning of the first text node |
|
2374 * corresponding to this point |
|
2375 */ |
|
2376 while ((cur != NULL) && |
|
2377 ((cur->type == XML_ELEMENT_NODE) || |
|
2378 (cur->type == XML_DOCUMENT_NODE) || |
|
2379 (cur->type == XML_HTML_DOCUMENT_NODE))) { |
|
2380 if (pos > 0) { |
|
2381 cur = xmlXPtrGetNthChild(cur, pos); |
|
2382 pos = 0; |
|
2383 } else { |
|
2384 cur = xmlXPtrAdvanceNode(cur, NULL); |
|
2385 pos = 0; |
|
2386 } |
|
2387 } |
|
2388 |
|
2389 if (cur == NULL) { |
|
2390 *node = NULL; |
|
2391 *indx = 0; |
|
2392 return(-1); |
|
2393 } |
|
2394 |
|
2395 /* |
|
2396 * if there is no move needed return the current value. |
|
2397 */ |
|
2398 if (pos == 0) pos = 1; |
|
2399 if (bytes == 0) { |
|
2400 *node = cur; |
|
2401 *indx = pos; |
|
2402 return(0); |
|
2403 } |
|
2404 /* |
|
2405 * We should have a text (or cdata) node ... |
|
2406 */ |
|
2407 len = 0; |
|
2408 if ((cur->type != XML_ELEMENT_NODE) && |
|
2409 (cur->content != NULL)) { |
|
2410 len = xmlStrlen(cur->content); |
|
2411 } |
|
2412 if (pos > len) { |
|
2413 /* Strange, the indx in the text node is greater than it's len */ |
|
2414 STRANGE |
|
2415 pos = len; |
|
2416 } |
|
2417 if (pos + bytes >= len) { |
|
2418 bytes -= (len - pos); |
|
2419 cur = xmlXPtrAdvanceNode(cur, NULL); |
|
2420 cur = 0; |
|
2421 } else if (pos + bytes < len) { |
|
2422 pos += bytes; |
|
2423 *node = cur; |
|
2424 *indx = pos; |
|
2425 return(0); |
|
2426 } |
|
2427 } |
|
2428 return(-1); |
|
2429 } |
|
2430 |
|
2431 /** |
|
2432 * xmlXPtrMatchString: |
|
2433 * @param string the string to search |
|
2434 * @param start the start textnode |
|
2435 * @param startindex the start index |
|
2436 * @param end the end textnode IN/OUT |
|
2437 * @param endindex the end index IN/OUT |
|
2438 * |
|
2439 * Check whether the document contains string at the position |
|
2440 * (start, startindex) and limited by the (end, endindex) point |
|
2441 * |
|
2442 * Returns -1 in case of failure, 0 if not found, 1 if found in which case |
|
2443 * (start, startindex) will indicate the position of the beginning |
|
2444 * of the range and (end, endindex) will indicate the end |
|
2445 * of the range |
|
2446 */ |
|
2447 static int |
|
2448 xmlXPtrMatchString(const xmlChar *string, xmlNodePtr start, int startindex, |
|
2449 xmlNodePtr *end, int *endindex) { |
|
2450 xmlNodePtr cur; |
|
2451 int pos; /* 0 based */ |
|
2452 int len; /* in bytes */ |
|
2453 int stringlen; /* in bytes */ |
|
2454 int match; |
|
2455 |
|
2456 if (string == NULL) |
|
2457 return(-1); |
|
2458 if (start == NULL) |
|
2459 return(-1); |
|
2460 if ((end == NULL) || (endindex == NULL)) |
|
2461 return(-1); |
|
2462 cur = start; |
|
2463 if (cur == NULL) |
|
2464 return(-1); |
|
2465 pos = startindex - 1; |
|
2466 stringlen = xmlStrlen(string); |
|
2467 |
|
2468 while (stringlen > 0) { |
|
2469 if ((cur == *end) && (pos + stringlen > *endindex)) |
|
2470 return(0); |
|
2471 |
|
2472 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { |
|
2473 len = xmlStrlen(cur->content); |
|
2474 if (len >= pos + stringlen) { |
|
2475 match = (!xmlStrncmp(&cur->content[pos], string, stringlen)); |
|
2476 if (match) { |
|
2477 #ifdef DEBUG_RANGES |
|
2478 xmlGenericError(xmlGenericErrorContext, |
|
2479 "found range %d bytes at index %d of ->", |
|
2480 stringlen, pos + 1); |
|
2481 xmlDebugDumpString(stdout, cur->content); |
|
2482 xmlGenericError(xmlGenericErrorContext, "\n"); |
|
2483 #endif |
|
2484 *end = cur; |
|
2485 *endindex = pos + stringlen; |
|
2486 return(1); |
|
2487 } else { |
|
2488 return(0); |
|
2489 } |
|
2490 } else { |
|
2491 int sub = len - pos; |
|
2492 match = (!xmlStrncmp(&cur->content[pos], string, sub)); |
|
2493 if (match) { |
|
2494 #ifdef DEBUG_RANGES |
|
2495 xmlGenericError(xmlGenericErrorContext, |
|
2496 "found subrange %d bytes at index %d of ->", |
|
2497 sub, pos + 1); |
|
2498 xmlDebugDumpString(stdout, cur->content); |
|
2499 xmlGenericError(xmlGenericErrorContext, "\n"); |
|
2500 #endif |
|
2501 string = &string[sub]; |
|
2502 stringlen -= sub; |
|
2503 } else { |
|
2504 return(0); |
|
2505 } |
|
2506 } |
|
2507 } |
|
2508 cur = xmlXPtrAdvanceNode(cur, NULL); |
|
2509 if (cur == NULL) |
|
2510 return(0); |
|
2511 pos = 0; |
|
2512 } |
|
2513 return(1); |
|
2514 } |
|
2515 |
|
2516 /** |
|
2517 * xmlXPtrSearchString: |
|
2518 * @param string the string to search |
|
2519 * @param start the start textnode IN/OUT |
|
2520 * @param startindex the start index IN/OUT |
|
2521 * @param end the end textnode |
|
2522 * @param endindex the end index |
|
2523 * |
|
2524 * Search the next occurrence of string within the document content |
|
2525 * until the (end, endindex) point is reached |
|
2526 * |
|
2527 * Returns -1 in case of failure, 0 if not found, 1 if found in which case |
|
2528 * (start, startindex) will indicate the position of the beginning |
|
2529 * of the range and (end, endindex) will indicate the end |
|
2530 * of the range |
|
2531 */ |
|
2532 static int |
|
2533 xmlXPtrSearchString(const xmlChar *string, xmlNodePtr *start, int *startindex, |
|
2534 xmlNodePtr *end, int *endindex) { |
|
2535 xmlNodePtr cur; |
|
2536 const xmlChar *str; |
|
2537 int pos; /* 0 based */ |
|
2538 int len; /* in bytes */ |
|
2539 xmlChar first; |
|
2540 |
|
2541 if (string == NULL) |
|
2542 return(-1); |
|
2543 if ((start == NULL) || (startindex == NULL)) |
|
2544 return(-1); |
|
2545 if ((end == NULL) || (endindex == NULL)) |
|
2546 return(-1); |
|
2547 cur = *start; |
|
2548 if (cur == NULL) |
|
2549 return(-1); |
|
2550 pos = *startindex - 1; |
|
2551 first = string[0]; |
|
2552 |
|
2553 while (cur != NULL) { |
|
2554 if ((cur->type != XML_ELEMENT_NODE) && (cur->content != NULL)) { |
|
2555 len = xmlStrlen(cur->content); |
|
2556 while (pos <= len) { |
|
2557 if (first != 0) { |
|
2558 str = xmlStrchr(&cur->content[pos], first); |
|
2559 if (str != NULL) { |
|
2560 pos = (str - (xmlChar *)(cur->content)); |
|
2561 #ifdef DEBUG_RANGES |
|
2562 xmlGenericError(xmlGenericErrorContext, |
|
2563 "found '%c' at index %d of ->", |
|
2564 first, pos + 1); |
|
2565 xmlDebugDumpString(stdout, cur->content); |
|
2566 xmlGenericError(xmlGenericErrorContext, "\n"); |
|
2567 #endif |
|
2568 if (xmlXPtrMatchString(string, cur, pos + 1, |
|
2569 end, endindex)) { |
|
2570 *start = cur; |
|
2571 *startindex = pos + 1; |
|
2572 return(1); |
|
2573 } |
|
2574 pos++; |
|
2575 } else { |
|
2576 pos = len + 1; |
|
2577 } |
|
2578 } else { |
|
2579 /* |
|
2580 * An empty string is considered to match before each |
|
2581 * character of the string-value and after the final |
|
2582 * character. |
|
2583 */ |
|
2584 #ifdef DEBUG_RANGES |
|
2585 xmlGenericError(xmlGenericErrorContext, |
|
2586 "found '' at index %d of ->", |
|
2587 pos + 1); |
|
2588 xmlDebugDumpString(stdout, cur->content); |
|
2589 xmlGenericError(xmlGenericErrorContext, "\n"); |
|
2590 #endif |
|
2591 *start = cur; |
|
2592 *startindex = pos + 1; |
|
2593 *end = cur; |
|
2594 *endindex = pos + 1; |
|
2595 return(1); |
|
2596 } |
|
2597 } |
|
2598 } |
|
2599 if ((cur == *end) && (pos >= *endindex)) |
|
2600 return(0); |
|
2601 cur = xmlXPtrAdvanceNode(cur, NULL); |
|
2602 if (cur == NULL) |
|
2603 return(0); |
|
2604 pos = 1; |
|
2605 } |
|
2606 return(0); |
|
2607 } |
|
2608 |
|
2609 /** |
|
2610 * xmlXPtrGetLastChar: |
|
2611 * @param node the node |
|
2612 * @param index the index |
|
2613 * |
|
2614 * Computes the point coordinates of the last char of this point |
|
2615 * |
|
2616 * Returns -1 in case of failure, 0 otherwise |
|
2617 */ |
|
2618 static int |
|
2619 xmlXPtrGetLastChar(xmlNodePtr *node, int *indx) { |
|
2620 xmlNodePtr cur; |
|
2621 int pos, len = 0; |
|
2622 |
|
2623 if ((node == NULL) || (indx == NULL)) |
|
2624 return(-1); |
|
2625 cur = *node; |
|
2626 pos = *indx; |
|
2627 |
|
2628 if (cur == NULL) |
|
2629 return(-1); |
|
2630 |
|
2631 if ((cur->type == XML_ELEMENT_NODE) || |
|
2632 (cur->type == XML_DOCUMENT_NODE) || |
|
2633 (cur->type == XML_HTML_DOCUMENT_NODE)) { |
|
2634 if (pos > 0) { |
|
2635 cur = xmlXPtrGetNthChild(cur, pos); |
|
2636 pos = 0; |
|
2637 } |
|
2638 } |
|
2639 while (cur != NULL) { |
|
2640 if (cur->last != NULL) |
|
2641 cur = cur->last; |
|
2642 else if ((cur->type != XML_ELEMENT_NODE) && |
|
2643 (cur->content != NULL)) { |
|
2644 len = xmlStrlen(cur->content); |
|
2645 break; |
|
2646 } else { |
|
2647 return(-1); |
|
2648 } |
|
2649 } |
|
2650 if (cur == NULL) |
|
2651 return(-1); |
|
2652 *node = cur; |
|
2653 *indx = len; |
|
2654 return(0); |
|
2655 } |
|
2656 |
|
2657 /** |
|
2658 * xmlXPtrGetStartPoint: |
|
2659 * @param obj an range |
|
2660 * @param node the resulting node |
|
2661 * @param indx the resulting index |
|
2662 * |
|
2663 * read the object and return the start point coordinates. |
|
2664 * |
|
2665 * Returns -1 in case of failure, 0 otherwise |
|
2666 */ |
|
2667 static int |
|
2668 xmlXPtrGetStartPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) { |
|
2669 if ((obj == NULL) || (node == NULL) || (indx == NULL)) |
|
2670 return(-1); |
|
2671 |
|
2672 switch (obj->type) { |
|
2673 case XPATH_POINT: |
|
2674 *node = obj->user; |
|
2675 if (obj->index <= 0) |
|
2676 *indx = 0; |
|
2677 else |
|
2678 *indx = obj->index; |
|
2679 return(0); |
|
2680 case XPATH_RANGE: |
|
2681 *node = obj->user; |
|
2682 if (obj->index <= 0) |
|
2683 *indx = 0; |
|
2684 else |
|
2685 *indx = obj->index; |
|
2686 return(0); |
|
2687 default: |
|
2688 break; |
|
2689 } |
|
2690 return(-1); |
|
2691 } |
|
2692 |
|
2693 /** |
|
2694 * xmlXPtrGetEndPoint: |
|
2695 * @param obj an range |
|
2696 * @param node the resulting node |
|
2697 * @param indx the resulting indx |
|
2698 * |
|
2699 * read the object and return the end point coordinates. |
|
2700 * |
|
2701 * Returns -1 in case of failure, 0 otherwise |
|
2702 */ |
|
2703 static int |
|
2704 xmlXPtrGetEndPoint(xmlXPathObjectPtr obj, xmlNodePtr *node, int *indx) { |
|
2705 if ((obj == NULL) || (node == NULL) || (indx == NULL)) |
|
2706 return(-1); |
|
2707 |
|
2708 switch (obj->type) { |
|
2709 case XPATH_POINT: |
|
2710 *node = obj->user; |
|
2711 if (obj->index <= 0) |
|
2712 *indx = 0; |
|
2713 else |
|
2714 *indx = obj->index; |
|
2715 return(0); |
|
2716 case XPATH_RANGE: |
|
2717 *node = obj->user; |
|
2718 if (obj->index <= 0) |
|
2719 *indx = 0; |
|
2720 else |
|
2721 *indx = obj->index; |
|
2722 return(0); |
|
2723 default: |
|
2724 break; |
|
2725 } |
|
2726 return(-1); |
|
2727 } |
|
2728 |
|
2729 /** |
|
2730 * xmlXPtrStringRangeFunction: |
|
2731 * @param ctxt the XPointer Parser context |
|
2732 * @param nargs the number of args |
|
2733 * |
|
2734 * Function implementing the string-range() function |
|
2735 * range as described in 5.4.2 |
|
2736 * |
|
2737 * ------------------------------ |
|
2738 * [Definition: For each location in the location-set argument, |
|
2739 * string-range returns a set of string ranges, a set of substrings in a |
|
2740 * string. Specifically, the string-value of the location is searched for |
|
2741 * substrings that match the string argument, and the resulting location-set |
|
2742 * will contain a range location for each non-overlapping match.] |
|
2743 * An empty string is considered to match before each character of the |
|
2744 * string-value and after the final character. Whitespace in a string |
|
2745 * is matched literally, with no normalization except that provided by |
|
2746 * XML for line ends. The third argument gives the position of the first |
|
2747 * character to be in the resulting range, relative to the start of the |
|
2748 * match. The default value is 1, which makes the range start immediately |
|
2749 * before the first character of the matched string. The fourth argument |
|
2750 * gives the number of characters in the range; the default is that the |
|
2751 * range extends to the end of the matched string. |
|
2752 * |
|
2753 * Element boundaries, as well as entire embedded nodes such as processing |
|
2754 * instructions and comments, are ignored as defined in [XPath]. |
|
2755 * |
|
2756 * If the string in the second argument is not found in the string-value |
|
2757 * of the location, or if a value in the third or fourth argument indicates |
|
2758 * a string that is beyond the beginning or end of the document, the |
|
2759 * expression fails. |
|
2760 * |
|
2761 * The points of the range-locations in the returned location-set will |
|
2762 * all be character points. |
|
2763 * ------------------------------ |
|
2764 */ |
|
2765 void |
|
2766 xmlXPtrStringRangeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
|
2767 int i, startindex, fendindex; |
|
2768 int endindex = 0; |
|
2769 xmlNodePtr start, end, fend; |
|
2770 xmlXPathObjectPtr set; |
|
2771 xmlLocationSetPtr oldset; |
|
2772 xmlLocationSetPtr newset; |
|
2773 xmlXPathObjectPtr string; |
|
2774 xmlXPathObjectPtr position = NULL; |
|
2775 xmlXPathObjectPtr number = NULL; |
|
2776 int found, pos = 0, num = 0; |
|
2777 |
|
2778 /* |
|
2779 * Grab the arguments |
|
2780 */ |
|
2781 if ((nargs < 2) || (nargs > 4)) |
|
2782 XP_ERROR(XPATH_INVALID_ARITY); |
|
2783 |
|
2784 if (nargs >= 4) { |
|
2785 CHECK_TYPE(XPATH_NUMBER); |
|
2786 number = valuePop(ctxt); |
|
2787 if (number != NULL) |
|
2788 num = (int) number->floatval; |
|
2789 } |
|
2790 if (nargs >= 3) { |
|
2791 CHECK_TYPE(XPATH_NUMBER); |
|
2792 position = valuePop(ctxt); |
|
2793 if (position != NULL) |
|
2794 pos = (int) position->floatval; |
|
2795 } |
|
2796 CHECK_TYPE(XPATH_STRING); |
|
2797 string = valuePop(ctxt); |
|
2798 if ((ctxt->value == NULL) || |
|
2799 ((ctxt->value->type != XPATH_LOCATIONSET) && |
|
2800 (ctxt->value->type != XPATH_NODESET))) |
|
2801 XP_ERROR(XPATH_INVALID_TYPE) |
|
2802 |
|
2803 set = valuePop(ctxt); |
|
2804 newset = xmlXPtrLocationSetCreate(NULL); |
|
2805 if (set->nodesetval == NULL) { |
|
2806 goto error; |
|
2807 } |
|
2808 if (set->type == XPATH_NODESET) { |
|
2809 xmlXPathObjectPtr tmp; |
|
2810 |
|
2811 /* |
|
2812 * First convert to a location set |
|
2813 */ |
|
2814 tmp = xmlXPtrNewLocationSetNodeSet(set->nodesetval); |
|
2815 xmlXPathFreeObject(set); |
|
2816 set = tmp; |
|
2817 } |
|
2818 oldset = (xmlLocationSetPtr) set->user; |
|
2819 |
|
2820 /* |
|
2821 * The loop is to search for each element in the location set |
|
2822 * the list of location set corresponding to that search |
|
2823 */ |
|
2824 for (i = 0;i < oldset->locNr;i++) { |
|
2825 #ifdef DEBUG_RANGES |
|
2826 xmlXPathDebugDumpObject(stdout, oldset->locTab[i], 0); |
|
2827 #endif |
|
2828 |
|
2829 xmlXPtrGetStartPoint(oldset->locTab[i], &start, &startindex); |
|
2830 xmlXPtrGetEndPoint(oldset->locTab[i], &end, &endindex); |
|
2831 xmlXPtrAdvanceChar(&start, &startindex, 0); |
|
2832 xmlXPtrGetLastChar(&end, &endindex); |
|
2833 |
|
2834 #ifdef DEBUG_RANGES |
|
2835 xmlGenericError(xmlGenericErrorContext, |
|
2836 "from index %d of ->", startindex); |
|
2837 xmlDebugDumpString(stdout, start->content); |
|
2838 xmlGenericError(xmlGenericErrorContext, "\n"); |
|
2839 xmlGenericError(xmlGenericErrorContext, |
|
2840 "to index %d of ->", endindex); |
|
2841 xmlDebugDumpString(stdout, end->content); |
|
2842 xmlGenericError(xmlGenericErrorContext, "\n"); |
|
2843 #endif |
|
2844 do { |
|
2845 fend = end; |
|
2846 fendindex = endindex; |
|
2847 found = xmlXPtrSearchString(string->stringval, &start, &startindex, |
|
2848 &fend, &fendindex); |
|
2849 if (found == 1) { |
|
2850 if (position == NULL) { |
|
2851 xmlXPtrLocationSetAdd(newset, |
|
2852 xmlXPtrNewRange(start, startindex, fend, fendindex)); |
|
2853 } else if (xmlXPtrAdvanceChar(&start, &startindex, |
|
2854 pos - 1) == 0) { |
|
2855 if ((number != NULL) && (num > 0)) { |
|
2856 int rindx; |
|
2857 xmlNodePtr rend; |
|
2858 rend = start; |
|
2859 rindx = startindex - 1; |
|
2860 if (xmlXPtrAdvanceChar(&rend, &rindx, |
|
2861 num) == 0) { |
|
2862 xmlXPtrLocationSetAdd(newset, |
|
2863 xmlXPtrNewRange(start, startindex, |
|
2864 rend, rindx)); |
|
2865 } |
|
2866 } else if ((number != NULL) && (num <= 0)) { |
|
2867 xmlXPtrLocationSetAdd(newset, |
|
2868 xmlXPtrNewRange(start, startindex, |
|
2869 start, startindex)); |
|
2870 } else { |
|
2871 xmlXPtrLocationSetAdd(newset, |
|
2872 xmlXPtrNewRange(start, startindex, |
|
2873 fend, fendindex)); |
|
2874 } |
|
2875 } |
|
2876 start = fend; |
|
2877 startindex = fendindex; |
|
2878 if (string->stringval[0] == 0) |
|
2879 startindex++; |
|
2880 } |
|
2881 } while (found == 1); |
|
2882 } |
|
2883 |
|
2884 /* |
|
2885 * Save the new value and cleanup |
|
2886 */ |
|
2887 error: |
|
2888 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
2889 xmlXPathFreeObject(set); |
|
2890 xmlXPathFreeObject(string); |
|
2891 if (position) xmlXPathFreeObject(position); |
|
2892 if (number) xmlXPathFreeObject(number); |
|
2893 } |
|
2894 |
|
2895 /** |
|
2896 * xmlXPtrEvalRangePredicate: |
|
2897 * @param ctxt the XPointer Parser context |
|
2898 * |
|
2899 * [8] Predicate ::= '[' PredicateExpr ']' |
|
2900 * [9] PredicateExpr ::= Expr |
|
2901 * |
|
2902 * Evaluate a predicate as in xmlXPathEvalPredicate() but for |
|
2903 * a Location Set instead of a node set |
|
2904 */ |
|
2905 XMLPUBFUNEXPORT void |
|
2906 xmlXPtrEvalRangePredicate(xmlXPathParserContextPtr ctxt) { |
|
2907 const xmlChar *cur; |
|
2908 xmlXPathObjectPtr res; |
|
2909 xmlXPathObjectPtr obj, tmp; |
|
2910 xmlLocationSetPtr newset = NULL; |
|
2911 xmlLocationSetPtr oldset; |
|
2912 int i; |
|
2913 |
|
2914 SKIP_BLANKS; |
|
2915 if (CUR != '[') { |
|
2916 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); |
|
2917 } |
|
2918 NEXT; |
|
2919 SKIP_BLANKS; |
|
2920 |
|
2921 /* |
|
2922 * Extract the old set, and then evaluate the result of the |
|
2923 * expression for all the element in the set. use it to grow |
|
2924 * up a new set. |
|
2925 */ |
|
2926 CHECK_TYPE(XPATH_LOCATIONSET); |
|
2927 obj = valuePop(ctxt); |
|
2928 oldset = obj->user; |
|
2929 ctxt->context->node = NULL; |
|
2930 |
|
2931 if ((oldset == NULL) || (oldset->locNr == 0)) { |
|
2932 ctxt->context->contextSize = 0; |
|
2933 ctxt->context->proximityPosition = 0; |
|
2934 xmlXPathEvalExpr(ctxt); |
|
2935 res = valuePop(ctxt); |
|
2936 if (res != NULL) |
|
2937 xmlXPathFreeObject(res); |
|
2938 valuePush(ctxt, obj); |
|
2939 CHECK_ERROR; |
|
2940 } else { |
|
2941 /* |
|
2942 * Save the expression pointer since we will have to evaluate |
|
2943 * it multiple times. Initialize the new set. |
|
2944 */ |
|
2945 cur = ctxt->cur; |
|
2946 newset = xmlXPtrLocationSetCreate(NULL); |
|
2947 |
|
2948 for (i = 0; i < oldset->locNr; i++) { |
|
2949 ctxt->cur = cur; |
|
2950 |
|
2951 /* |
|
2952 * Run the evaluation with a node list made of a single item |
|
2953 * in the nodeset. |
|
2954 */ |
|
2955 ctxt->context->node = oldset->locTab[i]->user; |
|
2956 tmp = xmlXPathNewNodeSet(ctxt->context->node); |
|
2957 valuePush(ctxt, tmp); |
|
2958 ctxt->context->contextSize = oldset->locNr; |
|
2959 ctxt->context->proximityPosition = i + 1; |
|
2960 |
|
2961 xmlXPathEvalExpr(ctxt); |
|
2962 CHECK_ERROR; |
|
2963 |
|
2964 /* |
|
2965 * The result of the evaluation need to be tested to |
|
2966 * decided whether the filter succeeded or not |
|
2967 */ |
|
2968 res = valuePop(ctxt); |
|
2969 if (xmlXPathEvaluatePredicateResult(ctxt, res)) { |
|
2970 xmlXPtrLocationSetAdd(newset, |
|
2971 xmlXPathObjectCopy(oldset->locTab[i])); |
|
2972 } |
|
2973 |
|
2974 /* |
|
2975 * Cleanup |
|
2976 */ |
|
2977 if (res != NULL) |
|
2978 xmlXPathFreeObject(res); |
|
2979 if (ctxt->value == tmp) { |
|
2980 res = valuePop(ctxt); |
|
2981 xmlXPathFreeObject(res); |
|
2982 } |
|
2983 |
|
2984 ctxt->context->node = NULL; |
|
2985 } |
|
2986 |
|
2987 /* |
|
2988 * The result is used as the new evaluation set. |
|
2989 */ |
|
2990 xmlXPathFreeObject(obj); |
|
2991 ctxt->context->node = NULL; |
|
2992 ctxt->context->contextSize = -1; |
|
2993 ctxt->context->proximityPosition = -1; |
|
2994 valuePush(ctxt, xmlXPtrWrapLocationSet(newset)); |
|
2995 } |
|
2996 if (CUR != ']') { |
|
2997 XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); |
|
2998 } |
|
2999 |
|
3000 NEXT; |
|
3001 SKIP_BLANKS; |
|
3002 } |
|
3003 |
|
3004 #else |
|
3005 #endif |
|
3006 |