| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * xinclude.c : Code to implement XInclude processing | |
| 3 * | |
| 4 * World Wide Web Consortium W3C Last Call Working Draft 10 November 2003 | |
| 5 * http://www.w3.org/TR/2003/WD-xinclude-20031110 | |
| 6 * | |
| 7 * See Copyright for the status of this software. | |
| 8 * | |
| 9 * daniel@veillard.com | |
| 10 */ | |
| 11 | |
| 12 #define IN_LIBXML | |
| 13 #include "libxml.h" | |
| 14 | |
| 15 #include <string.h> | |
| 16 #include <libxml/xmlmemory.h> | |
| 17 #include <libxml/tree.h> | |
| 18 #include <libxml/parser.h> | |
| 19 #include <libxml/uri.h> | |
| 20 #include <libxml/xpath.h> | |
| 21 #include <libxml/xpointer.h> | |
| 22 #include <libxml/parserInternals.h> | |
| 23 #include <libxml/xmlerror.h> | |
| 24 #include <libxml/encoding.h> | |
| 25 #include <libxml/globals.h> | |
| 26 | |
| 27 #ifdef LIBXML_XINCLUDE_ENABLED | |
| 28 #include <libxml/xinclude.h> | |
| 29 | |
| 30 #include "buf.h" | |
| 31 | |
| 32 #define XINCLUDE_MAX_DEPTH 40 | |
| 33 | |
| 34 /* #define DEBUG_XINCLUDE */ | |
| 35 #ifdef DEBUG_XINCLUDE | |
| 36 #ifdef LIBXML_DEBUG_ENABLED | |
| 37 #include <libxml/debugXML.h> | |
| 38 #endif | |
| 39 #endif | |
| 40 | |
| 41 /************************************************************************ | |
| 42 * * | |
| 43 * XInclude context handling * | |
| 44 * * | |
| 45 ************************************************************************/ | |
| 46 | |
| 47 /* | |
| 48 * An XInclude context | |
| 49 */ | |
| 50 typedef xmlChar *xmlURL; | |
| 51 | |
| 52 typedef struct _xmlXIncludeRef xmlXIncludeRef; | |
| 53 typedef xmlXIncludeRef *xmlXIncludeRefPtr; | |
| 54 struct _xmlXIncludeRef { | |
| 55 xmlChar *URI; /* the fully resolved resource URL */ | |
| 56 xmlChar *fragment; /* the fragment in the URI */ | |
| 57 xmlDocPtr doc; /* the parsed document */ | |
| 58 xmlNodePtr ref; /* the node making the reference in the source */ | |
| 59 xmlNodePtr inc; /* the included copy */ | |
| 60 int xml; /* xml or txt */ | |
| 61 int count; /* how many refs use that specific doc */ | |
| 62 xmlXPathObjectPtr xptr; /* the xpointer if needed */ | |
| 63 int emptyFb; /* flag to show fallback empty */ | |
| 64 }; | |
| 65 | |
| 66 struct _xmlXIncludeCtxt { | |
| 67 xmlDocPtr doc; /* the source document */ | |
| 68 int incBase; /* the first include for this document */ | |
| 69 int incNr; /* number of includes */ | |
| 70 int incMax; /* size of includes tab */ | |
| 71 xmlXIncludeRefPtr *incTab; /* array of included references */ | |
| 72 | |
| 73 int txtNr; /* number of unparsed documents */ | |
| 74 int txtMax; /* size of unparsed documents tab */ | |
| 75 xmlNodePtr *txtTab; /* array of unparsed text nodes */ | |
| 76 xmlURL *txturlTab; /* array of unparsed text URLs */ | |
| 77 | |
| 78 xmlChar * url; /* the current URL processed */ | |
| 79 int urlNr; /* number of URLs stacked */ | |
| 80 int urlMax; /* size of URL stack */ | |
| 81 xmlChar * *urlTab; /* URL stack */ | |
| 82 | |
| 83 int nbErrors; /* the number of errors detected */ | |
| 84 int legacy; /* using XINCLUDE_OLD_NS */ | |
| 85 int parseFlags; /* the flags used for parsing XML documents */ | |
| 86 xmlChar * base; /* the current xml:base */ | |
| 87 | |
| 88 void *_private; /* application data */ | |
| 89 }; | |
| 90 | |
| 91 static int | |
| 92 xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree); | |
| 93 | |
| 94 | |
| 95 /************************************************************************ | |
| 96 * * | |
| 97 * XInclude error handler * | |
| 98 * * | |
| 99 ************************************************************************/ | |
| 100 | |
| 101 /** | |
| 102 * xmlXIncludeErrMemory: | |
| 103 * @extra: extra information | |
| 104 * | |
| 105 * Handle an out of memory condition | |
| 106 */ | |
| 107 static void | |
| 108 xmlXIncludeErrMemory(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, | |
| 109 const char *extra) | |
| 110 { | |
| 111 if (ctxt != NULL) | |
| 112 ctxt->nbErrors++; | |
| 113 __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE, | |
| 114 XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0, | |
| 115 extra, NULL, NULL, 0, 0, | |
| 116 "Memory allocation failed : %s\n", extra); | |
| 117 } | |
| 118 | |
| 119 /** | |
| 120 * xmlXIncludeErr: | |
| 121 * @ctxt: the XInclude context | |
| 122 * @node: the context node | |
| 123 * @msg: the error message | |
| 124 * @extra: extra information | |
| 125 * | |
| 126 * Handle an XInclude error | |
| 127 */ | |
| 128 static void LIBXML_ATTR_FORMAT(4,0) | |
| 129 xmlXIncludeErr(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error, | |
| 130 const char *msg, const xmlChar *extra) | |
| 131 { | |
| 132 if (ctxt != NULL) | |
| 133 ctxt->nbErrors++; | |
| 134 __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE, | |
| 135 error, XML_ERR_ERROR, NULL, 0, | |
| 136 (const char *) extra, NULL, NULL, 0, 0, | |
| 137 msg, (const char *) extra); | |
| 138 } | |
| 139 | |
| 140 #if 0 | |
| 141 /** | |
| 142 * xmlXIncludeWarn: | |
| 143 * @ctxt: the XInclude context | |
| 144 * @node: the context node | |
| 145 * @msg: the error message | |
| 146 * @extra: extra information | |
| 147 * | |
| 148 * Emit an XInclude warning. | |
| 149 */ | |
| 150 static void LIBXML_ATTR_FORMAT(4,0) | |
| 151 xmlXIncludeWarn(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node, int error, | |
| 152 const char *msg, const xmlChar *extra) | |
| 153 { | |
| 154 __xmlRaiseError(NULL, NULL, NULL, ctxt, node, XML_FROM_XINCLUDE, | |
| 155 error, XML_ERR_WARNING, NULL, 0, | |
| 156 (const char *) extra, NULL, NULL, 0, 0, | |
| 157 msg, (const char *) extra); | |
| 158 } | |
| 159 #endif | |
| 160 | |
| 161 /** | |
| 162 * xmlXIncludeGetProp: | |
| 163 * @ctxt: the XInclude context | |
| 164 * @cur: the node | |
| 165 * @name: the attribute name | |
| 166 * | |
| 167 * Get an XInclude attribute | |
| 168 * | |
| 169 * Returns the value (to be freed) or NULL if not found | |
| 170 */ | |
| 171 static xmlChar * | |
| 172 xmlXIncludeGetProp(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur, | |
| 173 const xmlChar *name) { | |
| 174 xmlChar *ret; | |
| 175 | |
| 176 ret = xmlGetNsProp(cur, XINCLUDE_NS, name); | |
| 177 if (ret != NULL) | |
| 178 return(ret); | |
| 179 if (ctxt->legacy != 0) { | |
| 180 ret = xmlGetNsProp(cur, XINCLUDE_OLD_NS, name); | |
| 181 if (ret != NULL) | |
| 182 return(ret); | |
| 183 } | |
| 184 ret = xmlGetProp(cur, name); | |
| 185 return(ret); | |
| 186 } | |
| 187 /** | |
| 188 * xmlXIncludeFreeRef: | |
| 189 * @ref: the XInclude reference | |
| 190 * | |
| 191 * Free an XInclude reference | |
| 192 */ | |
| 193 static void | |
| 194 xmlXIncludeFreeRef(xmlXIncludeRefPtr ref) { | |
| 195 if (ref == NULL) | |
| 196 return; | |
| 197 #ifdef DEBUG_XINCLUDE | |
| 198 xmlGenericError(xmlGenericErrorContext, "Freeing ref\n"); | |
| 199 #endif | |
| 200 if (ref->doc != NULL) { | |
| 201 #ifdef DEBUG_XINCLUDE | |
| 202 xmlGenericError(xmlGenericErrorContext, "Freeing doc %s\n", ref->URI); | |
| 203 #endif | |
| 204 xmlFreeDoc(ref->doc); | |
| 205 } | |
| 206 if (ref->URI != NULL) | |
| 207 xmlFree(ref->URI); | |
| 208 if (ref->fragment != NULL) | |
| 209 xmlFree(ref->fragment); | |
| 210 if (ref->xptr != NULL) | |
| 211 xmlXPathFreeObject(ref->xptr); | |
| 212 xmlFree(ref); | |
| 213 } | |
| 214 | |
| 215 /** | |
| 216 * xmlXIncludeNewRef: | |
| 217 * @ctxt: the XInclude context | |
| 218 * @URI: the resource URI | |
| 219 * | |
| 220 * Creates a new reference within an XInclude context | |
| 221 * | |
| 222 * Returns the new set | |
| 223 */ | |
| 224 static xmlXIncludeRefPtr | |
| 225 xmlXIncludeNewRef(xmlXIncludeCtxtPtr ctxt, const xmlChar *URI, | |
| 226 xmlNodePtr ref) { | |
| 227 xmlXIncludeRefPtr ret; | |
| 228 | |
| 229 #ifdef DEBUG_XINCLUDE | |
| 230 xmlGenericError(xmlGenericErrorContext, "New ref %s\n", URI); | |
| 231 #endif | |
| 232 ret = (xmlXIncludeRefPtr) xmlMalloc(sizeof(xmlXIncludeRef)); | |
| 233 if (ret == NULL) { | |
| 234 xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context"); | |
| 235 return(NULL); | |
| 236 } | |
| 237 memset(ret, 0, sizeof(xmlXIncludeRef)); | |
| 238 if (URI == NULL) | |
| 239 ret->URI = NULL; | |
| 240 else | |
| 241 ret->URI = xmlStrdup(URI); | |
| 242 ret->fragment = NULL; | |
| 243 ret->ref = ref; | |
| 244 ret->doc = NULL; | |
| 245 ret->count = 0; | |
| 246 ret->xml = 0; | |
| 247 ret->inc = NULL; | |
| 248 if (ctxt->incMax == 0) { | |
| 249 ctxt->incMax = 4; | |
| 250 ctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(ctxt->incMax * | |
| 251 sizeof(ctxt->incTab[0])); | |
| 252 if (ctxt->incTab == NULL) { | |
| 253 xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context"); | |
| 254 xmlXIncludeFreeRef(ret); | |
| 255 return(NULL); | |
| 256 } | |
| 257 } | |
| 258 if (ctxt->incNr >= ctxt->incMax) { | |
| 259 ctxt->incMax *= 2; | |
| 260 ctxt->incTab = (xmlXIncludeRefPtr *) xmlRealloc(ctxt->incTab, | |
| 261 ctxt->incMax * sizeof(ctxt->incTab[0])); | |
| 262 if (ctxt->incTab == NULL) { | |
| 263 xmlXIncludeErrMemory(ctxt, ref, "growing XInclude context"); | |
| 264 xmlXIncludeFreeRef(ret); | |
| 265 return(NULL); | |
| 266 } | |
| 267 } | |
| 268 ctxt->incTab[ctxt->incNr++] = ret; | |
| 269 return(ret); | |
| 270 } | |
| 271 | |
| 272 /** | |
| 273 * xmlXIncludeNewContext: | |
| 274 * @doc: an XML Document | |
| 275 * | |
| 276 * Creates a new XInclude context | |
| 277 * | |
| 278 * Returns the new set | |
| 279 */ | |
| 280 xmlXIncludeCtxtPtr | |
| 281 xmlXIncludeNewContext(xmlDocPtr doc) { | |
| 282 xmlXIncludeCtxtPtr ret; | |
| 283 | |
| 284 #ifdef DEBUG_XINCLUDE | |
| 285 xmlGenericError(xmlGenericErrorContext, "New context\n"); | |
| 286 #endif | |
| 287 if (doc == NULL) | |
| 288 return(NULL); | |
| 289 ret = (xmlXIncludeCtxtPtr) xmlMalloc(sizeof(xmlXIncludeCtxt)); | |
| 290 if (ret == NULL) { | |
| 291 xmlXIncludeErrMemory(NULL, (xmlNodePtr) doc, | |
| 292 "creating XInclude context"); | |
| 293 return(NULL); | |
| 294 } | |
| 295 memset(ret, 0, sizeof(xmlXIncludeCtxt)); | |
| 296 ret->doc = doc; | |
| 297 ret->incNr = 0; | |
| 298 ret->incBase = 0; | |
| 299 ret->incMax = 0; | |
| 300 ret->incTab = NULL; | |
| 301 ret->nbErrors = 0; | |
| 302 return(ret); | |
| 303 } | |
| 304 | |
| 305 /** | |
| 306 * xmlXIncludeURLPush: | |
| 307 * @ctxt: the parser context | |
| 308 * @value: the url | |
| 309 * | |
| 310 * Pushes a new url on top of the url stack | |
| 311 * | |
| 312 * Returns -1 in case of error, the index in the stack otherwise | |
| 313 */ | |
| 314 static int | |
| 315 xmlXIncludeURLPush(xmlXIncludeCtxtPtr ctxt, | |
| 316 const xmlChar *value) | |
| 317 { | |
| 318 if (ctxt->urlNr > XINCLUDE_MAX_DEPTH) { | |
| 319 xmlXIncludeErr(ctxt, NULL, XML_XINCLUDE_RECURSION, | |
| 320 "detected a recursion in %s\n", value); | |
| 321 return(-1); | |
| 322 } | |
| 323 if (ctxt->urlTab == NULL) { | |
| 324 ctxt->urlMax = 4; | |
| 325 ctxt->urlNr = 0; | |
| 326 ctxt->urlTab = (xmlChar * *) xmlMalloc( | |
| 327 ctxt->urlMax * sizeof(ctxt->urlTab[0])); | |
| 328 if (ctxt->urlTab == NULL) { | |
| 329 xmlXIncludeErrMemory(ctxt, NULL, "adding URL"); | |
| 330 return (-1); | |
| 331 } | |
| 332 } | |
| 333 if (ctxt->urlNr >= ctxt->urlMax) { | |
| 334 ctxt->urlMax *= 2; | |
| 335 ctxt->urlTab = | |
| 336 (xmlChar * *) xmlRealloc(ctxt->urlTab, | |
| 337 ctxt->urlMax * | |
| 338 sizeof(ctxt->urlTab[0])); | |
| 339 if (ctxt->urlTab == NULL) { | |
| 340 xmlXIncludeErrMemory(ctxt, NULL, "adding URL"); | |
| 341 return (-1); | |
| 342 } | |
| 343 } | |
| 344 ctxt->url = ctxt->urlTab[ctxt->urlNr] = xmlStrdup(value); | |
| 345 return (ctxt->urlNr++); | |
| 346 } | |
| 347 | |
| 348 /** | |
| 349 * xmlXIncludeURLPop: | |
| 350 * @ctxt: the parser context | |
| 351 * | |
| 352 * Pops the top URL from the URL stack | |
| 353 */ | |
| 354 static void | |
| 355 xmlXIncludeURLPop(xmlXIncludeCtxtPtr ctxt) | |
| 356 { | |
| 357 xmlChar * ret; | |
| 358 | |
| 359 if (ctxt->urlNr <= 0) | |
| 360 return; | |
| 361 ctxt->urlNr--; | |
| 362 if (ctxt->urlNr > 0) | |
| 363 ctxt->url = ctxt->urlTab[ctxt->urlNr - 1]; | |
| 364 else | |
| 365 ctxt->url = NULL; | |
| 366 ret = ctxt->urlTab[ctxt->urlNr]; | |
| 367 ctxt->urlTab[ctxt->urlNr] = NULL; | |
| 368 if (ret != NULL) | |
| 369 xmlFree(ret); | |
| 370 } | |
| 371 | |
| 372 /** | |
| 373 * xmlXIncludeFreeContext: | |
| 374 * @ctxt: the XInclude context | |
| 375 * | |
| 376 * Free an XInclude context | |
| 377 */ | |
| 378 void | |
| 379 xmlXIncludeFreeContext(xmlXIncludeCtxtPtr ctxt) { | |
| 380 int i; | |
| 381 | |
| 382 #ifdef DEBUG_XINCLUDE | |
| 383 xmlGenericError(xmlGenericErrorContext, "Freeing context\n"); | |
| 384 #endif | |
| 385 if (ctxt == NULL) | |
| 386 return; | |
| 387 while (ctxt->urlNr > 0) | |
| 388 xmlXIncludeURLPop(ctxt); | |
| 389 if (ctxt->urlTab != NULL) | |
| 390 xmlFree(ctxt->urlTab); | |
| 391 for (i = 0;i < ctxt->incNr;i++) { | |
| 392 if (ctxt->incTab[i] != NULL) | |
| 393 xmlXIncludeFreeRef(ctxt->incTab[i]); | |
| 394 } | |
| 395 if (ctxt->txturlTab != NULL) { | |
| 396 for (i = 0;i < ctxt->txtNr;i++) { | |
| 397 if (ctxt->txturlTab[i] != NULL) | |
| 398 xmlFree(ctxt->txturlTab[i]); | |
| 399 } | |
| 400 } | |
| 401 if (ctxt->incTab != NULL) | |
| 402 xmlFree(ctxt->incTab); | |
| 403 if (ctxt->txtTab != NULL) | |
| 404 xmlFree(ctxt->txtTab); | |
| 405 if (ctxt->txturlTab != NULL) | |
| 406 xmlFree(ctxt->txturlTab); | |
| 407 if (ctxt->base != NULL) { | |
| 408 xmlFree(ctxt->base); | |
| 409 } | |
| 410 xmlFree(ctxt); | |
| 411 } | |
| 412 | |
| 413 /** | |
| 414 * xmlXIncludeParseFile: | |
| 415 * @ctxt: the XInclude context | |
| 416 * @URL: the URL or file path | |
| 417 * | |
| 418 * parse a document for XInclude | |
| 419 */ | |
| 420 static xmlDocPtr | |
| 421 xmlXIncludeParseFile(xmlXIncludeCtxtPtr ctxt, const char *URL) { | |
| 422 xmlDocPtr ret; | |
| 423 xmlParserCtxtPtr pctxt; | |
| 424 xmlParserInputPtr inputStream; | |
| 425 | |
| 426 xmlInitParser(); | |
| 427 | |
| 428 pctxt = xmlNewParserCtxt(); | |
| 429 if (pctxt == NULL) { | |
| 430 xmlXIncludeErrMemory(ctxt, NULL, "cannot allocate parser context"); | |
| 431 return(NULL); | |
| 432 } | |
| 433 | |
| 434 /* | |
| 435 * pass in the application data to the parser context. | |
| 436 */ | |
| 437 pctxt->_private = ctxt->_private; | |
| 438 | |
| 439 /* | |
| 440 * try to ensure that new documents included are actually | |
| 441 * built with the same dictionary as the including document. | |
| 442 */ | |
| 443 if ((ctxt->doc != NULL) && (ctxt->doc->dict != NULL)) { | |
| 444 if (pctxt->dict != NULL) | |
| 445 xmlDictFree(pctxt->dict); | |
| 446 pctxt->dict = ctxt->doc->dict; | |
| 447 xmlDictReference(pctxt->dict); | |
| 448 } | |
| 449 | |
| 450 xmlCtxtUseOptions(pctxt, ctxt->parseFlags | XML_PARSE_DTDLOAD); | |
| 451 | |
| 452 inputStream = xmlLoadExternalEntity(URL, NULL, pctxt); | |
| 453 if (inputStream == NULL) { | |
| 454 xmlFreeParserCtxt(pctxt); | |
| 455 return(NULL); | |
| 456 } | |
| 457 | |
| 458 inputPush(pctxt, inputStream); | |
| 459 | |
| 460 if (pctxt->directory == NULL) | |
| 461 pctxt->directory = xmlParserGetDirectory(URL); | |
| 462 | |
| 463 pctxt->loadsubset |= XML_DETECT_IDS; | |
| 464 | |
| 465 xmlParseDocument(pctxt); | |
| 466 | |
| 467 if (pctxt->wellFormed) { | |
| 468 ret = pctxt->myDoc; | |
| 469 } | |
| 470 else { | |
| 471 ret = NULL; | |
| 472 if (pctxt->myDoc != NULL) | |
| 473 xmlFreeDoc(pctxt->myDoc); | |
| 474 pctxt->myDoc = NULL; | |
| 475 } | |
| 476 xmlFreeParserCtxt(pctxt); | |
| 477 | |
| 478 return(ret); | |
| 479 } | |
| 480 | |
| 481 /** | |
| 482 * xmlXIncludeAddNode: | |
| 483 * @ctxt: the XInclude context | |
| 484 * @cur: the new node | |
| 485 * | |
| 486 * Add a new node to process to an XInclude context | |
| 487 */ | |
| 488 static int | |
| 489 xmlXIncludeAddNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr cur) { | |
| 490 xmlXIncludeRefPtr ref; | |
| 491 xmlURIPtr uri; | |
| 492 xmlChar *URL; | |
| 493 xmlChar *fragment = NULL; | |
| 494 xmlChar *href; | |
| 495 xmlChar *parse; | |
| 496 xmlChar *base; | |
| 497 xmlChar *URI; | |
| 498 int xml = 1, i; /* default Issue 64 */ | |
| 499 int local = 0; | |
| 500 | |
| 501 | |
| 502 if (ctxt == NULL) | |
| 503 return(-1); | |
| 504 if (cur == NULL) | |
| 505 return(-1); | |
| 506 | |
| 507 #ifdef DEBUG_XINCLUDE | |
| 508 xmlGenericError(xmlGenericErrorContext, "Add node\n"); | |
| 509 #endif | |
| 510 /* | |
| 511 * read the attributes | |
| 512 */ | |
| 513 href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF); | |
| 514 if (href == NULL) { | |
| 515 href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */ | |
| 516 if (href == NULL) | |
| 517 return(-1); | |
| 518 } | |
| 519 if ((href[0] == '#') || (href[0] == 0)) | |
| 520 local = 1; | |
| 521 parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE); | |
| 522 if (parse != NULL) { | |
| 523 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML)) | |
| 524 xml = 1; | |
| 525 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT)) | |
| 526 xml = 0; | |
| 527 else { | |
| 528 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_PARSE_VALUE, | |
| 529 "invalid value %s for 'parse'\n", parse); | |
| 530 if (href != NULL) | |
| 531 xmlFree(href); | |
| 532 if (parse != NULL) | |
| 533 xmlFree(parse); | |
| 534 return(-1); | |
| 535 } | |
| 536 } | |
| 537 | |
| 538 /* | |
| 539 * compute the URI | |
| 540 */ | |
| 541 base = xmlNodeGetBase(ctxt->doc, cur); | |
| 542 if (base == NULL) { | |
| 543 URI = xmlBuildURI(href, ctxt->doc->URL); | |
| 544 } else { | |
| 545 URI = xmlBuildURI(href, base); | |
| 546 } | |
| 547 if (URI == NULL) { | |
| 548 xmlChar *escbase; | |
| 549 xmlChar *eschref; | |
| 550 /* | |
| 551 * Some escaping may be needed | |
| 552 */ | |
| 553 escbase = xmlURIEscape(base); | |
| 554 eschref = xmlURIEscape(href); | |
| 555 URI = xmlBuildURI(eschref, escbase); | |
| 556 if (escbase != NULL) | |
| 557 xmlFree(escbase); | |
| 558 if (eschref != NULL) | |
| 559 xmlFree(eschref); | |
| 560 } | |
| 561 if (parse != NULL) | |
| 562 xmlFree(parse); | |
| 563 if (href != NULL) | |
| 564 xmlFree(href); | |
| 565 if (base != NULL) | |
| 566 xmlFree(base); | |
| 567 if (URI == NULL) { | |
| 568 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, | |
| 569 "failed build URL\n", NULL); | |
| 570 return(-1); | |
| 571 } | |
| 572 fragment = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE_XPOINTER); | |
| 573 | |
| 574 /* | |
| 575 * Check the URL and remove any fragment identifier | |
| 576 */ | |
| 577 uri = xmlParseURI((const char *)URI); | |
| 578 if (uri == NULL) { | |
| 579 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, | |
| 580 "invalid value URI %s\n", URI); | |
| 581 if (fragment != NULL) | |
| 582 xmlFree(fragment); | |
| 583 xmlFree(URI); | |
| 584 return(-1); | |
| 585 } | |
| 586 | |
| 587 if (uri->fragment != NULL) { | |
| 588 if (ctxt->legacy != 0) { | |
| 589 if (fragment == NULL) { | |
| 590 fragment = (xmlChar *) uri->fragment; | |
| 591 } else { | |
| 592 xmlFree(uri->fragment); | |
| 593 } | |
| 594 } else { | |
| 595 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_FRAGMENT_ID, | |
| 596 "Invalid fragment identifier in URI %s use the xpointer attribute\n", | |
| 597 URI); | |
| 598 if (fragment != NULL) | |
| 599 xmlFree(fragment); | |
| 600 xmlFreeURI(uri); | |
| 601 xmlFree(URI); | |
| 602 return(-1); | |
| 603 } | |
| 604 uri->fragment = NULL; | |
| 605 } | |
| 606 URL = xmlSaveUri(uri); | |
| 607 xmlFreeURI(uri); | |
| 608 xmlFree(URI); | |
| 609 if (URL == NULL) { | |
| 610 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_HREF_URI, | |
| 611 "invalid value URI %s\n", URI); | |
| 612 if (fragment != NULL) | |
| 613 xmlFree(fragment); | |
| 614 return(-1); | |
| 615 } | |
| 616 | |
| 617 /* | |
| 618 * If local and xml then we need a fragment | |
| 619 */ | |
| 620 if ((local == 1) && (xml == 1) && | |
| 621 ((fragment == NULL) || (fragment[0] == 0))) { | |
| 622 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION, | |
| 623 "detected a local recursion with no xpointer in %s\n", | |
| 624 URL); | |
| 625 if (fragment != NULL) | |
| 626 xmlFree(fragment); | |
| 627 return(-1); | |
| 628 } | |
| 629 | |
| 630 /* | |
| 631 * Check the URL against the stack for recursions | |
| 632 */ | |
| 633 if ((!local) && (xml == 1)) { | |
| 634 for (i = 0;i < ctxt->urlNr;i++) { | |
| 635 if (xmlStrEqual(URL, ctxt->urlTab[i])) { | |
| 636 xmlXIncludeErr(ctxt, cur, XML_XINCLUDE_RECURSION, | |
| 637 "detected a recursion in %s\n", URL); | |
| 638 return(-1); | |
| 639 } | |
| 640 } | |
| 641 } | |
| 642 | |
| 643 ref = xmlXIncludeNewRef(ctxt, URL, cur); | |
| 644 if (ref == NULL) { | |
| 645 return(-1); | |
| 646 } | |
| 647 ref->fragment = fragment; | |
| 648 ref->doc = NULL; | |
| 649 ref->xml = xml; | |
| 650 ref->count = 1; | |
| 651 xmlFree(URL); | |
| 652 return(0); | |
| 653 } | |
| 654 | |
| 655 /** | |
| 656 * xmlXIncludeRecurseDoc: | |
| 657 * @ctxt: the XInclude context | |
| 658 * @doc: the new document | |
| 659 * @url: the associated URL | |
| 660 * | |
| 661 * The XInclude recursive nature is handled at this point. | |
| 662 */ | |
| 663 static void | |
| 664 xmlXIncludeRecurseDoc(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, | |
| 665 const xmlURL url ATTRIBUTE_UNUSED) { | |
| 666 xmlXIncludeCtxtPtr newctxt; | |
| 667 int i; | |
| 668 | |
| 669 /* | |
| 670 * Avoid recursion in already substitued resources | |
| 671 for (i = 0;i < ctxt->urlNr;i++) { | |
| 672 if (xmlStrEqual(doc->URL, ctxt->urlTab[i])) | |
| 673 return; | |
| 674 } | |
| 675 */ | |
| 676 | |
| 677 #ifdef DEBUG_XINCLUDE | |
| 678 xmlGenericError(xmlGenericErrorContext, "Recursing in doc %s\n", doc->URL); | |
| 679 #endif | |
| 680 /* | |
| 681 * Handle recursion here. | |
| 682 */ | |
| 683 | |
| 684 newctxt = xmlXIncludeNewContext(doc); | |
| 685 if (newctxt != NULL) { | |
| 686 /* | |
| 687 * Copy the private user data | |
| 688 */ | |
| 689 newctxt->_private = ctxt->_private; | |
| 690 /* | |
| 691 * Copy the existing document set | |
| 692 */ | |
| 693 newctxt->incMax = ctxt->incMax; | |
| 694 newctxt->incNr = ctxt->incNr; | |
| 695 newctxt->incTab = (xmlXIncludeRefPtr *) xmlMalloc(newctxt->incMax * | |
| 696 sizeof(newctxt->incTab[0])); | |
| 697 if (newctxt->incTab == NULL) { | |
| 698 xmlXIncludeErrMemory(ctxt, (xmlNodePtr) doc, "processing doc"); | |
| 699 xmlFree(newctxt); | |
| 700 return; | |
| 701 } | |
| 702 /* | |
| 703 * copy the urlTab | |
| 704 */ | |
| 705 newctxt->urlMax = ctxt->urlMax; | |
| 706 newctxt->urlNr = ctxt->urlNr; | |
| 707 newctxt->urlTab = ctxt->urlTab; | |
| 708 | |
| 709 /* | |
| 710 * Inherit the existing base | |
| 711 */ | |
| 712 newctxt->base = xmlStrdup(ctxt->base); | |
| 713 | |
| 714 /* | |
| 715 * Inherit the documents already in use by other includes | |
| 716 */ | |
| 717 newctxt->incBase = ctxt->incNr; | |
| 718 for (i = 0;i < ctxt->incNr;i++) { | |
| 719 newctxt->incTab[i] = ctxt->incTab[i]; | |
| 720 newctxt->incTab[i]->count++; /* prevent the recursion from | |
| 721 freeing it */ | |
| 722 } | |
| 723 /* | |
| 724 * The new context should also inherit the Parse Flags | |
| 725 * (bug 132597) | |
| 726 */ | |
| 727 newctxt->parseFlags = ctxt->parseFlags; | |
| 728 xmlXIncludeDoProcess(newctxt, doc, xmlDocGetRootElement(doc)); | |
| 729 for (i = 0;i < ctxt->incNr;i++) { | |
| 730 newctxt->incTab[i]->count--; | |
| 731 newctxt->incTab[i] = NULL; | |
| 732 } | |
| 733 | |
| 734 /* urlTab may have been reallocated */ | |
| 735 ctxt->urlTab = newctxt->urlTab; | |
| 736 ctxt->urlMax = newctxt->urlMax; | |
| 737 | |
| 738 newctxt->urlMax = 0; | |
| 739 newctxt->urlNr = 0; | |
| 740 newctxt->urlTab = NULL; | |
| 741 | |
| 742 xmlXIncludeFreeContext(newctxt); | |
| 743 } | |
| 744 #ifdef DEBUG_XINCLUDE | |
| 745 xmlGenericError(xmlGenericErrorContext, "Done recursing in doc %s\n", url); | |
| 746 #endif | |
| 747 } | |
| 748 | |
| 749 /** | |
| 750 * xmlXIncludeAddTxt: | |
| 751 * @ctxt: the XInclude context | |
| 752 * @txt: the new text node | |
| 753 * @url: the associated URL | |
| 754 * | |
| 755 * Add a new txtument to the list | |
| 756 */ | |
| 757 static void | |
| 758 xmlXIncludeAddTxt(xmlXIncludeCtxtPtr ctxt, xmlNodePtr txt, const xmlURL url) { | |
| 759 #ifdef DEBUG_XINCLUDE | |
| 760 xmlGenericError(xmlGenericErrorContext, "Adding text %s\n", url); | |
| 761 #endif | |
| 762 if (ctxt->txtMax == 0) { | |
| 763 ctxt->txtMax = 4; | |
| 764 ctxt->txtTab = (xmlNodePtr *) xmlMalloc(ctxt->txtMax * | |
| 765 sizeof(ctxt->txtTab[0])); | |
| 766 if (ctxt->txtTab == NULL) { | |
| 767 xmlXIncludeErrMemory(ctxt, NULL, "processing text"); | |
| 768 return; | |
| 769 } | |
| 770 ctxt->txturlTab = (xmlURL *) xmlMalloc(ctxt->txtMax * | |
| 771 sizeof(ctxt->txturlTab[0])); | |
| 772 if (ctxt->txturlTab == NULL) { | |
| 773 xmlXIncludeErrMemory(ctxt, NULL, "processing text"); | |
| 774 return; | |
| 775 } | |
| 776 } | |
| 777 if (ctxt->txtNr >= ctxt->txtMax) { | |
| 778 ctxt->txtMax *= 2; | |
| 779 ctxt->txtTab = (xmlNodePtr *) xmlRealloc(ctxt->txtTab, | |
| 780 ctxt->txtMax * sizeof(ctxt->txtTab[0])); | |
| 781 if (ctxt->txtTab == NULL) { | |
| 782 xmlXIncludeErrMemory(ctxt, NULL, "processing text"); | |
| 783 return; | |
| 784 } | |
| 785 ctxt->txturlTab = (xmlURL *) xmlRealloc(ctxt->txturlTab, | |
| 786 ctxt->txtMax * sizeof(ctxt->txturlTab[0])); | |
| 787 if (ctxt->txturlTab == NULL) { | |
| 788 xmlXIncludeErrMemory(ctxt, NULL, "processing text"); | |
| 789 return; | |
| 790 } | |
| 791 } | |
| 792 ctxt->txtTab[ctxt->txtNr] = txt; | |
| 793 ctxt->txturlTab[ctxt->txtNr] = xmlStrdup(url); | |
| 794 ctxt->txtNr++; | |
| 795 } | |
| 796 | |
| 797 /************************************************************************ | |
| 798 * * | |
| 799 * Node copy with specific semantic * | |
| 800 * * | |
| 801 ************************************************************************/ | |
| 802 | |
| 803 static xmlNodePtr | |
| 804 xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, | |
| 805 xmlDocPtr source, xmlNodePtr elem); | |
| 806 | |
| 807 /** | |
| 808 * xmlXIncludeCopyNode: | |
| 809 * @ctxt: the XInclude context | |
| 810 * @target: the document target | |
| 811 * @source: the document source | |
| 812 * @elem: the element | |
| 813 * | |
| 814 * Make a copy of the node while preserving the XInclude semantic | |
| 815 * of the Infoset copy | |
| 816 */ | |
| 817 static xmlNodePtr | |
| 818 xmlXIncludeCopyNode(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, | |
| 819 xmlDocPtr source, xmlNodePtr elem) { | |
| 820 xmlNodePtr result = NULL; | |
| 821 | |
| 822 if ((ctxt == NULL) || (target == NULL) || (source == NULL) || | |
| 823 (elem == NULL)) | |
| 824 return(NULL); | |
| 825 if (elem->type == XML_DTD_NODE) | |
| 826 return(NULL); | |
| 827 if (elem->type == XML_DOCUMENT_NODE) | |
| 828 result = xmlXIncludeCopyNodeList(ctxt, target, source, elem->children); | |
| 829 else | |
| 830 result = xmlDocCopyNode(elem, target, 1); | |
| 831 return(result); | |
| 832 } | |
| 833 | |
| 834 /** | |
| 835 * xmlXIncludeCopyNodeList: | |
| 836 * @ctxt: the XInclude context | |
| 837 * @target: the document target | |
| 838 * @source: the document source | |
| 839 * @elem: the element list | |
| 840 * | |
| 841 * Make a copy of the node list while preserving the XInclude semantic | |
| 842 * of the Infoset copy | |
| 843 */ | |
| 844 static xmlNodePtr | |
| 845 xmlXIncludeCopyNodeList(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, | |
| 846 xmlDocPtr source, xmlNodePtr elem) { | |
| 847 xmlNodePtr cur, res, result = NULL, last = NULL; | |
| 848 | |
| 849 if ((ctxt == NULL) || (target == NULL) || (source == NULL) || | |
| 850 (elem == NULL)) | |
| 851 return(NULL); | |
| 852 cur = elem; | |
| 853 while (cur != NULL) { | |
| 854 res = xmlXIncludeCopyNode(ctxt, target, source, cur); | |
| 855 if (res != NULL) { | |
| 856 if (result == NULL) { | |
| 857 result = last = res; | |
| 858 } else { | |
| 859 last->next = res; | |
| 860 res->prev = last; | |
| 861 last = res; | |
| 862 } | |
| 863 } | |
| 864 cur = cur->next; | |
| 865 } | |
| 866 return(result); | |
| 867 } | |
| 868 | |
| 869 /** | |
| 870 * xmlXIncludeGetNthChild: | |
| 871 * @cur: the node | |
| 872 * @no: the child number | |
| 873 * | |
| 874 * Returns the @n'th element child of @cur or NULL | |
| 875 */ | |
| 876 static xmlNodePtr | |
| 877 xmlXIncludeGetNthChild(xmlNodePtr cur, int no) { | |
| 878 int i; | |
| 879 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) | |
| 880 return(NULL); | |
| 881 cur = cur->children; | |
| 882 for (i = 0;i <= no;cur = cur->next) { | |
| 883 if (cur == NULL) | |
| 884 return(cur); | |
| 885 if ((cur->type == XML_ELEMENT_NODE) || | |
| 886 (cur->type == XML_DOCUMENT_NODE) || | |
| 887 (cur->type == XML_HTML_DOCUMENT_NODE)) { | |
| 888 i++; | |
| 889 if (i == no) | |
| 890 break; | |
| 891 } | |
| 892 } | |
| 893 return(cur); | |
| 894 } | |
| 895 | |
| 896 xmlNodePtr xmlXPtrAdvanceNode(xmlNodePtr cur, int *level); /* in xpointer.c */ | |
| 897 /** | |
| 898 * xmlXIncludeCopyRange: | |
| 899 * @ctxt: the XInclude context | |
| 900 * @target: the document target | |
| 901 * @source: the document source | |
| 902 * @obj: the XPointer result from the evaluation. | |
| 903 * | |
| 904 * Build a node list tree copy of the XPointer result. | |
| 905 * | |
| 906 * Returns an xmlNodePtr list or NULL. | |
| 907 * The caller has to free the node tree. | |
| 908 */ | |
| 909 static xmlNodePtr | |
| 910 xmlXIncludeCopyRange(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, | |
| 911 xmlDocPtr source, xmlXPathObjectPtr range) { | |
| 912 /* pointers to generated nodes */ | |
| 913 xmlNodePtr list = NULL, last = NULL, listParent = NULL; | |
| 914 xmlNodePtr tmp, tmp2; | |
| 915 /* pointers to traversal nodes */ | |
| 916 xmlNodePtr start, cur, end; | |
| 917 int index1, index2; | |
| 918 int level = 0, lastLevel = 0, endLevel = 0, endFlag = 0; | |
| 919 | |
| 920 if ((ctxt == NULL) || (target == NULL) || (source == NULL) || | |
| 921 (range == NULL)) | |
| 922 return(NULL); | |
| 923 if (range->type != XPATH_RANGE) | |
| 924 return(NULL); | |
| 925 start = (xmlNodePtr) range->user; | |
| 926 | |
| 927 if ((start == NULL) || (start->type == XML_NAMESPACE_DECL)) | |
| 928 return(NULL); | |
| 929 end = range->user2; | |
| 930 if (end == NULL) | |
| 931 return(xmlDocCopyNode(start, target, 1)); | |
| 932 if (end->type == XML_NAMESPACE_DECL) | |
| 933 return(NULL); | |
| 934 | |
| 935 cur = start; | |
| 936 index1 = range->index; | |
| 937 index2 = range->index2; | |
| 938 /* | |
| 939 * level is depth of the current node under consideration | |
| 940 * list is the pointer to the root of the output tree | |
| 941 * listParent is a pointer to the parent of output tree (within | |
| 942 the included file) in case we need to add another level | |
| 943 * last is a pointer to the last node added to the output tree | |
| 944 * lastLevel is the depth of last (relative to the root) | |
| 945 */ | |
| 946 while (cur != NULL) { | |
| 947 /* | |
| 948 * Check if our output tree needs a parent | |
| 949 */ | |
| 950 if (level < 0) { | |
| 951 while (level < 0) { | |
| 952 /* copy must include namespaces and properties */ | |
| 953 tmp2 = xmlDocCopyNode(listParent, target, 2); | |
| 954 xmlAddChild(tmp2, list); | |
| 955 list = tmp2; | |
| 956 listParent = listParent->parent; | |
| 957 level++; | |
| 958 } | |
| 959 last = list; | |
| 960 lastLevel = 0; | |
| 961 } | |
| 962 /* | |
| 963 * Check whether we need to change our insertion point | |
| 964 */ | |
| 965 while (level < lastLevel) { | |
| 966 last = last->parent; | |
| 967 lastLevel --; | |
| 968 } | |
| 969 if (cur == end) { /* Are we at the end of the range? */ | |
| 970 if (cur->type == XML_TEXT_NODE) { | |
| 971 const xmlChar *content = cur->content; | |
| 972 int len; | |
| 973 | |
| 974 if (content == NULL) { | |
| 975 tmp = xmlNewTextLen(NULL, 0); | |
| 976 } else { | |
| 977 len = index2; | |
| 978 if ((cur == start) && (index1 > 1)) { | |
| 979 content += (index1 - 1); | |
| 980 len -= (index1 - 1); | |
| 981 } else { | |
| 982 len = index2; | |
| 983 } | |
| 984 tmp = xmlNewTextLen(content, len); | |
| 985 } | |
| 986 /* single sub text node selection */ | |
| 987 if (list == NULL) | |
| 988 return(tmp); | |
| 989 /* prune and return full set */ | |
| 990 if (level == lastLevel) | |
| 991 xmlAddNextSibling(last, tmp); | |
| 992 else | |
| 993 xmlAddChild(last, tmp); | |
| 994 return(list); | |
| 995 } else { /* ending node not a text node */ | |
| 996 endLevel = level; /* remember the level of the end node */ | |
| 997 endFlag = 1; | |
| 998 /* last node - need to take care of properties + namespaces */ | |
| 999 tmp = xmlDocCopyNode(cur, target, 2); | |
| 1000 if (list == NULL) { | |
| 1001 list = tmp; | |
| 1002 listParent = cur->parent; | |
| 1003 } else { | |
| 1004 if (level == lastLevel) | |
| 1005 xmlAddNextSibling(last, tmp); | |
| 1006 else { | |
| 1007 xmlAddChild(last, tmp); | |
| 1008 lastLevel = level; | |
| 1009 } | |
| 1010 } | |
| 1011 last = tmp; | |
| 1012 | |
| 1013 if (index2 > 1) { | |
| 1014 end = xmlXIncludeGetNthChild(cur, index2 - 1); | |
| 1015 index2 = 0; | |
| 1016 } | |
| 1017 if ((cur == start) && (index1 > 1)) { | |
| 1018 cur = xmlXIncludeGetNthChild(cur, index1 - 1); | |
| 1019 index1 = 0; | |
| 1020 } else { | |
| 1021 cur = cur->children; | |
| 1022 } | |
| 1023 level++; /* increment level to show change */ | |
| 1024 /* | |
| 1025 * Now gather the remaining nodes from cur to end | |
| 1026 */ | |
| 1027 continue; /* while */ | |
| 1028 } | |
| 1029 } else if (cur == start) { /* Not at the end, are we at start? */ | |
| 1030 if ((cur->type == XML_TEXT_NODE) || | |
| 1031 (cur->type == XML_CDATA_SECTION_NODE)) { | |
| 1032 const xmlChar *content = cur->content; | |
| 1033 | |
| 1034 if (content == NULL) { | |
| 1035 tmp = xmlNewTextLen(NULL, 0); | |
| 1036 } else { | |
| 1037 if (index1 > 1) { | |
| 1038 content += (index1 - 1); | |
| 1039 index1 = 0; | |
| 1040 } | |
| 1041 tmp = xmlNewText(content); | |
| 1042 } | |
| 1043 last = list = tmp; | |
| 1044 listParent = cur->parent; | |
| 1045 } else { /* Not text node */ | |
| 1046 /* | |
| 1047 * start of the range - need to take care of | |
| 1048 * properties and namespaces | |
| 1049 */ | |
| 1050 tmp = xmlDocCopyNode(cur, target, 2); | |
| 1051 list = last = tmp; | |
| 1052 listParent = cur->parent; | |
| 1053 if (index1 > 1) { /* Do we need to position? */ | |
| 1054 cur = xmlXIncludeGetNthChild(cur, index1 - 1); | |
| 1055 level = lastLevel = 1; | |
| 1056 index1 = 0; | |
| 1057 /* | |
| 1058 * Now gather the remaining nodes from cur to end | |
| 1059 */ | |
| 1060 continue; /* while */ | |
| 1061 } | |
| 1062 } | |
| 1063 } else { | |
| 1064 tmp = NULL; | |
| 1065 switch (cur->type) { | |
| 1066 case XML_DTD_NODE: | |
| 1067 case XML_ELEMENT_DECL: | |
| 1068 case XML_ATTRIBUTE_DECL: | |
| 1069 case XML_ENTITY_NODE: | |
| 1070 /* Do not copy DTD informations */ | |
| 1071 break; | |
| 1072 case XML_ENTITY_DECL: | |
| 1073 /* handle crossing entities -> stack needed */ | |
| 1074 break; | |
| 1075 case XML_XINCLUDE_START: | |
| 1076 case XML_XINCLUDE_END: | |
| 1077 /* don't consider it part of the tree content */ | |
| 1078 break; | |
| 1079 case XML_ATTRIBUTE_NODE: | |
| 1080 /* Humm, should not happen ! */ | |
| 1081 break; | |
| 1082 default: | |
| 1083 /* | |
| 1084 * Middle of the range - need to take care of | |
| 1085 * properties and namespaces | |
| 1086 */ | |
| 1087 tmp = xmlDocCopyNode(cur, target, 2); | |
| 1088 break; | |
| 1089 } | |
| 1090 if (tmp != NULL) { | |
| 1091 if (level == lastLevel) | |
| 1092 xmlAddNextSibling(last, tmp); | |
| 1093 else { | |
| 1094 xmlAddChild(last, tmp); | |
| 1095 lastLevel = level; | |
| 1096 } | |
| 1097 last = tmp; | |
| 1098 } | |
| 1099 } | |
| 1100 /* | |
| 1101 * Skip to next node in document order | |
| 1102 */ | |
| 1103 cur = xmlXPtrAdvanceNode(cur, &level); | |
| 1104 if (endFlag && (level >= endLevel)) | |
| 1105 break; | |
| 1106 } | |
| 1107 return(list); | |
| 1108 } | |
| 1109 | |
| 1110 /** | |
| 1111 * xmlXIncludeBuildNodeList: | |
| 1112 * @ctxt: the XInclude context | |
| 1113 * @target: the document target | |
| 1114 * @source: the document source | |
| 1115 * @obj: the XPointer result from the evaluation. | |
| 1116 * | |
| 1117 * Build a node list tree copy of the XPointer result. | |
| 1118 * This will drop Attributes and Namespace declarations. | |
| 1119 * | |
| 1120 * Returns an xmlNodePtr list or NULL. | |
| 1121 * the caller has to free the node tree. | |
| 1122 */ | |
| 1123 static xmlNodePtr | |
| 1124 xmlXIncludeCopyXPointer(xmlXIncludeCtxtPtr ctxt, xmlDocPtr target, | |
| 1125 xmlDocPtr source, xmlXPathObjectPtr obj) { | |
| 1126 xmlNodePtr list = NULL, last = NULL; | |
| 1127 int i; | |
| 1128 | |
| 1129 if (source == NULL) | |
| 1130 source = ctxt->doc; | |
| 1131 if ((ctxt == NULL) || (target == NULL) || (source == NULL) || | |
| 1132 (obj == NULL)) | |
| 1133 return(NULL); | |
| 1134 switch (obj->type) { | |
| 1135 case XPATH_NODESET: { | |
| 1136 xmlNodeSetPtr set = obj->nodesetval; | |
| 1137 if (set == NULL) | |
| 1138 return(NULL); | |
| 1139 for (i = 0;i < set->nodeNr;i++) { | |
| 1140 if (set->nodeTab[i] == NULL) | |
| 1141 continue; | |
| 1142 switch (set->nodeTab[i]->type) { | |
| 1143 case XML_TEXT_NODE: | |
| 1144 case XML_CDATA_SECTION_NODE: | |
| 1145 case XML_ELEMENT_NODE: | |
| 1146 case XML_ENTITY_REF_NODE: | |
| 1147 case XML_ENTITY_NODE: | |
| 1148 case XML_PI_NODE: | |
| 1149 case XML_COMMENT_NODE: | |
| 1150 case XML_DOCUMENT_NODE: | |
| 1151 case XML_HTML_DOCUMENT_NODE: | |
| 1152 #ifdef LIBXML_DOCB_ENABLED | |
| 1153 case XML_DOCB_DOCUMENT_NODE: | |
| 1154 #endif | |
| 1155 case XML_XINCLUDE_END: | |
| 1156 break; | |
| 1157 case XML_XINCLUDE_START: { | |
| 1158 xmlNodePtr tmp, cur = set->nodeTab[i]; | |
| 1159 | |
| 1160 cur = cur->next; | |
| 1161 while (cur != NULL) { | |
| 1162 switch(cur->type) { | |
| 1163 case XML_TEXT_NODE: | |
| 1164 case XML_CDATA_SECTION_NODE: | |
| 1165 case XML_ELEMENT_NODE: | |
| 1166 case XML_ENTITY_REF_NODE: | |
| 1167 case XML_ENTITY_NODE: | |
| 1168 case XML_PI_NODE: | |
| 1169 case XML_COMMENT_NODE: | |
| 1170 tmp = xmlXIncludeCopyNode(ctxt, target, | |
| 1171 source, cur); | |
| 1172 if (last == NULL) { | |
| 1173 list = last = tmp; | |
| 1174 } else { | |
| 1175 xmlAddNextSibling(last, tmp); | |
| 1176 last = tmp; | |
| 1177 } | |
| 1178 cur = cur->next; | |
| 1179 continue; | |
| 1180 default: | |
| 1181 break; | |
| 1182 } | |
| 1183 break; | |
| 1184 } | |
| 1185 continue; | |
| 1186 } | |
| 1187 case XML_ATTRIBUTE_NODE: | |
| 1188 case XML_NAMESPACE_DECL: | |
| 1189 case XML_DOCUMENT_TYPE_NODE: | |
| 1190 case XML_DOCUMENT_FRAG_NODE: | |
| 1191 case XML_NOTATION_NODE: | |
| 1192 case XML_DTD_NODE: | |
| 1193 case XML_ELEMENT_DECL: | |
| 1194 case XML_ATTRIBUTE_DECL: | |
| 1195 case XML_ENTITY_DECL: | |
| 1196 continue; /* for */ | |
| 1197 } | |
| 1198 if (last == NULL) | |
| 1199 list = last = xmlXIncludeCopyNode(ctxt, target, source, | |
| 1200 set->nodeTab[i]); | |
| 1201 else { | |
| 1202 xmlAddNextSibling(last, | |
| 1203 xmlXIncludeCopyNode(ctxt, target, source, | |
| 1204 set->nodeTab[i])); | |
| 1205 if (last->next != NULL) | |
| 1206 last = last->next; | |
| 1207 } | |
| 1208 } | |
| 1209 break; | |
| 1210 } | |
| 1211 #ifdef LIBXML_XPTR_ENABLED | |
| 1212 case XPATH_LOCATIONSET: { | |
| 1213 xmlLocationSetPtr set = (xmlLocationSetPtr) obj->user; | |
| 1214 if (set == NULL) | |
| 1215 return(NULL); | |
| 1216 for (i = 0;i < set->locNr;i++) { | |
| 1217 if (last == NULL) | |
| 1218 list = last = xmlXIncludeCopyXPointer(ctxt, target, source, | |
| 1219 set->locTab[i]); | |
| 1220 else | |
| 1221 xmlAddNextSibling(last, | |
| 1222 xmlXIncludeCopyXPointer(ctxt, target, source, | |
| 1223 set->locTab[i])); | |
| 1224 if (last != NULL) { | |
| 1225 while (last->next != NULL) | |
| 1226 last = last->next; | |
| 1227 } | |
| 1228 } | |
| 1229 break; | |
| 1230 } | |
| 1231 case XPATH_RANGE: | |
| 1232 return(xmlXIncludeCopyRange(ctxt, target, source, obj)); | |
| 1233 #endif | |
| 1234 case XPATH_POINT: | |
| 1235 /* points are ignored in XInclude */ | |
| 1236 break; | |
| 1237 default: | |
| 1238 break; | |
| 1239 } | |
| 1240 return(list); | |
| 1241 } | |
| 1242 /************************************************************************ | |
| 1243 * * | |
| 1244 * XInclude I/O handling * | |
| 1245 * * | |
| 1246 ************************************************************************/ | |
| 1247 | |
| 1248 typedef struct _xmlXIncludeMergeData xmlXIncludeMergeData; | |
| 1249 typedef xmlXIncludeMergeData *xmlXIncludeMergeDataPtr; | |
| 1250 struct _xmlXIncludeMergeData { | |
| 1251 xmlDocPtr doc; | |
| 1252 xmlXIncludeCtxtPtr ctxt; | |
| 1253 }; | |
| 1254 | |
| 1255 /** | |
| 1256 * xmlXIncludeMergeOneEntity: | |
| 1257 * @ent: the entity | |
| 1258 * @doc: the including doc | |
| 1259 * @nr: the entity name | |
| 1260 * | |
| 1261 * Inplements the merge of one entity | |
| 1262 */ | |
| 1263 static void | |
| 1264 xmlXIncludeMergeEntity(xmlEntityPtr ent, xmlXIncludeMergeDataPtr data, | |
| 1265 xmlChar *name ATTRIBUTE_UNUSED) { | |
| 1266 xmlEntityPtr ret, prev; | |
| 1267 xmlDocPtr doc; | |
| 1268 xmlXIncludeCtxtPtr ctxt; | |
| 1269 | |
| 1270 if ((ent == NULL) || (data == NULL)) | |
| 1271 return; | |
| 1272 ctxt = data->ctxt; | |
| 1273 doc = data->doc; | |
| 1274 if ((ctxt == NULL) || (doc == NULL)) | |
| 1275 return; | |
| 1276 switch (ent->etype) { | |
| 1277 case XML_INTERNAL_PARAMETER_ENTITY: | |
| 1278 case XML_EXTERNAL_PARAMETER_ENTITY: | |
| 1279 case XML_INTERNAL_PREDEFINED_ENTITY: | |
| 1280 return; | |
| 1281 case XML_INTERNAL_GENERAL_ENTITY: | |
| 1282 case XML_EXTERNAL_GENERAL_PARSED_ENTITY: | |
| 1283 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY: | |
| 1284 break; | |
| 1285 } | |
| 1286 ret = xmlAddDocEntity(doc, ent->name, ent->etype, ent->ExternalID, | |
| 1287 ent->SystemID, ent->content); | |
| 1288 if (ret != NULL) { | |
| 1289 if (ent->URI != NULL) | |
| 1290 ret->URI = xmlStrdup(ent->URI); | |
| 1291 } else { | |
| 1292 prev = xmlGetDocEntity(doc, ent->name); | |
| 1293 if (prev != NULL) { | |
| 1294 if (ent->etype != prev->etype) | |
| 1295 goto error; | |
| 1296 | |
| 1297 if ((ent->SystemID != NULL) && (prev->SystemID != NULL)) { | |
| 1298 if (!xmlStrEqual(ent->SystemID, prev->SystemID)) | |
| 1299 goto error; | |
| 1300 } else if ((ent->ExternalID != NULL) && | |
| 1301 (prev->ExternalID != NULL)) { | |
| 1302 if (!xmlStrEqual(ent->ExternalID, prev->ExternalID)) | |
| 1303 goto error; | |
| 1304 } else if ((ent->content != NULL) && (prev->content != NULL)) { | |
| 1305 if (!xmlStrEqual(ent->content, prev->content)) | |
| 1306 goto error; | |
| 1307 } else { | |
| 1308 goto error; | |
| 1309 } | |
| 1310 | |
| 1311 } | |
| 1312 } | |
| 1313 return; | |
| 1314 error: | |
| 1315 switch (ent->etype) { | |
| 1316 case XML_INTERNAL_PARAMETER_ENTITY: | |
| 1317 case XML_EXTERNAL_PARAMETER_ENTITY: | |
| 1318 case XML_INTERNAL_PREDEFINED_ENTITY: | |
| 1319 case XML_INTERNAL_GENERAL_ENTITY: | |
| 1320 case XML_EXTERNAL_GENERAL_PARSED_ENTITY: | |
| 1321 return; | |
| 1322 case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY: | |
| 1323 break; | |
| 1324 } | |
| 1325 xmlXIncludeErr(ctxt, (xmlNodePtr) ent, XML_XINCLUDE_ENTITY_DEF_MISMATCH, | |
| 1326 "mismatch in redefinition of entity %s\n", | |
| 1327 ent->name); | |
| 1328 } | |
| 1329 | |
| 1330 /** | |
| 1331 * xmlXIncludeMergeEntities: | |
| 1332 * @ctxt: an XInclude context | |
| 1333 * @doc: the including doc | |
| 1334 * @from: the included doc | |
| 1335 * | |
| 1336 * Inplements the entity merge | |
| 1337 * | |
| 1338 * Returns 0 if merge succeeded, -1 if some processing failed | |
| 1339 */ | |
| 1340 static int | |
| 1341 xmlXIncludeMergeEntities(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, | |
| 1342 xmlDocPtr from) { | |
| 1343 xmlNodePtr cur; | |
| 1344 xmlDtdPtr target, source; | |
| 1345 | |
| 1346 if (ctxt == NULL) | |
| 1347 return(-1); | |
| 1348 | |
| 1349 if ((from == NULL) || (from->intSubset == NULL)) | |
| 1350 return(0); | |
| 1351 | |
| 1352 target = doc->intSubset; | |
| 1353 if (target == NULL) { | |
| 1354 cur = xmlDocGetRootElement(doc); | |
| 1355 if (cur == NULL) | |
| 1356 return(-1); | |
| 1357 target = xmlCreateIntSubset(doc, cur->name, NULL, NULL); | |
| 1358 if (target == NULL) | |
| 1359 return(-1); | |
| 1360 } | |
| 1361 | |
| 1362 source = from->intSubset; | |
| 1363 if ((source != NULL) && (source->entities != NULL)) { | |
| 1364 xmlXIncludeMergeData data; | |
| 1365 | |
| 1366 data.ctxt = ctxt; | |
| 1367 data.doc = doc; | |
| 1368 | |
| 1369 xmlHashScan((xmlHashTablePtr) source->entities, | |
| 1370 (xmlHashScanner) xmlXIncludeMergeEntity, &data); | |
| 1371 } | |
| 1372 source = from->extSubset; | |
| 1373 if ((source != NULL) && (source->entities != NULL)) { | |
| 1374 xmlXIncludeMergeData data; | |
| 1375 | |
| 1376 data.ctxt = ctxt; | |
| 1377 data.doc = doc; | |
| 1378 | |
| 1379 /* | |
| 1380 * don't duplicate existing stuff when external subsets are the same | |
| 1381 */ | |
| 1382 if ((!xmlStrEqual(target->ExternalID, source->ExternalID)) && | |
| 1383 (!xmlStrEqual(target->SystemID, source->SystemID))) { | |
| 1384 xmlHashScan((xmlHashTablePtr) source->entities, | |
| 1385 (xmlHashScanner) xmlXIncludeMergeEntity, &data); | |
| 1386 } | |
| 1387 } | |
| 1388 return(0); | |
| 1389 } | |
| 1390 | |
| 1391 /** | |
| 1392 * xmlXIncludeLoadDoc: | |
| 1393 * @ctxt: the XInclude context | |
| 1394 * @url: the associated URL | |
| 1395 * @nr: the xinclude node number | |
| 1396 * | |
| 1397 * Load the document, and store the result in the XInclude context | |
| 1398 * | |
| 1399 * Returns 0 in case of success, -1 in case of failure | |
| 1400 */ | |
| 1401 static int | |
| 1402 xmlXIncludeLoadDoc(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) { | |
| 1403 xmlDocPtr doc; | |
| 1404 xmlURIPtr uri; | |
| 1405 xmlChar *URL; | |
| 1406 xmlChar *fragment = NULL; | |
| 1407 int i = 0; | |
| 1408 #ifdef LIBXML_XPTR_ENABLED | |
| 1409 int saveFlags; | |
| 1410 #endif | |
| 1411 | |
| 1412 #ifdef DEBUG_XINCLUDE | |
| 1413 xmlGenericError(xmlGenericErrorContext, "Loading doc %s:%d\n", url, nr); | |
| 1414 #endif | |
| 1415 /* | |
| 1416 * Check the URL and remove any fragment identifier | |
| 1417 */ | |
| 1418 uri = xmlParseURI((const char *)url); | |
| 1419 if (uri == NULL) { | |
| 1420 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1421 XML_XINCLUDE_HREF_URI, | |
| 1422 "invalid value URI %s\n", url); | |
| 1423 return(-1); | |
| 1424 } | |
| 1425 if (uri->fragment != NULL) { | |
| 1426 fragment = (xmlChar *) uri->fragment; | |
| 1427 uri->fragment = NULL; | |
| 1428 } | |
| 1429 if ((ctxt->incTab != NULL) && (ctxt->incTab[nr] != NULL) && | |
| 1430 (ctxt->incTab[nr]->fragment != NULL)) { | |
| 1431 if (fragment != NULL) xmlFree(fragment); | |
| 1432 fragment = xmlStrdup(ctxt->incTab[nr]->fragment); | |
| 1433 } | |
| 1434 URL = xmlSaveUri(uri); | |
| 1435 xmlFreeURI(uri); | |
| 1436 if (URL == NULL) { | |
| 1437 if (ctxt->incTab != NULL) | |
| 1438 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1439 XML_XINCLUDE_HREF_URI, | |
| 1440 "invalid value URI %s\n", url); | |
| 1441 else | |
| 1442 xmlXIncludeErr(ctxt, NULL, | |
| 1443 XML_XINCLUDE_HREF_URI, | |
| 1444 "invalid value URI %s\n", url); | |
| 1445 if (fragment != NULL) | |
| 1446 xmlFree(fragment); | |
| 1447 return(-1); | |
| 1448 } | |
| 1449 | |
| 1450 /* | |
| 1451 * Handling of references to the local document are done | |
| 1452 * directly through ctxt->doc. | |
| 1453 */ | |
| 1454 if ((URL[0] == 0) || (URL[0] == '#') || | |
| 1455 ((ctxt->doc != NULL) && (xmlStrEqual(URL, ctxt->doc->URL)))) { | |
| 1456 doc = NULL; | |
| 1457 goto loaded; | |
| 1458 } | |
| 1459 | |
| 1460 /* | |
| 1461 * Prevent reloading twice the document. | |
| 1462 */ | |
| 1463 for (i = 0; i < ctxt->incNr; i++) { | |
| 1464 if ((xmlStrEqual(URL, ctxt->incTab[i]->URI)) && | |
| 1465 (ctxt->incTab[i]->doc != NULL)) { | |
| 1466 doc = ctxt->incTab[i]->doc; | |
| 1467 #ifdef DEBUG_XINCLUDE | |
| 1468 printf("Already loaded %s\n", URL); | |
| 1469 #endif | |
| 1470 goto loaded; | |
| 1471 } | |
| 1472 } | |
| 1473 | |
| 1474 /* | |
| 1475 * Load it. | |
| 1476 */ | |
| 1477 #ifdef DEBUG_XINCLUDE | |
| 1478 printf("loading %s\n", URL); | |
| 1479 #endif | |
| 1480 #ifdef LIBXML_XPTR_ENABLED | |
| 1481 /* | |
| 1482 * If this is an XPointer evaluation, we want to assure that | |
| 1483 * all entities have been resolved prior to processing the | |
| 1484 * referenced document | |
| 1485 */ | |
| 1486 saveFlags = ctxt->parseFlags; | |
| 1487 if (fragment != NULL) { /* if this is an XPointer eval */ | |
| 1488 ctxt->parseFlags |= XML_PARSE_NOENT; | |
| 1489 } | |
| 1490 #endif | |
| 1491 | |
| 1492 doc = xmlXIncludeParseFile(ctxt, (const char *)URL); | |
| 1493 #ifdef LIBXML_XPTR_ENABLED | |
| 1494 ctxt->parseFlags = saveFlags; | |
| 1495 #endif | |
| 1496 if (doc == NULL) { | |
| 1497 xmlFree(URL); | |
| 1498 if (fragment != NULL) | |
| 1499 xmlFree(fragment); | |
| 1500 return(-1); | |
| 1501 } | |
| 1502 ctxt->incTab[nr]->doc = doc; | |
| 1503 /* | |
| 1504 * It's possible that the requested URL has been mapped to a | |
| 1505 * completely different location (e.g. through a catalog entry). | |
| 1506 * To check for this, we compare the URL with that of the doc | |
| 1507 * and change it if they disagree (bug 146988). | |
| 1508 */ | |
| 1509 if (!xmlStrEqual(URL, doc->URL)) { | |
| 1510 xmlFree(URL); | |
| 1511 URL = xmlStrdup(doc->URL); | |
| 1512 } | |
| 1513 for (i = nr + 1; i < ctxt->incNr; i++) { | |
| 1514 if (xmlStrEqual(URL, ctxt->incTab[i]->URI)) { | |
| 1515 ctxt->incTab[nr]->count++; | |
| 1516 #ifdef DEBUG_XINCLUDE | |
| 1517 printf("Increasing %s count since reused\n", URL); | |
| 1518 #endif | |
| 1519 break; | |
| 1520 } | |
| 1521 } | |
| 1522 | |
| 1523 /* | |
| 1524 * Make sure we have all entities fixed up | |
| 1525 */ | |
| 1526 xmlXIncludeMergeEntities(ctxt, ctxt->doc, doc); | |
| 1527 | |
| 1528 /* | |
| 1529 * We don't need the DTD anymore, free up space | |
| 1530 if (doc->intSubset != NULL) { | |
| 1531 xmlUnlinkNode((xmlNodePtr) doc->intSubset); | |
| 1532 xmlFreeNode((xmlNodePtr) doc->intSubset); | |
| 1533 doc->intSubset = NULL; | |
| 1534 } | |
| 1535 if (doc->extSubset != NULL) { | |
| 1536 xmlUnlinkNode((xmlNodePtr) doc->extSubset); | |
| 1537 xmlFreeNode((xmlNodePtr) doc->extSubset); | |
| 1538 doc->extSubset = NULL; | |
| 1539 } | |
| 1540 */ | |
| 1541 xmlXIncludeRecurseDoc(ctxt, doc, URL); | |
| 1542 | |
| 1543 loaded: | |
| 1544 if (fragment == NULL) { | |
| 1545 /* | |
| 1546 * Add the top children list as the replacement copy. | |
| 1547 */ | |
| 1548 if (doc == NULL) | |
| 1549 { | |
| 1550 /* Hopefully a DTD declaration won't be copied from | |
| 1551 * the same document */ | |
| 1552 ctxt->incTab[nr]->inc = xmlCopyNodeList(ctxt->doc->children); | |
| 1553 } else { | |
| 1554 ctxt->incTab[nr]->inc = xmlXIncludeCopyNodeList(ctxt, ctxt->doc, | |
| 1555 doc, doc->children); | |
| 1556 } | |
| 1557 } | |
| 1558 #ifdef LIBXML_XPTR_ENABLED | |
| 1559 else { | |
| 1560 /* | |
| 1561 * Computes the XPointer expression and make a copy used | |
| 1562 * as the replacement copy. | |
| 1563 */ | |
| 1564 xmlXPathObjectPtr xptr; | |
| 1565 xmlXPathContextPtr xptrctxt; | |
| 1566 xmlNodeSetPtr set; | |
| 1567 | |
| 1568 if (doc == NULL) { | |
| 1569 xptrctxt = xmlXPtrNewContext(ctxt->doc, ctxt->incTab[nr]->ref, | |
| 1570 NULL); | |
| 1571 } else { | |
| 1572 xptrctxt = xmlXPtrNewContext(doc, NULL, NULL); | |
| 1573 } | |
| 1574 if (xptrctxt == NULL) { | |
| 1575 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1576 XML_XINCLUDE_XPTR_FAILED, | |
| 1577 "could not create XPointer context\n", NULL); | |
| 1578 xmlFree(URL); | |
| 1579 xmlFree(fragment); | |
| 1580 return(-1); | |
| 1581 } | |
| 1582 xptr = xmlXPtrEval(fragment, xptrctxt); | |
| 1583 if (xptr == NULL) { | |
| 1584 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1585 XML_XINCLUDE_XPTR_FAILED, | |
| 1586 "XPointer evaluation failed: #%s\n", | |
| 1587 fragment); | |
| 1588 xmlXPathFreeContext(xptrctxt); | |
| 1589 xmlFree(URL); | |
| 1590 xmlFree(fragment); | |
| 1591 return(-1); | |
| 1592 } | |
| 1593 switch (xptr->type) { | |
| 1594 case XPATH_UNDEFINED: | |
| 1595 case XPATH_BOOLEAN: | |
| 1596 case XPATH_NUMBER: | |
| 1597 case XPATH_STRING: | |
| 1598 case XPATH_POINT: | |
| 1599 case XPATH_USERS: | |
| 1600 case XPATH_XSLT_TREE: | |
| 1601 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1602 XML_XINCLUDE_XPTR_RESULT, | |
| 1603 "XPointer is not a range: #%s\n", | |
| 1604 fragment); | |
| 1605 xmlXPathFreeContext(xptrctxt); | |
| 1606 xmlFree(URL); | |
| 1607 xmlFree(fragment); | |
| 1608 return(-1); | |
| 1609 case XPATH_NODESET: | |
| 1610 if ((xptr->nodesetval == NULL) || | |
| 1611 (xptr->nodesetval->nodeNr <= 0)) { | |
| 1612 xmlXPathFreeContext(xptrctxt); | |
| 1613 xmlFree(URL); | |
| 1614 xmlFree(fragment); | |
| 1615 return(-1); | |
| 1616 } | |
| 1617 | |
| 1618 case XPATH_RANGE: | |
| 1619 case XPATH_LOCATIONSET: | |
| 1620 break; | |
| 1621 } | |
| 1622 set = xptr->nodesetval; | |
| 1623 if (set != NULL) { | |
| 1624 for (i = 0;i < set->nodeNr;i++) { | |
| 1625 if (set->nodeTab[i] == NULL) | |
| 1626 continue; | |
| 1627 switch (set->nodeTab[i]->type) { | |
| 1628 case XML_ELEMENT_NODE: | |
| 1629 case XML_TEXT_NODE: | |
| 1630 case XML_CDATA_SECTION_NODE: | |
| 1631 case XML_ENTITY_REF_NODE: | |
| 1632 case XML_ENTITY_NODE: | |
| 1633 case XML_PI_NODE: | |
| 1634 case XML_COMMENT_NODE: | |
| 1635 case XML_DOCUMENT_NODE: | |
| 1636 case XML_HTML_DOCUMENT_NODE: | |
| 1637 #ifdef LIBXML_DOCB_ENABLED | |
| 1638 case XML_DOCB_DOCUMENT_NODE: | |
| 1639 #endif | |
| 1640 continue; | |
| 1641 | |
| 1642 case XML_ATTRIBUTE_NODE: | |
| 1643 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1644 XML_XINCLUDE_XPTR_RESULT, | |
| 1645 "XPointer selects an attribute: #%s\n", | |
| 1646 fragment); | |
| 1647 set->nodeTab[i] = NULL; | |
| 1648 continue; | |
| 1649 case XML_NAMESPACE_DECL: | |
| 1650 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1651 XML_XINCLUDE_XPTR_RESULT, | |
| 1652 "XPointer selects a namespace: #%s\n", | |
| 1653 fragment); | |
| 1654 set->nodeTab[i] = NULL; | |
| 1655 continue; | |
| 1656 case XML_DOCUMENT_TYPE_NODE: | |
| 1657 case XML_DOCUMENT_FRAG_NODE: | |
| 1658 case XML_NOTATION_NODE: | |
| 1659 case XML_DTD_NODE: | |
| 1660 case XML_ELEMENT_DECL: | |
| 1661 case XML_ATTRIBUTE_DECL: | |
| 1662 case XML_ENTITY_DECL: | |
| 1663 case XML_XINCLUDE_START: | |
| 1664 case XML_XINCLUDE_END: | |
| 1665 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1666 XML_XINCLUDE_XPTR_RESULT, | |
| 1667 "XPointer selects unexpected nodes: #%s\n", | |
| 1668 fragment); | |
| 1669 set->nodeTab[i] = NULL; | |
| 1670 set->nodeTab[i] = NULL; | |
| 1671 continue; /* for */ | |
| 1672 } | |
| 1673 } | |
| 1674 } | |
| 1675 if (doc == NULL) { | |
| 1676 ctxt->incTab[nr]->xptr = xptr; | |
| 1677 ctxt->incTab[nr]->inc = NULL; | |
| 1678 } else { | |
| 1679 ctxt->incTab[nr]->inc = | |
| 1680 xmlXIncludeCopyXPointer(ctxt, ctxt->doc, doc, xptr); | |
| 1681 xmlXPathFreeObject(xptr); | |
| 1682 } | |
| 1683 xmlXPathFreeContext(xptrctxt); | |
| 1684 xmlFree(fragment); | |
| 1685 } | |
| 1686 #endif | |
| 1687 | |
| 1688 /* | |
| 1689 * Do the xml:base fixup if needed | |
| 1690 */ | |
| 1691 if ((doc != NULL) && (URL != NULL) && | |
| 1692 (!(ctxt->parseFlags & XML_PARSE_NOBASEFIX)) && | |
| 1693 (!(doc->parseFlags & XML_PARSE_NOBASEFIX))) { | |
| 1694 xmlNodePtr node; | |
| 1695 xmlChar *base; | |
| 1696 xmlChar *curBase; | |
| 1697 | |
| 1698 /* | |
| 1699 * The base is only adjusted if "necessary", i.e. if the xinclude node | |
| 1700 * has a base specified, or the URL is relative | |
| 1701 */ | |
| 1702 base = xmlGetNsProp(ctxt->incTab[nr]->ref, BAD_CAST "base", | |
| 1703 XML_XML_NAMESPACE); | |
| 1704 if (base == NULL) { | |
| 1705 /* | |
| 1706 * No xml:base on the xinclude node, so we check whether the | |
| 1707 * URI base is different than (relative to) the context base | |
| 1708 */ | |
| 1709 curBase = xmlBuildRelativeURI(URL, ctxt->base); | |
| 1710 if (curBase == NULL) { /* Error return */ | |
| 1711 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1712 XML_XINCLUDE_HREF_URI, | |
| 1713 "trying to build relative URI from %s\n", URL); | |
| 1714 } else { | |
| 1715 /* If the URI doesn't contain a slash, it's not relative */ | |
| 1716 if (!xmlStrchr(curBase, (xmlChar) '/')) | |
| 1717 xmlFree(curBase); | |
| 1718 else | |
| 1719 base = curBase; | |
| 1720 } | |
| 1721 } | |
| 1722 if (base != NULL) { /* Adjustment may be needed */ | |
| 1723 node = ctxt->incTab[nr]->inc; | |
| 1724 while (node != NULL) { | |
| 1725 /* Only work on element nodes */ | |
| 1726 if (node->type == XML_ELEMENT_NODE) { | |
| 1727 curBase = xmlNodeGetBase(node->doc, node); | |
| 1728 /* If no current base, set it */ | |
| 1729 if (curBase == NULL) { | |
| 1730 xmlNodeSetBase(node, base); | |
| 1731 } else { | |
| 1732 /* | |
| 1733 * If the current base is the same as the | |
| 1734 * URL of the document, then reset it to be | |
| 1735 * the specified xml:base or the relative URI | |
| 1736 */ | |
| 1737 if (xmlStrEqual(curBase, node->doc->URL)) { | |
| 1738 xmlNodeSetBase(node, base); | |
| 1739 } else { | |
| 1740 /* | |
| 1741 * If the element already has an xml:base | |
| 1742 * set, then relativise it if necessary | |
| 1743 */ | |
| 1744 xmlChar *xmlBase; | |
| 1745 xmlBase = xmlGetNsProp(node, | |
| 1746 BAD_CAST "base", | |
| 1747 XML_XML_NAMESPACE); | |
| 1748 if (xmlBase != NULL) { | |
| 1749 xmlChar *relBase; | |
| 1750 relBase = xmlBuildURI(xmlBase, base); | |
| 1751 if (relBase == NULL) { /* error */ | |
| 1752 xmlXIncludeErr(ctxt, | |
| 1753 ctxt->incTab[nr]->ref, | |
| 1754 XML_XINCLUDE_HREF_URI, | |
| 1755 "trying to rebuild base from %s\n", | |
| 1756 xmlBase); | |
| 1757 } else { | |
| 1758 xmlNodeSetBase(node, relBase); | |
| 1759 xmlFree(relBase); | |
| 1760 } | |
| 1761 xmlFree(xmlBase); | |
| 1762 } | |
| 1763 } | |
| 1764 xmlFree(curBase); | |
| 1765 } | |
| 1766 } | |
| 1767 node = node->next; | |
| 1768 } | |
| 1769 xmlFree(base); | |
| 1770 } | |
| 1771 } | |
| 1772 if ((nr < ctxt->incNr) && (ctxt->incTab[nr]->doc != NULL) && | |
| 1773 (ctxt->incTab[nr]->count <= 1)) { | |
| 1774 #ifdef DEBUG_XINCLUDE | |
| 1775 printf("freeing %s\n", ctxt->incTab[nr]->doc->URL); | |
| 1776 #endif | |
| 1777 xmlFreeDoc(ctxt->incTab[nr]->doc); | |
| 1778 ctxt->incTab[nr]->doc = NULL; | |
| 1779 } | |
| 1780 xmlFree(URL); | |
| 1781 return(0); | |
| 1782 } | |
| 1783 | |
| 1784 /** | |
| 1785 * xmlXIncludeLoadTxt: | |
| 1786 * @ctxt: the XInclude context | |
| 1787 * @url: the associated URL | |
| 1788 * @nr: the xinclude node number | |
| 1789 * | |
| 1790 * Load the content, and store the result in the XInclude context | |
| 1791 * | |
| 1792 * Returns 0 in case of success, -1 in case of failure | |
| 1793 */ | |
| 1794 static int | |
| 1795 xmlXIncludeLoadTxt(xmlXIncludeCtxtPtr ctxt, const xmlChar *url, int nr) { | |
| 1796 xmlParserInputBufferPtr buf; | |
| 1797 xmlNodePtr node; | |
| 1798 xmlURIPtr uri; | |
| 1799 xmlChar *URL; | |
| 1800 int i; | |
| 1801 xmlChar *encoding = NULL; | |
| 1802 xmlCharEncoding enc = (xmlCharEncoding) 0; | |
| 1803 xmlParserCtxtPtr pctxt; | |
| 1804 xmlParserInputPtr inputStream; | |
| 1805 int xinclude_multibyte_fallback_used = 0; | |
| 1806 | |
| 1807 /* | |
| 1808 * Check the URL and remove any fragment identifier | |
| 1809 */ | |
| 1810 uri = xmlParseURI((const char *)url); | |
| 1811 if (uri == NULL) { | |
| 1812 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, XML_XINCLUDE_HREF_URI, | |
| 1813 "invalid value URI %s\n", url); | |
| 1814 return(-1); | |
| 1815 } | |
| 1816 if (uri->fragment != NULL) { | |
| 1817 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, XML_XINCLUDE_TEXT_FRAGMENT, | |
| 1818 "fragment identifier forbidden for text: %s\n", | |
| 1819 (const xmlChar *) uri->fragment); | |
| 1820 xmlFreeURI(uri); | |
| 1821 return(-1); | |
| 1822 } | |
| 1823 URL = xmlSaveUri(uri); | |
| 1824 xmlFreeURI(uri); | |
| 1825 if (URL == NULL) { | |
| 1826 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, XML_XINCLUDE_HREF_URI, | |
| 1827 "invalid value URI %s\n", url); | |
| 1828 return(-1); | |
| 1829 } | |
| 1830 | |
| 1831 /* | |
| 1832 * Handling of references to the local document are done | |
| 1833 * directly through ctxt->doc. | |
| 1834 */ | |
| 1835 if (URL[0] == 0) { | |
| 1836 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1837 XML_XINCLUDE_TEXT_DOCUMENT, | |
| 1838 "text serialization of document not available\n", NULL); | |
| 1839 xmlFree(URL); | |
| 1840 return(-1); | |
| 1841 } | |
| 1842 | |
| 1843 /* | |
| 1844 * Prevent reloading twice the document. | |
| 1845 */ | |
| 1846 for (i = 0; i < ctxt->txtNr; i++) { | |
| 1847 if (xmlStrEqual(URL, ctxt->txturlTab[i])) { | |
| 1848 node = xmlCopyNode(ctxt->txtTab[i], 1); | |
| 1849 goto loaded; | |
| 1850 } | |
| 1851 } | |
| 1852 /* | |
| 1853 * Try to get the encoding if available | |
| 1854 */ | |
| 1855 if ((ctxt->incTab[nr] != NULL) && (ctxt->incTab[nr]->ref != NULL)) { | |
| 1856 encoding = xmlGetProp(ctxt->incTab[nr]->ref, XINCLUDE_PARSE_ENCODING); | |
| 1857 } | |
| 1858 if (encoding != NULL) { | |
| 1859 /* | |
| 1860 * TODO: we should not have to remap to the xmlCharEncoding | |
| 1861 * predefined set, a better interface than | |
| 1862 * xmlParserInputBufferCreateFilename should allow any | |
| 1863 * encoding supported by iconv | |
| 1864 */ | |
| 1865 enc = xmlParseCharEncoding((const char *) encoding); | |
| 1866 if (enc == XML_CHAR_ENCODING_ERROR) { | |
| 1867 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1868 XML_XINCLUDE_UNKNOWN_ENCODING, | |
| 1869 "encoding %s not supported\n", encoding); | |
| 1870 xmlFree(encoding); | |
| 1871 xmlFree(URL); | |
| 1872 return(-1); | |
| 1873 } | |
| 1874 xmlFree(encoding); | |
| 1875 } | |
| 1876 | |
| 1877 /* | |
| 1878 * Load it. | |
| 1879 */ | |
| 1880 pctxt = xmlNewParserCtxt(); | |
| 1881 inputStream = xmlLoadExternalEntity((const char*)URL, NULL, pctxt); | |
| 1882 if(inputStream == NULL) { | |
| 1883 xmlFreeParserCtxt(pctxt); | |
| 1884 xmlFree(URL); | |
| 1885 return(-1); | |
| 1886 } | |
| 1887 buf = inputStream->buf; | |
| 1888 if (buf == NULL) { | |
| 1889 xmlFreeInputStream (inputStream); | |
| 1890 xmlFreeParserCtxt(pctxt); | |
| 1891 xmlFree(URL); | |
| 1892 return(-1); | |
| 1893 } | |
| 1894 if (buf->encoder) | |
| 1895 xmlCharEncCloseFunc(buf->encoder); | |
| 1896 buf->encoder = xmlGetCharEncodingHandler(enc); | |
| 1897 node = xmlNewText(NULL); | |
| 1898 | |
| 1899 /* | |
| 1900 * Scan all chars from the resource and add the to the node | |
| 1901 */ | |
| 1902 xinclude_multibyte_fallback: | |
| 1903 while (xmlParserInputBufferRead(buf, 128) > 0) { | |
| 1904 int len; | |
| 1905 const xmlChar *content; | |
| 1906 | |
| 1907 content = xmlBufContent(buf->buffer); | |
| 1908 len = xmlBufLength(buf->buffer); | |
| 1909 for (i = 0;i < len;) { | |
| 1910 int cur; | |
| 1911 int l; | |
| 1912 | |
| 1913 cur = xmlStringCurrentChar(NULL, &content[i], &l); | |
| 1914 if (!IS_CHAR(cur)) { | |
| 1915 /* Handle splitted multibyte char at buffer boundary */ | |
| 1916 if (((len - i) < 4) && (!xinclude_multibyte_fallback_used)) { | |
| 1917 xinclude_multibyte_fallback_used = 1; | |
| 1918 xmlBufShrink(buf->buffer, i); | |
| 1919 goto xinclude_multibyte_fallback; | |
| 1920 } else { | |
| 1921 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 1922 XML_XINCLUDE_INVALID_CHAR, | |
| 1923 "%s contains invalid char\n", URL); | |
| 1924 xmlFreeParserInputBuffer(buf); | |
| 1925 xmlFree(URL); | |
| 1926 return(-1); | |
| 1927 } | |
| 1928 } else { | |
| 1929 xinclude_multibyte_fallback_used = 0; | |
| 1930 xmlNodeAddContentLen(node, &content[i], l); | |
| 1931 } | |
| 1932 i += l; | |
| 1933 } | |
| 1934 xmlBufShrink(buf->buffer, len); | |
| 1935 } | |
| 1936 xmlFreeParserCtxt(pctxt); | |
| 1937 xmlXIncludeAddTxt(ctxt, node, URL); | |
| 1938 xmlFreeInputStream(inputStream); | |
| 1939 | |
| 1940 loaded: | |
| 1941 /* | |
| 1942 * Add the element as the replacement copy. | |
| 1943 */ | |
| 1944 ctxt->incTab[nr]->inc = node; | |
| 1945 xmlFree(URL); | |
| 1946 return(0); | |
| 1947 } | |
| 1948 | |
| 1949 /** | |
| 1950 * xmlXIncludeLoadFallback: | |
| 1951 * @ctxt: the XInclude context | |
| 1952 * @fallback: the fallback node | |
| 1953 * @nr: the xinclude node number | |
| 1954 * | |
| 1955 * Load the content of the fallback node, and store the result | |
| 1956 * in the XInclude context | |
| 1957 * | |
| 1958 * Returns 0 in case of success, -1 in case of failure | |
| 1959 */ | |
| 1960 static int | |
| 1961 xmlXIncludeLoadFallback(xmlXIncludeCtxtPtr ctxt, xmlNodePtr fallback, int nr) { | |
| 1962 xmlXIncludeCtxtPtr newctxt; | |
| 1963 int ret = 0; | |
| 1964 | |
| 1965 if ((fallback == NULL) || (fallback->type == XML_NAMESPACE_DECL) || | |
| 1966 (ctxt == NULL)) | |
| 1967 return(-1); | |
| 1968 if (fallback->children != NULL) { | |
| 1969 /* | |
| 1970 * It's possible that the fallback also has 'includes' | |
| 1971 * (Bug 129969), so we re-process the fallback just in case | |
| 1972 */ | |
| 1973 newctxt = xmlXIncludeNewContext(ctxt->doc); | |
| 1974 if (newctxt == NULL) | |
| 1975 return (-1); | |
| 1976 newctxt->_private = ctxt->_private; | |
| 1977 newctxt->base = xmlStrdup(ctxt->base); /* Inherit the base from the exi
sting context */ | |
| 1978 xmlXIncludeSetFlags(newctxt, ctxt->parseFlags); | |
| 1979 ret = xmlXIncludeDoProcess(newctxt, ctxt->doc, fallback->children); | |
| 1980 if (ctxt->nbErrors > 0) | |
| 1981 ret = -1; | |
| 1982 else if (ret > 0) | |
| 1983 ret = 0; /* xmlXIncludeDoProcess can return +ve number */ | |
| 1984 xmlXIncludeFreeContext(newctxt); | |
| 1985 | |
| 1986 ctxt->incTab[nr]->inc = xmlDocCopyNodeList(ctxt->doc, | |
| 1987 fallback->children); | |
| 1988 } else { | |
| 1989 ctxt->incTab[nr]->inc = NULL; | |
| 1990 ctxt->incTab[nr]->emptyFb = 1; /* flag empty callback */ | |
| 1991 } | |
| 1992 return(ret); | |
| 1993 } | |
| 1994 | |
| 1995 /************************************************************************ | |
| 1996 * * | |
| 1997 * XInclude Processing * | |
| 1998 * * | |
| 1999 ************************************************************************/ | |
| 2000 | |
| 2001 /** | |
| 2002 * xmlXIncludePreProcessNode: | |
| 2003 * @ctxt: an XInclude context | |
| 2004 * @node: an XInclude node | |
| 2005 * | |
| 2006 * Implement the XInclude preprocessing, currently just adding the element | |
| 2007 * for further processing. | |
| 2008 * | |
| 2009 * Returns the result list or NULL in case of error | |
| 2010 */ | |
| 2011 static xmlNodePtr | |
| 2012 xmlXIncludePreProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { | |
| 2013 xmlXIncludeAddNode(ctxt, node); | |
| 2014 return(NULL); | |
| 2015 } | |
| 2016 | |
| 2017 /** | |
| 2018 * xmlXIncludeLoadNode: | |
| 2019 * @ctxt: an XInclude context | |
| 2020 * @nr: the node number | |
| 2021 * | |
| 2022 * Find and load the infoset replacement for the given node. | |
| 2023 * | |
| 2024 * Returns 0 if substitution succeeded, -1 if some processing failed | |
| 2025 */ | |
| 2026 static int | |
| 2027 xmlXIncludeLoadNode(xmlXIncludeCtxtPtr ctxt, int nr) { | |
| 2028 xmlNodePtr cur; | |
| 2029 xmlChar *href; | |
| 2030 xmlChar *parse; | |
| 2031 xmlChar *base; | |
| 2032 xmlChar *oldBase; | |
| 2033 xmlChar *URI; | |
| 2034 int xml = 1; /* default Issue 64 */ | |
| 2035 int ret; | |
| 2036 | |
| 2037 if (ctxt == NULL) | |
| 2038 return(-1); | |
| 2039 if ((nr < 0) || (nr >= ctxt->incNr)) | |
| 2040 return(-1); | |
| 2041 cur = ctxt->incTab[nr]->ref; | |
| 2042 if (cur == NULL) | |
| 2043 return(-1); | |
| 2044 | |
| 2045 /* | |
| 2046 * read the attributes | |
| 2047 */ | |
| 2048 href = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_HREF); | |
| 2049 if (href == NULL) { | |
| 2050 href = xmlStrdup(BAD_CAST ""); /* @@@@ href is now optional */ | |
| 2051 if (href == NULL) | |
| 2052 return(-1); | |
| 2053 } | |
| 2054 parse = xmlXIncludeGetProp(ctxt, cur, XINCLUDE_PARSE); | |
| 2055 if (parse != NULL) { | |
| 2056 if (xmlStrEqual(parse, XINCLUDE_PARSE_XML)) | |
| 2057 xml = 1; | |
| 2058 else if (xmlStrEqual(parse, XINCLUDE_PARSE_TEXT)) | |
| 2059 xml = 0; | |
| 2060 else { | |
| 2061 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 2062 XML_XINCLUDE_PARSE_VALUE, | |
| 2063 "invalid value %s for 'parse'\n", parse); | |
| 2064 if (href != NULL) | |
| 2065 xmlFree(href); | |
| 2066 if (parse != NULL) | |
| 2067 xmlFree(parse); | |
| 2068 return(-1); | |
| 2069 } | |
| 2070 } | |
| 2071 | |
| 2072 /* | |
| 2073 * compute the URI | |
| 2074 */ | |
| 2075 base = xmlNodeGetBase(ctxt->doc, cur); | |
| 2076 if (base == NULL) { | |
| 2077 URI = xmlBuildURI(href, ctxt->doc->URL); | |
| 2078 } else { | |
| 2079 URI = xmlBuildURI(href, base); | |
| 2080 } | |
| 2081 if (URI == NULL) { | |
| 2082 xmlChar *escbase; | |
| 2083 xmlChar *eschref; | |
| 2084 /* | |
| 2085 * Some escaping may be needed | |
| 2086 */ | |
| 2087 escbase = xmlURIEscape(base); | |
| 2088 eschref = xmlURIEscape(href); | |
| 2089 URI = xmlBuildURI(eschref, escbase); | |
| 2090 if (escbase != NULL) | |
| 2091 xmlFree(escbase); | |
| 2092 if (eschref != NULL) | |
| 2093 xmlFree(eschref); | |
| 2094 } | |
| 2095 if (URI == NULL) { | |
| 2096 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 2097 XML_XINCLUDE_HREF_URI, "failed build URL\n", NULL); | |
| 2098 if (parse != NULL) | |
| 2099 xmlFree(parse); | |
| 2100 if (href != NULL) | |
| 2101 xmlFree(href); | |
| 2102 if (base != NULL) | |
| 2103 xmlFree(base); | |
| 2104 return(-1); | |
| 2105 } | |
| 2106 #ifdef DEBUG_XINCLUDE | |
| 2107 xmlGenericError(xmlGenericErrorContext, "parse: %s\n", | |
| 2108 xml ? "xml": "text"); | |
| 2109 xmlGenericError(xmlGenericErrorContext, "URI: %s\n", URI); | |
| 2110 #endif | |
| 2111 | |
| 2112 /* | |
| 2113 * Save the base for this include (saving the current one) | |
| 2114 */ | |
| 2115 oldBase = ctxt->base; | |
| 2116 ctxt->base = base; | |
| 2117 | |
| 2118 if (xml) { | |
| 2119 ret = xmlXIncludeLoadDoc(ctxt, URI, nr); | |
| 2120 /* xmlXIncludeGetFragment(ctxt, cur, URI); */ | |
| 2121 } else { | |
| 2122 ret = xmlXIncludeLoadTxt(ctxt, URI, nr); | |
| 2123 } | |
| 2124 | |
| 2125 /* | |
| 2126 * Restore the original base before checking for fallback | |
| 2127 */ | |
| 2128 ctxt->base = oldBase; | |
| 2129 | |
| 2130 if (ret < 0) { | |
| 2131 xmlNodePtr children; | |
| 2132 | |
| 2133 /* | |
| 2134 * Time to try a fallback if availble | |
| 2135 */ | |
| 2136 #ifdef DEBUG_XINCLUDE | |
| 2137 xmlGenericError(xmlGenericErrorContext, "error looking for fallback\n"); | |
| 2138 #endif | |
| 2139 children = cur->children; | |
| 2140 while (children != NULL) { | |
| 2141 if ((children->type == XML_ELEMENT_NODE) && | |
| 2142 (children->ns != NULL) && | |
| 2143 (xmlStrEqual(children->name, XINCLUDE_FALLBACK)) && | |
| 2144 ((xmlStrEqual(children->ns->href, XINCLUDE_NS)) || | |
| 2145 (xmlStrEqual(children->ns->href, XINCLUDE_OLD_NS)))) { | |
| 2146 ret = xmlXIncludeLoadFallback(ctxt, children, nr); | |
| 2147 if (ret == 0) | |
| 2148 break; | |
| 2149 } | |
| 2150 children = children->next; | |
| 2151 } | |
| 2152 } | |
| 2153 if (ret < 0) { | |
| 2154 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 2155 XML_XINCLUDE_NO_FALLBACK, | |
| 2156 "could not load %s, and no fallback was found\n", | |
| 2157 URI); | |
| 2158 } | |
| 2159 | |
| 2160 /* | |
| 2161 * Cleanup | |
| 2162 */ | |
| 2163 if (URI != NULL) | |
| 2164 xmlFree(URI); | |
| 2165 if (parse != NULL) | |
| 2166 xmlFree(parse); | |
| 2167 if (href != NULL) | |
| 2168 xmlFree(href); | |
| 2169 if (base != NULL) | |
| 2170 xmlFree(base); | |
| 2171 return(0); | |
| 2172 } | |
| 2173 | |
| 2174 /** | |
| 2175 * xmlXIncludeIncludeNode: | |
| 2176 * @ctxt: an XInclude context | |
| 2177 * @nr: the node number | |
| 2178 * | |
| 2179 * Inplement the infoset replacement for the given node | |
| 2180 * | |
| 2181 * Returns 0 if substitution succeeded, -1 if some processing failed | |
| 2182 */ | |
| 2183 static int | |
| 2184 xmlXIncludeIncludeNode(xmlXIncludeCtxtPtr ctxt, int nr) { | |
| 2185 xmlNodePtr cur, end, list, tmp; | |
| 2186 | |
| 2187 if (ctxt == NULL) | |
| 2188 return(-1); | |
| 2189 if ((nr < 0) || (nr >= ctxt->incNr)) | |
| 2190 return(-1); | |
| 2191 cur = ctxt->incTab[nr]->ref; | |
| 2192 if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) | |
| 2193 return(-1); | |
| 2194 | |
| 2195 /* | |
| 2196 * If we stored an XPointer a late computation may be needed | |
| 2197 */ | |
| 2198 if ((ctxt->incTab[nr]->inc == NULL) && | |
| 2199 (ctxt->incTab[nr]->xptr != NULL)) { | |
| 2200 ctxt->incTab[nr]->inc = | |
| 2201 xmlXIncludeCopyXPointer(ctxt, ctxt->doc, ctxt->doc, | |
| 2202 ctxt->incTab[nr]->xptr); | |
| 2203 xmlXPathFreeObject(ctxt->incTab[nr]->xptr); | |
| 2204 ctxt->incTab[nr]->xptr = NULL; | |
| 2205 } | |
| 2206 list = ctxt->incTab[nr]->inc; | |
| 2207 ctxt->incTab[nr]->inc = NULL; | |
| 2208 | |
| 2209 /* | |
| 2210 * Check against the risk of generating a multi-rooted document | |
| 2211 */ | |
| 2212 if ((cur->parent != NULL) && | |
| 2213 (cur->parent->type != XML_ELEMENT_NODE)) { | |
| 2214 int nb_elem = 0; | |
| 2215 | |
| 2216 tmp = list; | |
| 2217 while (tmp != NULL) { | |
| 2218 if (tmp->type == XML_ELEMENT_NODE) | |
| 2219 nb_elem++; | |
| 2220 tmp = tmp->next; | |
| 2221 } | |
| 2222 if (nb_elem > 1) { | |
| 2223 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 2224 XML_XINCLUDE_MULTIPLE_ROOT, | |
| 2225 "XInclude error: would result in multiple root nodes\n", | |
| 2226 NULL); | |
| 2227 return(-1); | |
| 2228 } | |
| 2229 } | |
| 2230 | |
| 2231 if (ctxt->parseFlags & XML_PARSE_NOXINCNODE) { | |
| 2232 /* | |
| 2233 * Add the list of nodes | |
| 2234 */ | |
| 2235 while (list != NULL) { | |
| 2236 end = list; | |
| 2237 list = list->next; | |
| 2238 | |
| 2239 xmlAddPrevSibling(cur, end); | |
| 2240 } | |
| 2241 xmlUnlinkNode(cur); | |
| 2242 xmlFreeNode(cur); | |
| 2243 } else { | |
| 2244 /* | |
| 2245 * Change the current node as an XInclude start one, and add an | |
| 2246 * XInclude end one | |
| 2247 */ | |
| 2248 cur->type = XML_XINCLUDE_START; | |
| 2249 end = xmlNewDocNode(cur->doc, cur->ns, cur->name, NULL); | |
| 2250 if (end == NULL) { | |
| 2251 xmlXIncludeErr(ctxt, ctxt->incTab[nr]->ref, | |
| 2252 XML_XINCLUDE_BUILD_FAILED, | |
| 2253 "failed to build node\n", NULL); | |
| 2254 return(-1); | |
| 2255 } | |
| 2256 end->type = XML_XINCLUDE_END; | |
| 2257 xmlAddNextSibling(cur, end); | |
| 2258 | |
| 2259 /* | |
| 2260 * Add the list of nodes | |
| 2261 */ | |
| 2262 while (list != NULL) { | |
| 2263 cur = list; | |
| 2264 list = list->next; | |
| 2265 | |
| 2266 xmlAddPrevSibling(end, cur); | |
| 2267 } | |
| 2268 } | |
| 2269 | |
| 2270 | |
| 2271 return(0); | |
| 2272 } | |
| 2273 | |
| 2274 /** | |
| 2275 * xmlXIncludeTestNode: | |
| 2276 * @ctxt: the XInclude processing context | |
| 2277 * @node: an XInclude node | |
| 2278 * | |
| 2279 * test if the node is an XInclude node | |
| 2280 * | |
| 2281 * Returns 1 true, 0 otherwise | |
| 2282 */ | |
| 2283 static int | |
| 2284 xmlXIncludeTestNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { | |
| 2285 if (node == NULL) | |
| 2286 return(0); | |
| 2287 if (node->type != XML_ELEMENT_NODE) | |
| 2288 return(0); | |
| 2289 if (node->ns == NULL) | |
| 2290 return(0); | |
| 2291 if ((xmlStrEqual(node->ns->href, XINCLUDE_NS)) || | |
| 2292 (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS))) { | |
| 2293 if (xmlStrEqual(node->ns->href, XINCLUDE_OLD_NS)) { | |
| 2294 if (ctxt->legacy == 0) { | |
| 2295 #if 0 /* wait for the XML Core Working Group to get something stable ! */ | |
| 2296 xmlXIncludeWarn(ctxt, node, XML_XINCLUDE_DEPRECATED_NS, | |
| 2297 "Deprecated XInclude namespace found, use %s", | |
| 2298 XINCLUDE_NS); | |
| 2299 #endif | |
| 2300 ctxt->legacy = 1; | |
| 2301 } | |
| 2302 } | |
| 2303 if (xmlStrEqual(node->name, XINCLUDE_NODE)) { | |
| 2304 xmlNodePtr child = node->children; | |
| 2305 int nb_fallback = 0; | |
| 2306 | |
| 2307 while (child != NULL) { | |
| 2308 if ((child->type == XML_ELEMENT_NODE) && | |
| 2309 (child->ns != NULL) && | |
| 2310 ((xmlStrEqual(child->ns->href, XINCLUDE_NS)) || | |
| 2311 (xmlStrEqual(child->ns->href, XINCLUDE_OLD_NS)))) { | |
| 2312 if (xmlStrEqual(child->name, XINCLUDE_NODE)) { | |
| 2313 xmlXIncludeErr(ctxt, node, | |
| 2314 XML_XINCLUDE_INCLUDE_IN_INCLUDE, | |
| 2315 "%s has an 'include' child\n", | |
| 2316 XINCLUDE_NODE); | |
| 2317 return(0); | |
| 2318 } | |
| 2319 if (xmlStrEqual(child->name, XINCLUDE_FALLBACK)) { | |
| 2320 nb_fallback++; | |
| 2321 } | |
| 2322 } | |
| 2323 child = child->next; | |
| 2324 } | |
| 2325 if (nb_fallback > 1) { | |
| 2326 xmlXIncludeErr(ctxt, node, XML_XINCLUDE_FALLBACKS_IN_INCLUDE, | |
| 2327 "%s has multiple fallback children\n", | |
| 2328 XINCLUDE_NODE); | |
| 2329 return(0); | |
| 2330 } | |
| 2331 return(1); | |
| 2332 } | |
| 2333 if (xmlStrEqual(node->name, XINCLUDE_FALLBACK)) { | |
| 2334 if ((node->parent == NULL) || | |
| 2335 (node->parent->type != XML_ELEMENT_NODE) || | |
| 2336 (node->parent->ns == NULL) || | |
| 2337 ((!xmlStrEqual(node->parent->ns->href, XINCLUDE_NS)) && | |
| 2338 (!xmlStrEqual(node->parent->ns->href, XINCLUDE_OLD_NS))) || | |
| 2339 (!xmlStrEqual(node->parent->name, XINCLUDE_NODE))) { | |
| 2340 xmlXIncludeErr(ctxt, node, | |
| 2341 XML_XINCLUDE_FALLBACK_NOT_IN_INCLUDE, | |
| 2342 "%s is not the child of an 'include'\n", | |
| 2343 XINCLUDE_FALLBACK); | |
| 2344 } | |
| 2345 } | |
| 2346 } | |
| 2347 return(0); | |
| 2348 } | |
| 2349 | |
| 2350 /** | |
| 2351 * xmlXIncludeDoProcess: | |
| 2352 * @ctxt: the XInclude processing context | |
| 2353 * @doc: an XML document | |
| 2354 * @tree: the top of the tree to process | |
| 2355 * | |
| 2356 * Implement the XInclude substitution on the XML document @doc | |
| 2357 * | |
| 2358 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2359 * or the number of substitutions done. | |
| 2360 */ | |
| 2361 static int | |
| 2362 xmlXIncludeDoProcess(xmlXIncludeCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr tree) { | |
| 2363 xmlNodePtr cur; | |
| 2364 int ret = 0; | |
| 2365 int i, start; | |
| 2366 | |
| 2367 if ((doc == NULL) || (tree == NULL) || (tree->type == XML_NAMESPACE_DECL)) | |
| 2368 return(-1); | |
| 2369 if (ctxt == NULL) | |
| 2370 return(-1); | |
| 2371 | |
| 2372 if (doc->URL != NULL) { | |
| 2373 ret = xmlXIncludeURLPush(ctxt, doc->URL); | |
| 2374 if (ret < 0) | |
| 2375 return(-1); | |
| 2376 } | |
| 2377 start = ctxt->incNr; | |
| 2378 | |
| 2379 /* | |
| 2380 * First phase: lookup the elements in the document | |
| 2381 */ | |
| 2382 cur = tree; | |
| 2383 if (xmlXIncludeTestNode(ctxt, cur) == 1) | |
| 2384 xmlXIncludePreProcessNode(ctxt, cur); | |
| 2385 while ((cur != NULL) && (cur != tree->parent)) { | |
| 2386 /* TODO: need to work on entities -> stack */ | |
| 2387 if ((cur->children != NULL) && | |
| 2388 (cur->children->type != XML_ENTITY_DECL) && | |
| 2389 (cur->children->type != XML_XINCLUDE_START) && | |
| 2390 (cur->children->type != XML_XINCLUDE_END)) { | |
| 2391 cur = cur->children; | |
| 2392 if (xmlXIncludeTestNode(ctxt, cur)) | |
| 2393 xmlXIncludePreProcessNode(ctxt, cur); | |
| 2394 } else if (cur->next != NULL) { | |
| 2395 cur = cur->next; | |
| 2396 if (xmlXIncludeTestNode(ctxt, cur)) | |
| 2397 xmlXIncludePreProcessNode(ctxt, cur); | |
| 2398 } else { | |
| 2399 if (cur == tree) | |
| 2400 break; | |
| 2401 do { | |
| 2402 cur = cur->parent; | |
| 2403 if ((cur == NULL) || (cur == tree->parent)) | |
| 2404 break; /* do */ | |
| 2405 if (cur->next != NULL) { | |
| 2406 cur = cur->next; | |
| 2407 if (xmlXIncludeTestNode(ctxt, cur)) | |
| 2408 xmlXIncludePreProcessNode(ctxt, cur); | |
| 2409 break; /* do */ | |
| 2410 } | |
| 2411 } while (cur != NULL); | |
| 2412 } | |
| 2413 } | |
| 2414 | |
| 2415 /* | |
| 2416 * Second Phase : collect the infosets fragments | |
| 2417 */ | |
| 2418 for (i = start;i < ctxt->incNr; i++) { | |
| 2419 xmlXIncludeLoadNode(ctxt, i); | |
| 2420 ret++; | |
| 2421 } | |
| 2422 | |
| 2423 /* | |
| 2424 * Third phase: extend the original document infoset. | |
| 2425 * | |
| 2426 * Originally we bypassed the inclusion if there were any errors | |
| 2427 * encountered on any of the XIncludes. A bug was raised (bug | |
| 2428 * 132588) requesting that we output the XIncludes without error, | |
| 2429 * so the check for inc!=NULL || xptr!=NULL was put in. This may | |
| 2430 * give some other problems in the future, but for now it seems to | |
| 2431 * work ok. | |
| 2432 * | |
| 2433 */ | |
| 2434 for (i = ctxt->incBase;i < ctxt->incNr; i++) { | |
| 2435 if ((ctxt->incTab[i]->inc != NULL) || | |
| 2436 (ctxt->incTab[i]->xptr != NULL) || | |
| 2437 (ctxt->incTab[i]->emptyFb != 0)) /* (empty fallback) */ | |
| 2438 xmlXIncludeIncludeNode(ctxt, i); | |
| 2439 } | |
| 2440 | |
| 2441 if (doc->URL != NULL) | |
| 2442 xmlXIncludeURLPop(ctxt); | |
| 2443 return(ret); | |
| 2444 } | |
| 2445 | |
| 2446 /** | |
| 2447 * xmlXIncludeSetFlags: | |
| 2448 * @ctxt: an XInclude processing context | |
| 2449 * @flags: a set of xmlParserOption used for parsing XML includes | |
| 2450 * | |
| 2451 * Set the flags used for further processing of XML resources. | |
| 2452 * | |
| 2453 * Returns 0 in case of success and -1 in case of error. | |
| 2454 */ | |
| 2455 int | |
| 2456 xmlXIncludeSetFlags(xmlXIncludeCtxtPtr ctxt, int flags) { | |
| 2457 if (ctxt == NULL) | |
| 2458 return(-1); | |
| 2459 ctxt->parseFlags = flags; | |
| 2460 return(0); | |
| 2461 } | |
| 2462 | |
| 2463 /** | |
| 2464 * xmlXIncludeProcessTreeFlagsData: | |
| 2465 * @tree: an XML node | |
| 2466 * @flags: a set of xmlParserOption used for parsing XML includes | |
| 2467 * @data: application data that will be passed to the parser context | |
| 2468 * in the _private field of the parser context(s) | |
| 2469 * | |
| 2470 * Implement the XInclude substitution on the XML node @tree | |
| 2471 * | |
| 2472 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2473 * or the number of substitutions done. | |
| 2474 */ | |
| 2475 | |
| 2476 int | |
| 2477 xmlXIncludeProcessTreeFlagsData(xmlNodePtr tree, int flags, void *data) { | |
| 2478 xmlXIncludeCtxtPtr ctxt; | |
| 2479 int ret = 0; | |
| 2480 | |
| 2481 if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL) || | |
| 2482 (tree->doc == NULL)) | |
| 2483 return(-1); | |
| 2484 | |
| 2485 ctxt = xmlXIncludeNewContext(tree->doc); | |
| 2486 if (ctxt == NULL) | |
| 2487 return(-1); | |
| 2488 ctxt->_private = data; | |
| 2489 ctxt->base = xmlStrdup((xmlChar *)tree->doc->URL); | |
| 2490 xmlXIncludeSetFlags(ctxt, flags); | |
| 2491 ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree); | |
| 2492 if ((ret >= 0) && (ctxt->nbErrors > 0)) | |
| 2493 ret = -1; | |
| 2494 | |
| 2495 xmlXIncludeFreeContext(ctxt); | |
| 2496 return(ret); | |
| 2497 } | |
| 2498 | |
| 2499 /** | |
| 2500 * xmlXIncludeProcessFlagsData: | |
| 2501 * @doc: an XML document | |
| 2502 * @flags: a set of xmlParserOption used for parsing XML includes | |
| 2503 * @data: application data that will be passed to the parser context | |
| 2504 * in the _private field of the parser context(s) | |
| 2505 * | |
| 2506 * Implement the XInclude substitution on the XML document @doc | |
| 2507 * | |
| 2508 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2509 * or the number of substitutions done. | |
| 2510 */ | |
| 2511 int | |
| 2512 xmlXIncludeProcessFlagsData(xmlDocPtr doc, int flags, void *data) { | |
| 2513 xmlNodePtr tree; | |
| 2514 | |
| 2515 if (doc == NULL) | |
| 2516 return(-1); | |
| 2517 tree = xmlDocGetRootElement(doc); | |
| 2518 if (tree == NULL) | |
| 2519 return(-1); | |
| 2520 return(xmlXIncludeProcessTreeFlagsData(tree, flags, data)); | |
| 2521 } | |
| 2522 | |
| 2523 /** | |
| 2524 * xmlXIncludeProcessFlags: | |
| 2525 * @doc: an XML document | |
| 2526 * @flags: a set of xmlParserOption used for parsing XML includes | |
| 2527 * | |
| 2528 * Implement the XInclude substitution on the XML document @doc | |
| 2529 * | |
| 2530 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2531 * or the number of substitutions done. | |
| 2532 */ | |
| 2533 int | |
| 2534 xmlXIncludeProcessFlags(xmlDocPtr doc, int flags) { | |
| 2535 return xmlXIncludeProcessFlagsData(doc, flags, NULL); | |
| 2536 } | |
| 2537 | |
| 2538 /** | |
| 2539 * xmlXIncludeProcess: | |
| 2540 * @doc: an XML document | |
| 2541 * | |
| 2542 * Implement the XInclude substitution on the XML document @doc | |
| 2543 * | |
| 2544 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2545 * or the number of substitutions done. | |
| 2546 */ | |
| 2547 int | |
| 2548 xmlXIncludeProcess(xmlDocPtr doc) { | |
| 2549 return(xmlXIncludeProcessFlags(doc, 0)); | |
| 2550 } | |
| 2551 | |
| 2552 /** | |
| 2553 * xmlXIncludeProcessTreeFlags: | |
| 2554 * @tree: a node in an XML document | |
| 2555 * @flags: a set of xmlParserOption used for parsing XML includes | |
| 2556 * | |
| 2557 * Implement the XInclude substitution for the given subtree | |
| 2558 * | |
| 2559 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2560 * or the number of substitutions done. | |
| 2561 */ | |
| 2562 int | |
| 2563 xmlXIncludeProcessTreeFlags(xmlNodePtr tree, int flags) { | |
| 2564 xmlXIncludeCtxtPtr ctxt; | |
| 2565 int ret = 0; | |
| 2566 | |
| 2567 if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL) || | |
| 2568 (tree->doc == NULL)) | |
| 2569 return(-1); | |
| 2570 ctxt = xmlXIncludeNewContext(tree->doc); | |
| 2571 if (ctxt == NULL) | |
| 2572 return(-1); | |
| 2573 ctxt->base = xmlNodeGetBase(tree->doc, tree); | |
| 2574 xmlXIncludeSetFlags(ctxt, flags); | |
| 2575 ret = xmlXIncludeDoProcess(ctxt, tree->doc, tree); | |
| 2576 if ((ret >= 0) && (ctxt->nbErrors > 0)) | |
| 2577 ret = -1; | |
| 2578 | |
| 2579 xmlXIncludeFreeContext(ctxt); | |
| 2580 return(ret); | |
| 2581 } | |
| 2582 | |
| 2583 /** | |
| 2584 * xmlXIncludeProcessTree: | |
| 2585 * @tree: a node in an XML document | |
| 2586 * | |
| 2587 * Implement the XInclude substitution for the given subtree | |
| 2588 * | |
| 2589 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2590 * or the number of substitutions done. | |
| 2591 */ | |
| 2592 int | |
| 2593 xmlXIncludeProcessTree(xmlNodePtr tree) { | |
| 2594 return(xmlXIncludeProcessTreeFlags(tree, 0)); | |
| 2595 } | |
| 2596 | |
| 2597 /** | |
| 2598 * xmlXIncludeProcessNode: | |
| 2599 * @ctxt: an existing XInclude context | |
| 2600 * @node: a node in an XML document | |
| 2601 * | |
| 2602 * Implement the XInclude substitution for the given subtree reusing | |
| 2603 * the informations and data coming from the given context. | |
| 2604 * | |
| 2605 * Returns 0 if no substitution were done, -1 if some processing failed | |
| 2606 * or the number of substitutions done. | |
| 2607 */ | |
| 2608 int | |
| 2609 xmlXIncludeProcessNode(xmlXIncludeCtxtPtr ctxt, xmlNodePtr node) { | |
| 2610 int ret = 0; | |
| 2611 | |
| 2612 if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || | |
| 2613 (node->doc == NULL) || (ctxt == NULL)) | |
| 2614 return(-1); | |
| 2615 ret = xmlXIncludeDoProcess(ctxt, node->doc, node); | |
| 2616 if ((ret >= 0) && (ctxt->nbErrors > 0)) | |
| 2617 ret = -1; | |
| 2618 return(ret); | |
| 2619 } | |
| 2620 | |
| 2621 #else /* !LIBXML_XINCLUDE_ENABLED */ | |
| 2622 #endif | |
| 2623 #define bottom_xinclude | |
| 2624 #include "elfgcchack.h" | |
| OLD | NEW |