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