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