catalog.c 96.9 KB
Newer Older
1
/**
Daniel Veillard's avatar
Daniel Veillard committed
2
 * catalog.c: set of generic Catalog related routines
3
4
5
6
 *
 * Reference:  SGML Open Technical Resolution TR9401:1997.
 *             http://www.jclark.com/sp/catalog.htm
 *
7
8
9
 *             XML Catalogs Working Draft 06 August 2001
 *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
10
11
12
13
14
 * See Copyright for the status of this software.
 *
 * Daniel.Veillard@imag.fr
 */

15
#define IN_LIBXML
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "libxml.h"

#ifdef LIBXML_CATALOG_ENABLED
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
31
32
33
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
34
35
36
37
38
39
40
#include <string.h>
#include <libxml/xmlmemory.h>
#include <libxml/hash.h>
#include <libxml/uri.h>
#include <libxml/parserInternals.h>
#include <libxml/catalog.h>
#include <libxml/xmlerror.h>
41
#include <libxml/threads.h>
42
#include <libxml/globals.h>
43

44
45
#include "buf.h"

46
#define MAX_DELEGATE	50
47
#define MAX_CATAL_DEPTH	50
48

49
#ifdef _WIN32
50
# define PATH_SEPARATOR ';'
51
#else
52
# define PATH_SEPARATOR ':'
53
54
#endif

55
56
57
58
/**
 * TODO:
 *
 * macro to flag unimplemented blocks
59
60
61
62
63
 * XML_CATALOG_PREFER user env to select between system/public prefered
 * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
 *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
 *> values "system" and "public".  I have made the default be "system" to
 *> match yours.
64
 */
Daniel Veillard's avatar
Daniel Veillard committed
65
#define TODO								\
66
67
68
69
    xmlGenericError(xmlGenericErrorContext,				\
	    "Unimplemented block at %s:%d\n",				\
            __FILE__, __LINE__);

70
#define XML_URN_PUBID "urn:publicid:"
71
#define XML_CATAL_BREAK ((xmlChar *) -1)
72
#ifndef XML_XML_DEFAULT_CATALOG
73
#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
74
#endif
75
#ifndef XML_SGML_DEFAULT_CATALOG
76
#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
77
78
#endif

79
80
81
#if defined(_WIN32) && defined(_MSC_VER)
#undef XML_XML_DEFAULT_CATALOG
static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";
82
83
84
85
86
#if defined(_WIN32_WCE)
/* Windows CE don't have a A variant */
#define GetModuleHandleA GetModuleHandle
#define GetModuleFileNameA GetModuleFileName
#else
Denis Pauk's avatar
Denis Pauk committed
87
#if !defined(_WINDOWS_)
88
89
90
void* __stdcall GetModuleHandleA(const char*);
unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
#endif
91
#endif
Denis Pauk's avatar
Denis Pauk committed
92
#endif
93

94
static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
95
static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
96

97
98
99
100
101
102
103
/************************************************************************
 *									*
 *			Types, all private				*
 *									*
 ************************************************************************/

typedef enum {
104
    XML_CATA_REMOVED = -1,
105
106
    XML_CATA_NONE = 0,
    XML_CATA_CATALOG,
107
    XML_CATA_BROKEN_CATALOG,
108
    XML_CATA_NEXT_CATALOG,
109
    XML_CATA_GROUP,
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    XML_CATA_PUBLIC,
    XML_CATA_SYSTEM,
    XML_CATA_REWRITE_SYSTEM,
    XML_CATA_DELEGATE_PUBLIC,
    XML_CATA_DELEGATE_SYSTEM,
    XML_CATA_URI,
    XML_CATA_REWRITE_URI,
    XML_CATA_DELEGATE_URI,
    SGML_CATA_SYSTEM,
    SGML_CATA_PUBLIC,
    SGML_CATA_ENTITY,
    SGML_CATA_PENTITY,
    SGML_CATA_DOCTYPE,
    SGML_CATA_LINKTYPE,
    SGML_CATA_NOTATION,
    SGML_CATA_DELEGATE,
    SGML_CATA_BASE,
    SGML_CATA_CATALOG,
    SGML_CATA_DOCUMENT,
    SGML_CATA_SGMLDECL
130
131
132
133
134
} xmlCatalogEntryType;

typedef struct _xmlCatalogEntry xmlCatalogEntry;
typedef xmlCatalogEntry *xmlCatalogEntryPtr;
struct _xmlCatalogEntry {
135
136
137
    struct _xmlCatalogEntry *next;
    struct _xmlCatalogEntry *parent;
    struct _xmlCatalogEntry *children;
138
139
140
    xmlCatalogEntryType type;
    xmlChar *name;
    xmlChar *value;
141
    xmlChar *URL;  /* The expanded URL using the base */
142
    xmlCatalogPrefer prefer;
143
    int dealloc;
144
    int depth;
145
    struct _xmlCatalogEntry *group;
146
147
};

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
typedef enum {
    XML_XML_CATALOG_TYPE = 1,
    XML_SGML_CATALOG_TYPE
} xmlCatalogType;

#define XML_MAX_SGML_CATA_DEPTH 10
struct _xmlCatalog {
    xmlCatalogType type;	/* either XML or SGML */

    /*
     * SGML Catalogs are stored as a simple hash table of catalog entries
     * Catalog stack to check against overflows when building the
     * SGML catalog
     */
    char *catalTab[XML_MAX_SGML_CATA_DEPTH];	/* stack of catals */
    int          catalNr;	/* Number of current catal streams */
    int          catalMax;	/* Max number of catal streams */
    xmlHashTablePtr sgml;

    /*
     * XML Catalogs are stored as a tree of Catalog entries
     */
    xmlCatalogPrefer prefer;
    xmlCatalogEntryPtr xml;
};

/************************************************************************
 *									*
 *			Global variables				*
 *									*
 ************************************************************************/

180
181
182
183
/*
 * Those are preferences
 */
static int xmlDebugCatalogs = 0;   /* used for debugging */
184
static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
185
static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
186
187
188
189
190

/*
 * Hash table containing all the trees of XML catalogs parsed by
 * the application.
 */
191
static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
192

193
194
195
196
197
/*
 * The default catalog in use by the application
 */
static xmlCatalogPtr xmlDefaultCatalog = NULL;

198
199
200
201
202
203
204
205
/*
 * A mutex for modifying the shared global catalog(s)
 * xmlDefaultCatalog tree.
 * It also protects xmlCatalogXMLFiles
 * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
 */
static xmlRMutexPtr xmlCatalogMutex = NULL;

206
207
208
209
/*
 * Whether the catalog support was initialized.
 */
static int xmlCatalogInitialized = 0;
210

211
212
/************************************************************************
 *									*
Daniel Veillard's avatar
Daniel Veillard committed
213
 *			Catalog error handlers				*
214
215
216
217
218
219
220
221
222
223
224
225
 *									*
 ************************************************************************/

/**
 * xmlCatalogErrMemory:
 * @extra:  extra informations
 *
 * Handle an out of memory condition
 */
static void
xmlCatalogErrMemory(const char *extra)
{
226
    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
227
228
229
230
231
232
233
234
235
236
237
238
239
240
                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
		    extra, NULL, NULL, 0, 0,
		    "Memory allocation failed : %s\n", extra);
}

/**
 * xmlCatalogErr:
 * @catal: the Catalog entry
 * @node: the context node
 * @msg:  the error message
 * @extra:  extra informations
 *
 * Handle a catalog error
 */
241
static void LIBXML_ATTR_FORMAT(4,0)
242
243
244
245
xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
               const char *msg, const xmlChar *str1, const xmlChar *str2,
	       const xmlChar *str3)
{
246
    __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
247
248
249
250
251
252
                    error, XML_ERR_ERROR, NULL, 0,
		    (const char *) str1, (const char *) str2,
		    (const char *) str3, 0, 0,
		    msg, str1, str2, str3);
}

253

254
255
/************************************************************************
 *									*
256
 *			Allocation and Freeing				*
257
258
259
 *									*
 ************************************************************************/

260
261
262
263
264
265
/**
 * xmlNewCatalogEntry:
 * @type:  type of entry
 * @name:  name of the entry
 * @value:  value of the entry
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
Daniel Veillard's avatar
Daniel Veillard committed
266
 * @group:  for members of a group, the group entry
267
 *
Daniel Veillard's avatar
Daniel Veillard committed
268
 * create a new Catalog entry, this type is shared both by XML and
269
270
271
272
 * SGML catalogs, but the acceptable types values differs.
 *
 * Returns the xmlCatalogEntryPtr or NULL in case of error
 */
273
static xmlCatalogEntryPtr
274
xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
275
276
	   const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
	   xmlCatalogEntryPtr group) {
277
    xmlCatalogEntryPtr ret;
278
    xmlChar *normid = NULL;
279
280
281

    ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
    if (ret == NULL) {
282
        xmlCatalogErrMemory("allocating catalog entry");
283
284
	return(NULL);
    }
285
286
287
    ret->next = NULL;
    ret->parent = NULL;
    ret->children = NULL;
288
    ret->type = type;
289
290
291
292
293
    if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
        normid = xmlCatalogNormalizePublic(name);
        if (normid != NULL)
            name = (*normid != 0 ? normid : NULL);
    }
294
295
296
297
    if (name != NULL)
	ret->name = xmlStrdup(name);
    else
	ret->name = NULL;
298
299
    if (normid != NULL)
        xmlFree(normid);
300
301
302
303
    if (value != NULL)
	ret->value = xmlStrdup(value);
    else
	ret->value = NULL;
304
305
306
307
308
309
    if (URL == NULL)
	URL = value;
    if (URL != NULL)
	ret->URL = xmlStrdup(URL);
    else
	ret->URL = NULL;
310
    ret->prefer = prefer;
311
    ret->dealloc = 0;
312
    ret->depth = 0;
313
    ret->group = group;
314
315
316
    return(ret);
}

317
318
319
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);

320
321
/**
 * xmlFreeCatalogEntry:
Nick Wellnhofer's avatar
Nick Wellnhofer committed
322
 * @payload:  a Catalog entry
323
324
325
 *
 * Free the memory allocated to a Catalog entry
 */
326
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
327
328
xmlFreeCatalogEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr ret = (xmlCatalogEntryPtr) payload;
329
330
    if (ret == NULL)
	return;
331
    /*
332
     * Entries stored in the file hash must be deallocated
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
     * only by the file hash cleaner !
     */
    if (ret->dealloc == 1)
	return;

    if (xmlDebugCatalogs) {
	if (ret->name != NULL)
	    xmlGenericError(xmlGenericErrorContext,
		    "Free catalog entry %s\n", ret->name);
	else if (ret->value != NULL)
	    xmlGenericError(xmlGenericErrorContext,
		    "Free catalog entry %s\n", ret->value);
	else
	    xmlGenericError(xmlGenericErrorContext,
		    "Free catalog entry\n");
    }

350
351
352
353
    if (ret->name != NULL)
	xmlFree(ret->name);
    if (ret->value != NULL)
	xmlFree(ret->value);
354
355
    if (ret->URL != NULL)
	xmlFree(ret->URL);
356
357
358
    xmlFree(ret);
}

359
360
361
362
363
364
/**
 * xmlFreeCatalogEntryList:
 * @ret:  a Catalog entry list
 *
 * Free the memory allocated to a full chained list of Catalog entries
 */
365
366
367
368
369
370
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
    xmlCatalogEntryPtr next;

    while (ret != NULL) {
	next = ret->next;
Nick Wellnhofer's avatar
Nick Wellnhofer committed
371
	xmlFreeCatalogEntry(ret, NULL);
372
373
374
375
	ret = next;
    }
}

376
377
/**
 * xmlFreeCatalogHashEntryList:
Nick Wellnhofer's avatar
Nick Wellnhofer committed
378
 * @payload:  a Catalog entry list
379
380
381
382
383
 *
 * Free the memory allocated to list of Catalog entries from the
 * catalog file hash.
 */
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
384
385
386
xmlFreeCatalogHashEntryList(void *payload,
                            const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr catal = (xmlCatalogEntryPtr) payload;
387
388
389
390
391
392
393
394
395
396
    xmlCatalogEntryPtr children, next;

    if (catal == NULL)
	return;

    children = catal->children;
    while (children != NULL) {
	next = children->next;
	children->dealloc = 0;
	children->children = NULL;
Nick Wellnhofer's avatar
Nick Wellnhofer committed
397
	xmlFreeCatalogEntry(children, NULL);
398
399
400
	children = next;
    }
    catal->dealloc = 0;
Nick Wellnhofer's avatar
Nick Wellnhofer committed
401
    xmlFreeCatalogEntry(catal, NULL);
402
403
}

404
/**
405
 * xmlCreateNewCatalog:
406
407
408
 * @type:  type of catalog
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 *
Daniel Veillard's avatar
Daniel Veillard committed
409
 * create a new Catalog, this type is shared both by XML and
410
411
412
413
414
 * SGML catalogs, but the acceptable types values differs.
 *
 * Returns the xmlCatalogPtr or NULL in case of error
 */
static xmlCatalogPtr
415
xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
416
417
418
419
    xmlCatalogPtr ret;

    ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
    if (ret == NULL) {
420
        xmlCatalogErrMemory("allocating catalog");
421
422
423
424
425
426
427
	return(NULL);
    }
    memset(ret, 0, sizeof(xmlCatalog));
    ret->type = type;
    ret->catalNr = 0;
    ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
    ret->prefer = prefer;
428
429
    if (ret->type == XML_SGML_CATALOG_TYPE)
	ret->sgml = xmlHashCreate(10);
430
431
432
433
434
    return(ret);
}

/**
 * xmlFreeCatalog:
435
 * @catal:  a Catalog
436
437
438
439
440
441
442
443
444
445
 *
 * Free the memory allocated to a Catalog
 */
void
xmlFreeCatalog(xmlCatalogPtr catal) {
    if (catal == NULL)
	return;
    if (catal->xml != NULL)
	xmlFreeCatalogEntryList(catal->xml);
    if (catal->sgml != NULL)
Nick Wellnhofer's avatar
Nick Wellnhofer committed
446
	xmlHashFree(catal->sgml, xmlFreeCatalogEntry);
447
448
449
450
451
452
453
454
455
    xmlFree(catal);
}

/************************************************************************
 *									*
 *			Serializing Catalogs				*
 *									*
 ************************************************************************/

456
#ifdef LIBXML_OUTPUT_ENABLED
457
458
/**
 * xmlCatalogDumpEntry:
459
 * @entry:  the catalog entry
460
461
 * @out:  the file.
 *
462
 * Serialize an SGML Catalog entry
463
464
 */
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
465
466
467
468
xmlCatalogDumpEntry(void *payload, void *data,
                    const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
    FILE *out = (FILE *) data;
469
470
471
    if ((entry == NULL) || (out == NULL))
	return;
    switch (entry->type) {
472
	case SGML_CATA_ENTITY:
473
	    fprintf(out, "ENTITY "); break;
474
	case SGML_CATA_PENTITY:
475
	    fprintf(out, "ENTITY %%"); break;
476
	case SGML_CATA_DOCTYPE:
477
	    fprintf(out, "DOCTYPE "); break;
478
	case SGML_CATA_LINKTYPE:
479
	    fprintf(out, "LINKTYPE "); break;
480
	case SGML_CATA_NOTATION:
481
	    fprintf(out, "NOTATION "); break;
482
	case SGML_CATA_PUBLIC:
483
	    fprintf(out, "PUBLIC "); break;
484
	case SGML_CATA_SYSTEM:
485
	    fprintf(out, "SYSTEM "); break;
486
	case SGML_CATA_DELEGATE:
487
	    fprintf(out, "DELEGATE "); break;
488
	case SGML_CATA_BASE:
489
	    fprintf(out, "BASE "); break;
490
	case SGML_CATA_CATALOG:
491
	    fprintf(out, "CATALOG "); break;
492
	case SGML_CATA_DOCUMENT:
493
	    fprintf(out, "DOCUMENT "); break;
494
	case SGML_CATA_SGMLDECL:
495
496
497
498
499
	    fprintf(out, "SGMLDECL "); break;
	default:
	    return;
    }
    switch (entry->type) {
500
501
502
503
504
	case SGML_CATA_ENTITY:
	case SGML_CATA_PENTITY:
	case SGML_CATA_DOCTYPE:
	case SGML_CATA_LINKTYPE:
	case SGML_CATA_NOTATION:
505
	    fprintf(out, "%s", (const char *) entry->name); break;
506
507
508
509
510
511
512
	case SGML_CATA_PUBLIC:
	case SGML_CATA_SYSTEM:
	case SGML_CATA_SGMLDECL:
	case SGML_CATA_DOCUMENT:
	case SGML_CATA_CATALOG:
	case SGML_CATA_BASE:
	case SGML_CATA_DELEGATE:
513
514
515
516
517
	    fprintf(out, "\"%s\"", entry->name); break;
	default:
	    break;
    }
    switch (entry->type) {
518
519
520
521
522
523
524
525
	case SGML_CATA_ENTITY:
	case SGML_CATA_PENTITY:
	case SGML_CATA_DOCTYPE:
	case SGML_CATA_LINKTYPE:
	case SGML_CATA_NOTATION:
	case SGML_CATA_PUBLIC:
	case SGML_CATA_SYSTEM:
	case SGML_CATA_DELEGATE:
526
527
528
529
530
531
532
	    fprintf(out, " \"%s\"", entry->value); break;
	default:
	    break;
    }
    fprintf(out, "\n");
}

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
/**
 * xmlDumpXMLCatalogNode:
 * @catal:  top catalog entry
 * @catalog: pointer to the xml tree
 * @doc: the containing document
 * @ns: the current namespace
 * @cgroup: group node for group members
 *
 * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
 * for group entries
 */
static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
		    xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
    xmlNodePtr node;
    xmlCatalogEntryPtr cur;
    /*
     * add all the catalog entries
     */
    cur = catal;
    while (cur != NULL) {
        if (cur->group == cgroup) {
	    switch (cur->type) {
	        case XML_CATA_REMOVED:
		    break;
	        case XML_CATA_BROKEN_CATALOG:
	        case XML_CATA_CATALOG:
		    if (cur == catal) {
			cur = cur->children;
		        continue;
		    }
		    break;
		case XML_CATA_NEXT_CATALOG:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
                    break;
		case XML_CATA_NONE:
		    break;
		case XML_CATA_GROUP:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
		    xmlSetProp(node, BAD_CAST "id", cur->name);
574
575
576
577
578
		    if (cur->value != NULL) {
		        xmlNsPtr xns;
			xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
			if (xns != NULL)
			    xmlSetNsProp(node, xns, BAD_CAST "base",
Daniel Veillard's avatar
Daniel Veillard committed
579
					 cur->value);
580
		    }
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
		    switch (cur->prefer) {
			case XML_CATA_PREFER_NONE:
		            break;
			case XML_CATA_PREFER_PUBLIC:
		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
			    break;
			case XML_CATA_PREFER_SYSTEM:
		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
			    break;
		    }
		    xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
		    xmlAddChild(catalog, node);
	            break;
		case XML_CATA_PUBLIC:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
		    xmlSetProp(node, BAD_CAST "publicId", cur->name);
		    xmlSetProp(node, BAD_CAST "uri", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_SYSTEM:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
		    xmlSetProp(node, BAD_CAST "systemId", cur->name);
		    xmlSetProp(node, BAD_CAST "uri", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_REWRITE_SYSTEM:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_DELEGATE_PUBLIC:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
		    xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_DELEGATE_SYSTEM:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_URI:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
		    xmlSetProp(node, BAD_CAST "name", cur->name);
		    xmlSetProp(node, BAD_CAST "uri", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_REWRITE_URI:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case XML_CATA_DELEGATE_URI:
		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
		    xmlAddChild(catalog, node);
		    break;
		case SGML_CATA_SYSTEM:
		case SGML_CATA_PUBLIC:
		case SGML_CATA_ENTITY:
		case SGML_CATA_PENTITY:
		case SGML_CATA_DOCTYPE:
		case SGML_CATA_LINKTYPE:
		case SGML_CATA_NOTATION:
		case SGML_CATA_DELEGATE:
		case SGML_CATA_BASE:
		case SGML_CATA_CATALOG:
		case SGML_CATA_DOCUMENT:
		case SGML_CATA_SGMLDECL:
		    break;
	    }
        }
	cur = cur->next;
    }
}

661
662
663
664
665
666
static int
xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
    int ret;
    xmlDocPtr doc;
    xmlNsPtr ns;
    xmlDtdPtr dtd;
667
    xmlNodePtr catalog;
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
    xmlOutputBufferPtr buf;

    /*
     * Rebuild a catalog
     */
    doc = xmlNewDoc(NULL);
    if (doc == NULL)
	return(-1);
    dtd = xmlNewDtd(doc, BAD_CAST "catalog",
	       BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");

    xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);

    ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
    if (ns == NULL) {
	xmlFreeDoc(doc);
	return(-1);
    }
    catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
    if (catalog == NULL) {
	xmlFreeNs(ns);
	xmlFreeDoc(doc);
	return(-1);
    }
    catalog->nsDef = ns;
    xmlAddChild((xmlNodePtr) doc, catalog);

696
    xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
Daniel Veillard's avatar
Daniel Veillard committed
697

698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
    /*
     * reserialize it
     */
    buf = xmlOutputBufferCreateFile(out, NULL);
    if (buf == NULL) {
	xmlFreeDoc(doc);
	return(-1);
    }
    ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);

    /*
     * Free it
     */
    xmlFreeDoc(doc);

    return(ret);
}
715
#endif /* LIBXML_OUTPUT_ENABLED */
716
717
718
719
720
721
722

/************************************************************************
 *									*
 *			Converting SGML Catalogs to XML			*
 *									*
 ************************************************************************/

723
724
725
/**
 * xmlCatalogConvertEntry:
 * @entry:  the entry
726
 * @catal:  pointer to the catalog being converted
727
 *
728
 * Convert one entry from the catalog
729
730
 */
static void
Nick Wellnhofer's avatar
Nick Wellnhofer committed
731
732
733
734
xmlCatalogConvertEntry(void *payload, void *data,
                       const xmlChar *name ATTRIBUTE_UNUSED) {
    xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
    xmlCatalogPtr catal = (xmlCatalogPtr) data;
735
736
    if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
	(catal->xml == NULL))
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
	return;
    switch (entry->type) {
	case SGML_CATA_ENTITY:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_PENTITY:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_DOCTYPE:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_LINKTYPE:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_NOTATION:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_PUBLIC:
	    entry->type = XML_CATA_PUBLIC;
	    break;
	case SGML_CATA_SYSTEM:
	    entry->type = XML_CATA_SYSTEM;
	    break;
	case SGML_CATA_DELEGATE:
	    entry->type = XML_CATA_DELEGATE_PUBLIC;
	    break;
	case SGML_CATA_CATALOG:
	    entry->type = XML_CATA_CATALOG;
	    break;
	default:
Nick Wellnhofer's avatar
Nick Wellnhofer committed
767
	    xmlHashRemoveEntry(catal->sgml, entry->name, xmlFreeCatalogEntry);
768
769
770
771
772
773
	    return;
    }
    /*
     * Conversion successful, remove from the SGML catalog
     * and add it to the default XML one
     */
774
775
    xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
    entry->parent = catal->xml;
776
    entry->next = NULL;
777
778
    if (catal->xml->children == NULL)
	catal->xml->children = entry;
779
780
781
    else {
	xmlCatalogEntryPtr prev;

782
	prev = catal->xml->children;
783
784
785
786
	while (prev->next != NULL)
	    prev = prev->next;
	prev->next = entry;
    }
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
}

/**
 * xmlConvertSGMLCatalog:
 * @catal: the catalog
 *
 * Convert all the SGML catalog entries as XML ones
 *
 * Returns the number of entries converted if successful, -1 otherwise
 */
int
xmlConvertSGMLCatalog(xmlCatalogPtr catal) {

    if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
	return(-1);

    if (xmlDebugCatalogs) {
	xmlGenericError(xmlGenericErrorContext,
		"Converting SGML catalog to XML\n");
    }
Nick Wellnhofer's avatar
Nick Wellnhofer committed
807
    xmlHashScan(catal->sgml, xmlCatalogConvertEntry, &catal);
808
    return(0);
809
810
}

811
812
813
814
815
816
817
818
/************************************************************************
 *									*
 *			Helper function					*
 *									*
 ************************************************************************/

/**
 * xmlCatalogUnWrapURN:
819
 * @urn:  an "urn:publicid:" to unwrap
820
821
822
823
824
825
826
827
828
829
830
831
832
833
 *
 * Expand the URN into the equivalent Public Identifier
 *
 * Returns the new identifier or NULL, the string must be deallocated
 *         by the caller.
 */
static xmlChar *
xmlCatalogUnWrapURN(const xmlChar *urn) {
    xmlChar result[2000];
    unsigned int i = 0;

    if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
	return(NULL);
    urn += sizeof(XML_URN_PUBID) - 1;
Daniel Veillard's avatar
Daniel Veillard committed
834

835
    while (*urn != 0) {
836
	if (i > sizeof(result) - 4)
837
838
839
840
841
842
843
844
845
846
847
848
849
	    break;
	if (*urn == '+') {
	    result[i++] = ' ';
	    urn++;
	} else if (*urn == ':') {
	    result[i++] = '/';
	    result[i++] = '/';
	    urn++;
	} else if (*urn == ';') {
	    result[i++] = ':';
	    result[i++] = ':';
	    urn++;
	} else if (*urn == '%') {
850
	    if ((urn[1] == '2') && (urn[2] == 'B'))
851
		result[i++] = '+';
852
	    else if ((urn[1] == '3') && (urn[2] == 'A'))
853
		result[i++] = ':';
854
	    else if ((urn[1] == '2') && (urn[2] == 'F'))
855
		result[i++] = '/';
856
	    else if ((urn[1] == '3') && (urn[2] == 'B'))
857
		result[i++] = ';';
858
	    else if ((urn[1] == '2') && (urn[2] == '7'))
859
		result[i++] = '\'';
860
	    else if ((urn[1] == '3') && (urn[2] == 'F'))
861
		result[i++] = '?';
862
	    else if ((urn[1] == '2') && (urn[2] == '3'))
863
		result[i++] = '#';
864
	    else if ((urn[1] == '2') && (urn[2] == '5'))
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
		result[i++] = '%';
	    else {
		result[i++] = *urn;
		urn++;
		continue;
	    }
	    urn += 3;
	} else {
	    result[i++] = *urn;
	    urn++;
	}
    }
    result[i] = 0;

    return(xmlStrdup(result));
}

882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
/**
 * xmlParseCatalogFile:
 * @filename:  the filename
 *
 * parse an XML file and build a tree. It's like xmlParseFile()
 * except it bypass all catalog lookups.
 *
 * Returns the resulting document tree or NULL in case of error
 */

xmlDocPtr
xmlParseCatalogFile(const char *filename) {
    xmlDocPtr ret;
    xmlParserCtxtPtr ctxt;
    char *directory = NULL;
    xmlParserInputPtr inputStream;
    xmlParserInputBufferPtr buf;

    ctxt = xmlNewParserCtxt();
    if (ctxt == NULL) {
902
#ifdef LIBXML_SAX1_ENABLED
903
904
905
	if (xmlDefaultSAXHandler.error != NULL) {
	    xmlDefaultSAXHandler.error(NULL, "out of memory\n");
	}
906
#endif
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
	return(NULL);
    }

    buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
    if (buf == NULL) {
	xmlFreeParserCtxt(ctxt);
	return(NULL);
    }

    inputStream = xmlNewInputStream(ctxt);
    if (inputStream == NULL) {
	xmlFreeParserCtxt(ctxt);
	return(NULL);
    }

922
    inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
923
    inputStream->buf = buf;
924
    xmlBufResetInput(buf->buffer, inputStream);
925
926
927
928
929
930

    inputPush(ctxt, inputStream);
    if ((ctxt->directory == NULL) && (directory == NULL))
        directory = xmlParserGetDirectory(filename);
    if ((ctxt->directory == NULL) && (directory != NULL))
        ctxt->directory = directory;
931
932
933
934
    ctxt->valid = 0;
    ctxt->validate = 0;
    ctxt->loadsubset = 0;
    ctxt->pedantic = 0;
935
    ctxt->dictNames = 1;
936
937
938
939
940
941
942
943
944
945
946

    xmlParseDocument(ctxt);

    if (ctxt->wellFormed)
	ret = ctxt->myDoc;
    else {
        ret = NULL;
        xmlFreeDoc(ctxt->myDoc);
        ctxt->myDoc = NULL;
    }
    xmlFreeParserCtxt(ctxt);
Daniel Veillard's avatar
Daniel Veillard committed
947

948
949
950
    return(ret);
}

951
952
953
954
955
956
957
958
/**
 * xmlLoadFileContent:
 * @filename:  a file path
 *
 * Load a file content into memory.
 *
 * Returns a pointer to the 0 terminated string or NULL in case of error
 */
959
static xmlChar *
960
961
962
963
964
965
966
967
968
xmlLoadFileContent(const char *filename)
{
#ifdef HAVE_STAT
    int fd;
#else
    FILE *fd;
#endif
    int len;
    long size;
969

970
971
972
973
974
975
976
977
978
979
980
981
982
983
#ifdef HAVE_STAT
    struct stat info;
#endif
    xmlChar *content;

    if (filename == NULL)
        return (NULL);

#ifdef HAVE_STAT
    if (stat(filename, &info) < 0)
        return (NULL);
#endif

#ifdef HAVE_STAT
Daniel Veillard's avatar
Daniel Veillard committed
984
    if ((fd = open(filename, O_RDONLY)) < 0)
985
#else
Daniel Veillard's avatar
Daniel Veillard committed
986
    if ((fd = fopen(filename, "rb")) == NULL)
987
#endif
Daniel Veillard's avatar
Daniel Veillard committed
988
    {
989
990
991
992
993
994
995
996
997
998
        return (NULL);
    }
#ifdef HAVE_STAT
    size = info.st_size;
#else
    if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
        fclose(fd);
        return (NULL);
    }
#endif
Denis Pauk's avatar
Denis Pauk committed
999
    content = (xmlChar*)xmlMallocAtomic(size + 10);
1000
    if (content == NULL) {
1001
        xmlCatalogErrMemory("allocating catalog data");
1002
1003
1004
1005
1006
#ifdef HAVE_STAT
	close(fd);
#else
	fclose(fd);
#endif
1007
1008
1009
1010
        return (NULL);
    }
#ifdef HAVE_STAT
    len = read(fd, content, size);
1011
    close(fd);
1012
1013
#else
    len = fread(content, 1, size, fd);
1014
    fclose(fd);
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
#endif
    if (len < 0) {
        xmlFree(content);
        return (NULL);
    }
    content[len] = 0;

    return(content);
}

1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
/**
 * xmlCatalogNormalizePublic:
 * @pubID:  the public ID string
 *
 *  Normalizes the Public Identifier
 *
 * Implements 6.2. Public Identifier Normalization
 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 *
 * Returns the new string or NULL, the string must be deallocated
 *         by the caller.
 */
static xmlChar *
xmlCatalogNormalizePublic(const xmlChar *pubID)
{
    int ok = 1;
    int white;
    const xmlChar *p;
    xmlChar *ret;
    xmlChar *q;

    if (pubID == NULL)
        return(NULL);

    white = 1;
    for (p = pubID;*p != 0 && ok;p++) {
        if (!xmlIsBlank_ch(*p))
            white = 0;
        else if (*p == 0x20 && !white)
            white = 1;
        else
            ok = 0;
    }
    if (ok && !white)	/* is normalized */
        return(NULL);

    ret = xmlStrdup(pubID);
    q = ret;
    white = 0;
    for (p = pubID;*p != 0;p++) {
        if (xmlIsBlank_ch(*p)) {
            if (q != ret)
                white = 1;
        } else {
            if (white) {
                *(q++) = 0x20;
                white = 0;
            }
            *(q++) = *p;
        }
    }
    *q = 0;
    return(ret);
}

1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
/************************************************************************
 *									*
 *			The XML Catalog parser				*
 *									*
 ************************************************************************/

static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1090
	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1091
1092
1093
1094
1095
static xmlChar *
xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
	              const xmlChar *sysID);
static xmlChar *
xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1096

1097
1098
1099
1100
1101
1102
1103

/**
 * xmlGetXMLCatalogEntryType:
 * @name:  the name
 *
 * lookup the internal type associated to an XML catalog entry name
 *
1104
 * Returns the type associated with that name
1105
 */
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
static xmlCatalogEntryType
xmlGetXMLCatalogEntryType(const xmlChar *name) {
    xmlCatalogEntryType type = XML_CATA_NONE;
    if (xmlStrEqual(name, (const xmlChar *) "system"))
	type = XML_CATA_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "public"))
	type = XML_CATA_PUBLIC;
    else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
	type = XML_CATA_REWRITE_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
	type = XML_CATA_DELEGATE_PUBLIC;
    else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
	type = XML_CATA_DELEGATE_SYSTEM;
    else if (xmlStrEqual(name, (const xmlChar *) "uri"))
	type = XML_CATA_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
	type = XML_CATA_REWRITE_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
	type = XML_CATA_DELEGATE_URI;
    else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
	type = XML_CATA_NEXT_CATALOG;
    else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
	type = XML_CATA_CATALOG;
    return(type);
}

1132
1133
1134
1135
1136
1137
1138
1139
/**
 * xmlParseXMLCatalogOneNode:
 * @cur:  the XML node
 * @type:  the type of Catalog entry
 * @name:  the name of the node
 * @attrName:  the attribute holding the value
 * @uriAttrName:  the attribute holding the URI-Reference
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
1140
 * @cgroup:  the group which includes this node
1141
1142
1143
1144
1145
1146
 *
 * Finishes the examination of an XML tree node of a catalog and build
 * a Catalog entry from it.
 *
 * Returns the new Catalog entry node or NULL in case of error.
 */
1147
1148
1149
static xmlCatalogEntryPtr
xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
			  const xmlChar *name, const xmlChar *attrName,
1150
1151
			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
			  xmlCatalogEntryPtr cgroup) {
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
    int ok = 1;
    xmlChar *uriValue;
    xmlChar *nameValue = NULL;
    xmlChar *base = NULL;
    xmlChar *URL = NULL;
    xmlCatalogEntryPtr ret = NULL;

    if (attrName != NULL) {
	nameValue = xmlGetProp(cur, attrName);
	if (nameValue == NULL) {
1162
1163
	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
			  "%s entry lacks '%s'\n", name, attrName, NULL);
1164
1165
1166
1167
1168
	    ok = 0;
	}
    }
    uriValue = xmlGetProp(cur, uriAttrName);
    if (uriValue == NULL) {
1169
1170
	xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
		"%s entry lacks '%s'\n", name, uriAttrName, NULL);
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
	ok = 0;
    }
    if (!ok) {
	if (nameValue != NULL)
	    xmlFree(nameValue);
	if (uriValue != NULL)
	    xmlFree(uriValue);
	return(NULL);
    }

    base = xmlNodeGetBase(cur->doc, cur);
    URL = xmlBuildURI(uriValue, base);
    if (URL != NULL) {
1184
	if (xmlDebugCatalogs > 1) {
1185
	    if (nameValue != NULL)
1186
1187
		xmlGenericError(xmlGenericErrorContext,
			"Found %s: '%s' '%s'\n", name, nameValue, URL);
1188
	    else
1189
1190
		xmlGenericError(xmlGenericErrorContext,
			"Found %s: '%s'\n", name, URL);
1191
	}
1192
	ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
1193
    } else {
1194
	xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
		"%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
    }
    if (nameValue != NULL)
	xmlFree(nameValue);
    if (uriValue != NULL)
	xmlFree(uriValue);
    if (base != NULL)
	xmlFree(base);
    if (URL != NULL)
	xmlFree(URL);
    return(ret);
}

1208
1209
1210
1211
1212
/**
 * xmlParseXMLCatalogNode:
 * @cur:  the XML node
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 * @parent:  the parent Catalog entry
1213
 * @cgroup:  the group which includes this node
1214
1215
1216
1217
1218
 *
 * Examines an XML tree node of a catalog and build
 * a Catalog entry from it adding it to its parent. The examination can
 * be recursive.
 */
1219
1220
static void
xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1221
	               xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
1222
1223
1224
1225
1226
1227
1228
1229
{
    xmlChar *base = NULL;
    xmlCatalogEntryPtr entry = NULL;

    if (cur == NULL)
        return;
    if (xmlStrEqual(cur->name, BAD_CAST "group")) {
        xmlChar *prop;
1230
	xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
1231
1232
1233
1234
1235
1236
1237
1238

        prop = xmlGetProp(cur, BAD_CAST "prefer");
        if (prop != NULL) {
            if (xmlStrEqual(prop, BAD_CAST "system")) {
                prefer = XML_CATA_PREFER_SYSTEM;
            } else if (xmlStrEqual(prop, BAD_CAST "public")) {
                prefer = XML_CATA_PREFER_PUBLIC;
            } else {
1239
1240
1241
		xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
                              "Invalid value for prefer: '%s'\n",
			      prop, NULL, NULL);
1242
1243
            }
            xmlFree(prop);
1244
	    pref = prefer;
1245
        }
1246
1247
1248
	prop = xmlGetProp(cur, BAD_CAST "id");
	base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
	entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
William M. Brack's avatar
William M. Brack committed
1249
	xmlFree(prop);
1250
1251
    } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1252
		BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
1253
1254
    } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1255
		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
1256
1257
1258
    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1259
		BAD_CAST "rewritePrefix", prefer, cgroup);
1260
1261
1262
    } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1263
		BAD_CAST "catalog", prefer, cgroup);
1264
1265
1266
    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1267
		BAD_CAST "catalog", prefer, cgroup);
1268
1269
1270
    } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
		BAD_CAST "uri", BAD_CAST "name",
1271
		BAD_CAST "uri", prefer, cgroup);
1272
1273
1274
    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1275
		BAD_CAST "rewritePrefix", prefer, cgroup);
1276
1277
1278
    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1279
		BAD_CAST "catalog", prefer, cgroup);
1280
1281
1282
    } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
		BAD_CAST "nextCatalog", NULL,
1283
		BAD_CAST "catalog", prefer, cgroup);
1284
    }
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
    if (entry != NULL) {
        if (parent != NULL) {
	    entry->parent = parent;
	    if (parent->children == NULL)
		parent->children = entry;
	    else {
		xmlCatalogEntryPtr prev;

		prev = parent->children;
		while (prev->next != NULL)
		    prev = prev->next;
		prev->next = entry;
	    }
	}
	if (entry->type == XML_CATA_GROUP) {
	    /*
	     * Recurse to propagate prefer to the subtree
	     * (xml:base handling is automated)
	     */
            xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
1305
	}
1306
    }
1307
1308
1309
1310
    if (base != NULL)
	xmlFree(base);
}

1311
1312
1313
1314
1315
/**
 * xmlParseXMLCatalogNodeList:
 * @cur:  the XML node list of siblings
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 * @parent:  the parent Catalog entry
1316
 * @cgroup:  the group which includes this list
1317
1318
1319
1320
1321
 *
 * Examines a list of XML sibling nodes of a catalog and build
 * a list of Catalog entry from it adding it to the parent.
 * The examination will recurse to examine node subtrees.
 */
1322
1323
static void
xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1324
	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
1325
1326
1327
    while (cur != NULL) {
	if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
	    (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1328
	    xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
1329
1330
1331
1332
1333
1334
	}
	cur = cur->next;
    }
    /* TODO: sort the list according to REWRITE lengths and prefer value */
}

1335
1336
1337
1338
1339
1340
1341
/**
 * xmlParseXMLCatalogFile:
 * @prefer:  the PUBLIC vs. SYSTEM current preference value
 * @filename:  the filename for the catalog
 *
 * Parses the catalog file to extract the XML tree and then analyze the
 * tree to build a list of Catalog entries corresponding to this catalog
Daniel Veillard's avatar
Daniel Veillard committed
1342
 *
1343
1344
 * Returns the resulting Catalog entries list
 */
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
    xmlDocPtr doc;
    xmlNodePtr cur;
    xmlChar *prop;
    xmlCatalogEntryPtr parent = NULL;

    if (filename == NULL)
        return(NULL);

1355
    doc = xmlParseCatalogFile((const char *) filename);
1356
1357
1358
1359
    if (doc == NULL) {
	if (xmlDebugCatalogs)
	    xmlGenericError(xmlGenericErrorContext,
		    "Failed to parse catalog %s\n", filename);
1360
	return(NULL);
1361
1362
1363
1364
    }

    if (xmlDebugCatalogs)
	xmlGenericError(xmlGenericErrorContext,
1365
		"%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1366
1367
1368
1369
1370
1371

    cur = xmlDocGetRootElement(doc);
    if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
	(cur->ns != NULL) && (cur->ns->href != NULL) &&
	(xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {

1372
	parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,