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