catalog.c revision 6218b31b7fa3da1017c9f6e9f09a6baf11ff2718
1/**
2 * catalog.c: set of generic Catalog related routines
3 *
4 * Reference:  SGML Open Technical Resolution TR9401:1997.
5 *             http://www.jclark.com/sp/catalog.htm
6 *
7 *             XML Catalogs Working Draft 06 August 2001
8 *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
9 *
10 * See Copyright for the status of this software.
11 *
12 * Daniel.Veillard@imag.fr
13 */
14
15#define IN_LIBXML
16#include "libxml.h"
17
18#ifdef LIBXML_CATALOG_ENABLED
19#ifdef HAVE_SYS_TYPES_H
20#include <sys/types.h>
21#endif
22#ifdef HAVE_SYS_STAT_H
23#include <sys/stat.h>
24#endif
25#ifdef HAVE_UNISTD_H
26#include <unistd.h>
27#endif
28#ifdef HAVE_FCNTL_H
29#include <fcntl.h>
30#endif
31#ifdef HAVE_STDLIB_H
32#include <stdlib.h>
33#endif
34#include <string.h>
35#include <libxml/xmlmemory.h>
36#include <libxml/hash.h>
37#include <libxml/uri.h>
38#include <libxml/parserInternals.h>
39#include <libxml/catalog.h>
40#include <libxml/xmlerror.h>
41#include <libxml/threads.h>
42#include <libxml/globals.h>
43
44#define MAX_DELEGATE	50
45#define MAX_CATAL_DEPTH	50
46
47/**
48 * TODO:
49 *
50 * macro to flag unimplemented blocks
51 * XML_CATALOG_PREFER user env to select between system/public prefered
52 * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
53 *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
54 *> values "system" and "public".  I have made the default be "system" to
55 *> match yours.
56 */
57#define TODO 								\
58    xmlGenericError(xmlGenericErrorContext,				\
59	    "Unimplemented block at %s:%d\n",				\
60            __FILE__, __LINE__);
61
62#define XML_URN_PUBID "urn:publicid:"
63#define XML_CATAL_BREAK ((xmlChar *) -1)
64#ifndef XML_XML_DEFAULT_CATALOG
65#define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
66#endif
67#ifndef XML_SGML_DEFAULT_CATALOG
68#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
69#endif
70
71#if defined(_WIN32) && defined(_MSC_VER)
72#undef XML_XML_DEFAULT_CATALOG
73static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";
74void* __stdcall GetModuleHandleA(const char*);
75unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
76#endif
77
78static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
79static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
80
81/************************************************************************
82 *									*
83 *			Types, all private				*
84 *									*
85 ************************************************************************/
86
87typedef enum {
88    XML_CATA_REMOVED = -1,
89    XML_CATA_NONE = 0,
90    XML_CATA_CATALOG,
91    XML_CATA_BROKEN_CATALOG,
92    XML_CATA_NEXT_CATALOG,
93    XML_CATA_GROUP,
94    XML_CATA_PUBLIC,
95    XML_CATA_SYSTEM,
96    XML_CATA_REWRITE_SYSTEM,
97    XML_CATA_DELEGATE_PUBLIC,
98    XML_CATA_DELEGATE_SYSTEM,
99    XML_CATA_URI,
100    XML_CATA_REWRITE_URI,
101    XML_CATA_DELEGATE_URI,
102    SGML_CATA_SYSTEM,
103    SGML_CATA_PUBLIC,
104    SGML_CATA_ENTITY,
105    SGML_CATA_PENTITY,
106    SGML_CATA_DOCTYPE,
107    SGML_CATA_LINKTYPE,
108    SGML_CATA_NOTATION,
109    SGML_CATA_DELEGATE,
110    SGML_CATA_BASE,
111    SGML_CATA_CATALOG,
112    SGML_CATA_DOCUMENT,
113    SGML_CATA_SGMLDECL
114} xmlCatalogEntryType;
115
116typedef struct _xmlCatalogEntry xmlCatalogEntry;
117typedef xmlCatalogEntry *xmlCatalogEntryPtr;
118struct _xmlCatalogEntry {
119    struct _xmlCatalogEntry *next;
120    struct _xmlCatalogEntry *parent;
121    struct _xmlCatalogEntry *children;
122    xmlCatalogEntryType type;
123    xmlChar *name;
124    xmlChar *value;
125    xmlChar *URL;  /* The expanded URL using the base */
126    xmlCatalogPrefer prefer;
127    int dealloc;
128    int depth;
129    struct _xmlCatalogEntry *group;
130};
131
132typedef enum {
133    XML_XML_CATALOG_TYPE = 1,
134    XML_SGML_CATALOG_TYPE
135} xmlCatalogType;
136
137#define XML_MAX_SGML_CATA_DEPTH 10
138struct _xmlCatalog {
139    xmlCatalogType type;	/* either XML or SGML */
140
141    /*
142     * SGML Catalogs are stored as a simple hash table of catalog entries
143     * Catalog stack to check against overflows when building the
144     * SGML catalog
145     */
146    char *catalTab[XML_MAX_SGML_CATA_DEPTH];	/* stack of catals */
147    int          catalNr;	/* Number of current catal streams */
148    int          catalMax;	/* Max number of catal streams */
149    xmlHashTablePtr sgml;
150
151    /*
152     * XML Catalogs are stored as a tree of Catalog entries
153     */
154    xmlCatalogPrefer prefer;
155    xmlCatalogEntryPtr xml;
156};
157
158/************************************************************************
159 *									*
160 *			Global variables				*
161 *									*
162 ************************************************************************/
163
164/*
165 * Those are preferences
166 */
167static int xmlDebugCatalogs = 0;   /* used for debugging */
168static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
169static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
170
171/*
172 * Hash table containing all the trees of XML catalogs parsed by
173 * the application.
174 */
175static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
176
177/*
178 * The default catalog in use by the application
179 */
180static xmlCatalogPtr xmlDefaultCatalog = NULL;
181
182/*
183 * A mutex for modifying the shared global catalog(s)
184 * xmlDefaultCatalog tree.
185 * It also protects xmlCatalogXMLFiles
186 * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
187 */
188static xmlRMutexPtr xmlCatalogMutex = NULL;
189
190/*
191 * Whether the catalog support was initialized.
192 */
193static int xmlCatalogInitialized = 0;
194
195/************************************************************************
196 *									*
197 * 			Catalog error handlers				*
198 *									*
199 ************************************************************************/
200
201/**
202 * xmlCatalogErrMemory:
203 * @extra:  extra informations
204 *
205 * Handle an out of memory condition
206 */
207static void
208xmlCatalogErrMemory(const char *extra)
209{
210    __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
211                    XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
212		    extra, NULL, NULL, 0, 0,
213		    "Memory allocation failed : %s\n", extra);
214}
215
216/**
217 * xmlCatalogErr:
218 * @catal: the Catalog entry
219 * @node: the context node
220 * @msg:  the error message
221 * @extra:  extra informations
222 *
223 * Handle a catalog error
224 */
225static void
226xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
227               const char *msg, const xmlChar *str1, const xmlChar *str2,
228	       const xmlChar *str3)
229{
230    __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
231                    error, XML_ERR_ERROR, NULL, 0,
232		    (const char *) str1, (const char *) str2,
233		    (const char *) str3, 0, 0,
234		    msg, str1, str2, str3);
235}
236
237
238/************************************************************************
239 *									*
240 *			Allocation and Freeing				*
241 *									*
242 ************************************************************************/
243
244/**
245 * xmlNewCatalogEntry:
246 * @type:  type of entry
247 * @name:  name of the entry
248 * @value:  value of the entry
249 * @prefer:  the PUBLIC vs. SYSTEM current preference value
250 * @group:  for members of a group, the group entry
251 *
252 * create a new Catalog entry, this type is shared both by XML and
253 * SGML catalogs, but the acceptable types values differs.
254 *
255 * Returns the xmlCatalogEntryPtr or NULL in case of error
256 */
257static xmlCatalogEntryPtr
258xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
259	   const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
260	   xmlCatalogEntryPtr group) {
261    xmlCatalogEntryPtr ret;
262    xmlChar *normid = NULL;
263
264    ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
265    if (ret == NULL) {
266        xmlCatalogErrMemory("allocating catalog entry");
267	return(NULL);
268    }
269    ret->next = NULL;
270    ret->parent = NULL;
271    ret->children = NULL;
272    ret->type = type;
273    if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
274        normid = xmlCatalogNormalizePublic(name);
275        if (normid != NULL)
276            name = (*normid != 0 ? normid : NULL);
277    }
278    if (name != NULL)
279	ret->name = xmlStrdup(name);
280    else
281	ret->name = NULL;
282    if (normid != NULL)
283        xmlFree(normid);
284    if (value != NULL)
285	ret->value = xmlStrdup(value);
286    else
287	ret->value = NULL;
288    if (URL == NULL)
289	URL = value;
290    if (URL != NULL)
291	ret->URL = xmlStrdup(URL);
292    else
293	ret->URL = NULL;
294    ret->prefer = prefer;
295    ret->dealloc = 0;
296    ret->depth = 0;
297    ret->group = group;
298    return(ret);
299}
300
301static void
302xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
303
304/**
305 * xmlFreeCatalogEntry:
306 * @ret:  a Catalog entry
307 *
308 * Free the memory allocated to a Catalog entry
309 */
310static void
311xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
312    if (ret == NULL)
313	return;
314    /*
315     * Entries stored in the file hash must be deallocated
316     * only by the file hash cleaner !
317     */
318    if (ret->dealloc == 1)
319	return;
320
321    if (xmlDebugCatalogs) {
322	if (ret->name != NULL)
323	    xmlGenericError(xmlGenericErrorContext,
324		    "Free catalog entry %s\n", ret->name);
325	else if (ret->value != NULL)
326	    xmlGenericError(xmlGenericErrorContext,
327		    "Free catalog entry %s\n", ret->value);
328	else
329	    xmlGenericError(xmlGenericErrorContext,
330		    "Free catalog entry\n");
331    }
332
333    if (ret->name != NULL)
334	xmlFree(ret->name);
335    if (ret->value != NULL)
336	xmlFree(ret->value);
337    if (ret->URL != NULL)
338	xmlFree(ret->URL);
339    xmlFree(ret);
340}
341
342/**
343 * xmlFreeCatalogEntryList:
344 * @ret:  a Catalog entry list
345 *
346 * Free the memory allocated to a full chained list of Catalog entries
347 */
348static void
349xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
350    xmlCatalogEntryPtr next;
351
352    while (ret != NULL) {
353	next = ret->next;
354	xmlFreeCatalogEntry(ret);
355	ret = next;
356    }
357}
358
359/**
360 * xmlFreeCatalogHashEntryList:
361 * @ret:  a Catalog entry list
362 *
363 * Free the memory allocated to list of Catalog entries from the
364 * catalog file hash.
365 */
366static void
367xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {
368    xmlCatalogEntryPtr children, next;
369
370    if (catal == NULL)
371	return;
372
373    children = catal->children;
374    while (children != NULL) {
375	next = children->next;
376	children->dealloc = 0;
377	children->children = NULL;
378	xmlFreeCatalogEntry(children);
379	children = next;
380    }
381    catal->dealloc = 0;
382    xmlFreeCatalogEntry(catal);
383}
384
385/**
386 * xmlCreateNewCatalog:
387 * @type:  type of catalog
388 * @prefer:  the PUBLIC vs. SYSTEM current preference value
389 *
390 * create a new Catalog, this type is shared both by XML and
391 * SGML catalogs, but the acceptable types values differs.
392 *
393 * Returns the xmlCatalogPtr or NULL in case of error
394 */
395static xmlCatalogPtr
396xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
397    xmlCatalogPtr ret;
398
399    ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
400    if (ret == NULL) {
401        xmlCatalogErrMemory("allocating catalog");
402	return(NULL);
403    }
404    memset(ret, 0, sizeof(xmlCatalog));
405    ret->type = type;
406    ret->catalNr = 0;
407    ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
408    ret->prefer = prefer;
409    if (ret->type == XML_SGML_CATALOG_TYPE)
410	ret->sgml = xmlHashCreate(10);
411    return(ret);
412}
413
414/**
415 * xmlFreeCatalog:
416 * @catal:  a Catalog
417 *
418 * Free the memory allocated to a Catalog
419 */
420void
421xmlFreeCatalog(xmlCatalogPtr catal) {
422    if (catal == NULL)
423	return;
424    if (catal->xml != NULL)
425	xmlFreeCatalogEntryList(catal->xml);
426    if (catal->sgml != NULL)
427	xmlHashFree(catal->sgml,
428		(xmlHashDeallocator) xmlFreeCatalogEntry);
429    xmlFree(catal);
430}
431
432/************************************************************************
433 *									*
434 *			Serializing Catalogs				*
435 *									*
436 ************************************************************************/
437
438#ifdef LIBXML_OUTPUT_ENABLED
439/**
440 * xmlCatalogDumpEntry:
441 * @entry:  the catalog entry
442 * @out:  the file.
443 *
444 * Serialize an SGML Catalog entry
445 */
446static void
447xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
448    if ((entry == NULL) || (out == NULL))
449	return;
450    switch (entry->type) {
451	case SGML_CATA_ENTITY:
452	    fprintf(out, "ENTITY "); break;
453	case SGML_CATA_PENTITY:
454	    fprintf(out, "ENTITY %%"); break;
455	case SGML_CATA_DOCTYPE:
456	    fprintf(out, "DOCTYPE "); break;
457	case SGML_CATA_LINKTYPE:
458	    fprintf(out, "LINKTYPE "); break;
459	case SGML_CATA_NOTATION:
460	    fprintf(out, "NOTATION "); break;
461	case SGML_CATA_PUBLIC:
462	    fprintf(out, "PUBLIC "); break;
463	case SGML_CATA_SYSTEM:
464	    fprintf(out, "SYSTEM "); break;
465	case SGML_CATA_DELEGATE:
466	    fprintf(out, "DELEGATE "); break;
467	case SGML_CATA_BASE:
468	    fprintf(out, "BASE "); break;
469	case SGML_CATA_CATALOG:
470	    fprintf(out, "CATALOG "); break;
471	case SGML_CATA_DOCUMENT:
472	    fprintf(out, "DOCUMENT "); break;
473	case SGML_CATA_SGMLDECL:
474	    fprintf(out, "SGMLDECL "); break;
475	default:
476	    return;
477    }
478    switch (entry->type) {
479	case SGML_CATA_ENTITY:
480	case SGML_CATA_PENTITY:
481	case SGML_CATA_DOCTYPE:
482	case SGML_CATA_LINKTYPE:
483	case SGML_CATA_NOTATION:
484	    fprintf(out, "%s", (const char *) entry->name); break;
485	case SGML_CATA_PUBLIC:
486	case SGML_CATA_SYSTEM:
487	case SGML_CATA_SGMLDECL:
488	case SGML_CATA_DOCUMENT:
489	case SGML_CATA_CATALOG:
490	case SGML_CATA_BASE:
491	case SGML_CATA_DELEGATE:
492	    fprintf(out, "\"%s\"", entry->name); break;
493	default:
494	    break;
495    }
496    switch (entry->type) {
497	case SGML_CATA_ENTITY:
498	case SGML_CATA_PENTITY:
499	case SGML_CATA_DOCTYPE:
500	case SGML_CATA_LINKTYPE:
501	case SGML_CATA_NOTATION:
502	case SGML_CATA_PUBLIC:
503	case SGML_CATA_SYSTEM:
504	case SGML_CATA_DELEGATE:
505	    fprintf(out, " \"%s\"", entry->value); break;
506	default:
507	    break;
508    }
509    fprintf(out, "\n");
510}
511
512/**
513 * xmlDumpXMLCatalogNode:
514 * @catal:  top catalog entry
515 * @catalog: pointer to the xml tree
516 * @doc: the containing document
517 * @ns: the current namespace
518 * @cgroup: group node for group members
519 *
520 * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
521 * for group entries
522 */
523static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
524		    xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
525    xmlNodePtr node;
526    xmlCatalogEntryPtr cur;
527    /*
528     * add all the catalog entries
529     */
530    cur = catal;
531    while (cur != NULL) {
532        if (cur->group == cgroup) {
533	    switch (cur->type) {
534	        case XML_CATA_REMOVED:
535		    break;
536	        case XML_CATA_BROKEN_CATALOG:
537	        case XML_CATA_CATALOG:
538		    if (cur == catal) {
539			cur = cur->children;
540		        continue;
541		    }
542		    break;
543		case XML_CATA_NEXT_CATALOG:
544		    node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
545		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
546		    xmlAddChild(catalog, node);
547                    break;
548		case XML_CATA_NONE:
549		    break;
550		case XML_CATA_GROUP:
551		    node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
552		    xmlSetProp(node, BAD_CAST "id", cur->name);
553		    if (cur->value != NULL) {
554		        xmlNsPtr xns;
555			xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
556			if (xns != NULL)
557			    xmlSetNsProp(node, xns, BAD_CAST "base",
558			    		 cur->value);
559		    }
560		    switch (cur->prefer) {
561			case XML_CATA_PREFER_NONE:
562		            break;
563			case XML_CATA_PREFER_PUBLIC:
564		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
565			    break;
566			case XML_CATA_PREFER_SYSTEM:
567		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
568			    break;
569		    }
570		    xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
571		    xmlAddChild(catalog, node);
572	            break;
573		case XML_CATA_PUBLIC:
574		    node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
575		    xmlSetProp(node, BAD_CAST "publicId", cur->name);
576		    xmlSetProp(node, BAD_CAST "uri", cur->value);
577		    xmlAddChild(catalog, node);
578		    break;
579		case XML_CATA_SYSTEM:
580		    node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
581		    xmlSetProp(node, BAD_CAST "systemId", cur->name);
582		    xmlSetProp(node, BAD_CAST "uri", cur->value);
583		    xmlAddChild(catalog, node);
584		    break;
585		case XML_CATA_REWRITE_SYSTEM:
586		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
587		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
588		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
589		    xmlAddChild(catalog, node);
590		    break;
591		case XML_CATA_DELEGATE_PUBLIC:
592		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
593		    xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
594		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
595		    xmlAddChild(catalog, node);
596		    break;
597		case XML_CATA_DELEGATE_SYSTEM:
598		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
599		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
600		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
601		    xmlAddChild(catalog, node);
602		    break;
603		case XML_CATA_URI:
604		    node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
605		    xmlSetProp(node, BAD_CAST "name", cur->name);
606		    xmlSetProp(node, BAD_CAST "uri", cur->value);
607		    xmlAddChild(catalog, node);
608		    break;
609		case XML_CATA_REWRITE_URI:
610		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
611		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
612		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
613		    xmlAddChild(catalog, node);
614		    break;
615		case XML_CATA_DELEGATE_URI:
616		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
617		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
618		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
619		    xmlAddChild(catalog, node);
620		    break;
621		case SGML_CATA_SYSTEM:
622		case SGML_CATA_PUBLIC:
623		case SGML_CATA_ENTITY:
624		case SGML_CATA_PENTITY:
625		case SGML_CATA_DOCTYPE:
626		case SGML_CATA_LINKTYPE:
627		case SGML_CATA_NOTATION:
628		case SGML_CATA_DELEGATE:
629		case SGML_CATA_BASE:
630		case SGML_CATA_CATALOG:
631		case SGML_CATA_DOCUMENT:
632		case SGML_CATA_SGMLDECL:
633		    break;
634	    }
635        }
636	cur = cur->next;
637    }
638}
639
640static int
641xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
642    int ret;
643    xmlDocPtr doc;
644    xmlNsPtr ns;
645    xmlDtdPtr dtd;
646    xmlNodePtr catalog;
647    xmlOutputBufferPtr buf;
648
649    /*
650     * Rebuild a catalog
651     */
652    doc = xmlNewDoc(NULL);
653    if (doc == NULL)
654	return(-1);
655    dtd = xmlNewDtd(doc, BAD_CAST "catalog",
656	       BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
657BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
658
659    xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
660
661    ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
662    if (ns == NULL) {
663	xmlFreeDoc(doc);
664	return(-1);
665    }
666    catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
667    if (catalog == NULL) {
668	xmlFreeNs(ns);
669	xmlFreeDoc(doc);
670	return(-1);
671    }
672    catalog->nsDef = ns;
673    xmlAddChild((xmlNodePtr) doc, catalog);
674
675    xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
676
677    /*
678     * reserialize it
679     */
680    buf = xmlOutputBufferCreateFile(out, NULL);
681    if (buf == NULL) {
682	xmlFreeDoc(doc);
683	return(-1);
684    }
685    ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
686
687    /*
688     * Free it
689     */
690    xmlFreeDoc(doc);
691
692    return(ret);
693}
694#endif /* LIBXML_OUTPUT_ENABLED */
695
696/************************************************************************
697 *									*
698 *			Converting SGML Catalogs to XML			*
699 *									*
700 ************************************************************************/
701
702/**
703 * xmlCatalogConvertEntry:
704 * @entry:  the entry
705 * @catal:  pointer to the catalog being converted
706 *
707 * Convert one entry from the catalog
708 */
709static void
710xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {
711    if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
712	(catal->xml == NULL))
713	return;
714    switch (entry->type) {
715	case SGML_CATA_ENTITY:
716	    entry->type = XML_CATA_PUBLIC;
717	    break;
718	case SGML_CATA_PENTITY:
719	    entry->type = XML_CATA_PUBLIC;
720	    break;
721	case SGML_CATA_DOCTYPE:
722	    entry->type = XML_CATA_PUBLIC;
723	    break;
724	case SGML_CATA_LINKTYPE:
725	    entry->type = XML_CATA_PUBLIC;
726	    break;
727	case SGML_CATA_NOTATION:
728	    entry->type = XML_CATA_PUBLIC;
729	    break;
730	case SGML_CATA_PUBLIC:
731	    entry->type = XML_CATA_PUBLIC;
732	    break;
733	case SGML_CATA_SYSTEM:
734	    entry->type = XML_CATA_SYSTEM;
735	    break;
736	case SGML_CATA_DELEGATE:
737	    entry->type = XML_CATA_DELEGATE_PUBLIC;
738	    break;
739	case SGML_CATA_CATALOG:
740	    entry->type = XML_CATA_CATALOG;
741	    break;
742	default:
743	    xmlHashRemoveEntry(catal->sgml, entry->name,
744		               (xmlHashDeallocator) xmlFreeCatalogEntry);
745	    return;
746    }
747    /*
748     * Conversion successful, remove from the SGML catalog
749     * and add it to the default XML one
750     */
751    xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
752    entry->parent = catal->xml;
753    entry->next = NULL;
754    if (catal->xml->children == NULL)
755	catal->xml->children = entry;
756    else {
757	xmlCatalogEntryPtr prev;
758
759	prev = catal->xml->children;
760	while (prev->next != NULL)
761	    prev = prev->next;
762	prev->next = entry;
763    }
764}
765
766/**
767 * xmlConvertSGMLCatalog:
768 * @catal: the catalog
769 *
770 * Convert all the SGML catalog entries as XML ones
771 *
772 * Returns the number of entries converted if successful, -1 otherwise
773 */
774int
775xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
776
777    if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
778	return(-1);
779
780    if (xmlDebugCatalogs) {
781	xmlGenericError(xmlGenericErrorContext,
782		"Converting SGML catalog to XML\n");
783    }
784    xmlHashScan(catal->sgml,
785		(xmlHashScanner) xmlCatalogConvertEntry,
786		&catal);
787    return(0);
788}
789
790/************************************************************************
791 *									*
792 *			Helper function					*
793 *									*
794 ************************************************************************/
795
796/**
797 * xmlCatalogUnWrapURN:
798 * @urn:  an "urn:publicid:" to unwrap
799 *
800 * Expand the URN into the equivalent Public Identifier
801 *
802 * Returns the new identifier or NULL, the string must be deallocated
803 *         by the caller.
804 */
805static xmlChar *
806xmlCatalogUnWrapURN(const xmlChar *urn) {
807    xmlChar result[2000];
808    unsigned int i = 0;
809
810    if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
811	return(NULL);
812    urn += sizeof(XML_URN_PUBID) - 1;
813
814    while (*urn != 0) {
815	if (i > sizeof(result) - 4)
816	    break;
817	if (*urn == '+') {
818	    result[i++] = ' ';
819	    urn++;
820	} else if (*urn == ':') {
821	    result[i++] = '/';
822	    result[i++] = '/';
823	    urn++;
824	} else if (*urn == ';') {
825	    result[i++] = ':';
826	    result[i++] = ':';
827	    urn++;
828	} else if (*urn == '%') {
829	    if ((urn[1] == '2') && (urn[2] == 'B'))
830		result[i++] = '+';
831	    else if ((urn[1] == '3') && (urn[2] == 'A'))
832		result[i++] = ':';
833	    else if ((urn[1] == '2') && (urn[2] == 'F'))
834		result[i++] = '/';
835	    else if ((urn[1] == '3') && (urn[2] == 'B'))
836		result[i++] = ';';
837	    else if ((urn[1] == '2') && (urn[2] == '7'))
838		result[i++] = '\'';
839	    else if ((urn[1] == '3') && (urn[2] == 'F'))
840		result[i++] = '?';
841	    else if ((urn[1] == '2') && (urn[2] == '3'))
842		result[i++] = '#';
843	    else if ((urn[1] == '2') && (urn[2] == '5'))
844		result[i++] = '%';
845	    else {
846		result[i++] = *urn;
847		urn++;
848		continue;
849	    }
850	    urn += 3;
851	} else {
852	    result[i++] = *urn;
853	    urn++;
854	}
855    }
856    result[i] = 0;
857
858    return(xmlStrdup(result));
859}
860
861/**
862 * xmlParseCatalogFile:
863 * @filename:  the filename
864 *
865 * parse an XML file and build a tree. It's like xmlParseFile()
866 * except it bypass all catalog lookups.
867 *
868 * Returns the resulting document tree or NULL in case of error
869 */
870
871xmlDocPtr
872xmlParseCatalogFile(const char *filename) {
873    xmlDocPtr ret;
874    xmlParserCtxtPtr ctxt;
875    char *directory = NULL;
876    xmlParserInputPtr inputStream;
877    xmlParserInputBufferPtr buf;
878
879    ctxt = xmlNewParserCtxt();
880    if (ctxt == NULL) {
881	if (xmlDefaultSAXHandler.error != NULL) {
882	    xmlDefaultSAXHandler.error(NULL, "out of memory\n");
883	}
884	return(NULL);
885    }
886
887    buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
888    if (buf == NULL) {
889	xmlFreeParserCtxt(ctxt);
890	return(NULL);
891    }
892
893    inputStream = xmlNewInputStream(ctxt);
894    if (inputStream == NULL) {
895	xmlFreeParserCtxt(ctxt);
896	return(NULL);
897    }
898
899    inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
900    inputStream->buf = buf;
901    inputStream->base = inputStream->buf->buffer->content;
902    inputStream->cur = inputStream->buf->buffer->content;
903    inputStream->end =
904	&inputStream->buf->buffer->content[inputStream->buf->buffer->use];
905
906    inputPush(ctxt, inputStream);
907    if ((ctxt->directory == NULL) && (directory == NULL))
908        directory = xmlParserGetDirectory(filename);
909    if ((ctxt->directory == NULL) && (directory != NULL))
910        ctxt->directory = directory;
911    ctxt->valid = 0;
912    ctxt->validate = 0;
913    ctxt->loadsubset = 0;
914    ctxt->pedantic = 0;
915
916    xmlParseDocument(ctxt);
917
918    if (ctxt->wellFormed)
919	ret = ctxt->myDoc;
920    else {
921        ret = NULL;
922        xmlFreeDoc(ctxt->myDoc);
923        ctxt->myDoc = NULL;
924    }
925    xmlFreeParserCtxt(ctxt);
926
927    return(ret);
928}
929
930/**
931 * xmlLoadFileContent:
932 * @filename:  a file path
933 *
934 * Load a file content into memory.
935 *
936 * Returns a pointer to the 0 terminated string or NULL in case of error
937 */
938static xmlChar *
939xmlLoadFileContent(const char *filename)
940{
941#ifdef HAVE_STAT
942    int fd;
943#else
944    FILE *fd;
945#endif
946    int len;
947    long size;
948
949#ifdef HAVE_STAT
950    struct stat info;
951#endif
952    xmlChar *content;
953
954    if (filename == NULL)
955        return (NULL);
956
957#ifdef HAVE_STAT
958    if (stat(filename, &info) < 0)
959        return (NULL);
960#endif
961
962#ifdef HAVE_STAT
963    if ((fd = open(filename, O_RDONLY)) < 0)
964#else
965    if ((fd = fopen(filename, "rb")) == NULL)
966#endif
967    {
968        return (NULL);
969    }
970#ifdef HAVE_STAT
971    size = info.st_size;
972#else
973    if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
974        fclose(fd);
975        return (NULL);
976    }
977#endif
978    content = xmlMallocAtomic(size + 10);
979    if (content == NULL) {
980        xmlCatalogErrMemory("allocating catalog data");
981        return (NULL);
982    }
983#ifdef HAVE_STAT
984    len = read(fd, content, size);
985#else
986    len = fread(content, 1, size, fd);
987#endif
988    if (len < 0) {
989        xmlFree(content);
990        return (NULL);
991    }
992#ifdef HAVE_STAT
993    close(fd);
994#else
995    fclose(fd);
996#endif
997    content[len] = 0;
998
999    return(content);
1000}
1001
1002/**
1003 * xmlCatalogNormalizePublic:
1004 * @pubID:  the public ID string
1005 *
1006 *  Normalizes the Public Identifier
1007 *
1008 * Implements 6.2. Public Identifier Normalization
1009 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1010 *
1011 * Returns the new string or NULL, the string must be deallocated
1012 *         by the caller.
1013 */
1014static xmlChar *
1015xmlCatalogNormalizePublic(const xmlChar *pubID)
1016{
1017    int ok = 1;
1018    int white;
1019    const xmlChar *p;
1020    xmlChar *ret;
1021    xmlChar *q;
1022
1023    if (pubID == NULL)
1024        return(NULL);
1025
1026    white = 1;
1027    for (p = pubID;*p != 0 && ok;p++) {
1028        if (!xmlIsBlank_ch(*p))
1029            white = 0;
1030        else if (*p == 0x20 && !white)
1031            white = 1;
1032        else
1033            ok = 0;
1034    }
1035    if (ok && !white)	/* is normalized */
1036        return(NULL);
1037
1038    ret = xmlStrdup(pubID);
1039    q = ret;
1040    white = 0;
1041    for (p = pubID;*p != 0;p++) {
1042        if (xmlIsBlank_ch(*p)) {
1043            if (q != ret)
1044                white = 1;
1045        } else {
1046            if (white) {
1047                *(q++) = 0x20;
1048                white = 0;
1049            }
1050            *(q++) = *p;
1051        }
1052    }
1053    *q = 0;
1054    return(ret);
1055}
1056
1057/************************************************************************
1058 *									*
1059 *			The XML Catalog parser				*
1060 *									*
1061 ************************************************************************/
1062
1063static xmlCatalogEntryPtr
1064xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
1065static void
1066xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1067	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1068static xmlChar *
1069xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1070	              const xmlChar *sysID);
1071static xmlChar *
1072xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1073
1074
1075/**
1076 * xmlGetXMLCatalogEntryType:
1077 * @name:  the name
1078 *
1079 * lookup the internal type associated to an XML catalog entry name
1080 *
1081 * Returns the type associated with that name
1082 */
1083static xmlCatalogEntryType
1084xmlGetXMLCatalogEntryType(const xmlChar *name) {
1085    xmlCatalogEntryType type = XML_CATA_NONE;
1086    if (xmlStrEqual(name, (const xmlChar *) "system"))
1087	type = XML_CATA_SYSTEM;
1088    else if (xmlStrEqual(name, (const xmlChar *) "public"))
1089	type = XML_CATA_PUBLIC;
1090    else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
1091	type = XML_CATA_REWRITE_SYSTEM;
1092    else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
1093	type = XML_CATA_DELEGATE_PUBLIC;
1094    else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
1095	type = XML_CATA_DELEGATE_SYSTEM;
1096    else if (xmlStrEqual(name, (const xmlChar *) "uri"))
1097	type = XML_CATA_URI;
1098    else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
1099	type = XML_CATA_REWRITE_URI;
1100    else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
1101	type = XML_CATA_DELEGATE_URI;
1102    else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
1103	type = XML_CATA_NEXT_CATALOG;
1104    else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
1105	type = XML_CATA_CATALOG;
1106    return(type);
1107}
1108
1109/**
1110 * xmlParseXMLCatalogOneNode:
1111 * @cur:  the XML node
1112 * @type:  the type of Catalog entry
1113 * @name:  the name of the node
1114 * @attrName:  the attribute holding the value
1115 * @uriAttrName:  the attribute holding the URI-Reference
1116 * @prefer:  the PUBLIC vs. SYSTEM current preference value
1117 * @cgroup:  the group which includes this node
1118 *
1119 * Finishes the examination of an XML tree node of a catalog and build
1120 * a Catalog entry from it.
1121 *
1122 * Returns the new Catalog entry node or NULL in case of error.
1123 */
1124static xmlCatalogEntryPtr
1125xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
1126			  const xmlChar *name, const xmlChar *attrName,
1127			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
1128			  xmlCatalogEntryPtr cgroup) {
1129    int ok = 1;
1130    xmlChar *uriValue;
1131    xmlChar *nameValue = NULL;
1132    xmlChar *base = NULL;
1133    xmlChar *URL = NULL;
1134    xmlCatalogEntryPtr ret = NULL;
1135
1136    if (attrName != NULL) {
1137	nameValue = xmlGetProp(cur, attrName);
1138	if (nameValue == NULL) {
1139	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1140			  "%s entry lacks '%s'\n", name, attrName, NULL);
1141	    ok = 0;
1142	}
1143    }
1144    uriValue = xmlGetProp(cur, uriAttrName);
1145    if (uriValue == NULL) {
1146	xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1147		"%s entry lacks '%s'\n", name, uriAttrName, NULL);
1148	ok = 0;
1149    }
1150    if (!ok) {
1151	if (nameValue != NULL)
1152	    xmlFree(nameValue);
1153	if (uriValue != NULL)
1154	    xmlFree(uriValue);
1155	return(NULL);
1156    }
1157
1158    base = xmlNodeGetBase(cur->doc, cur);
1159    URL = xmlBuildURI(uriValue, base);
1160    if (URL != NULL) {
1161	if (xmlDebugCatalogs > 1) {
1162	    if (nameValue != NULL)
1163		xmlGenericError(xmlGenericErrorContext,
1164			"Found %s: '%s' '%s'\n", name, nameValue, URL);
1165	    else
1166		xmlGenericError(xmlGenericErrorContext,
1167			"Found %s: '%s'\n", name, URL);
1168	}
1169	ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
1170    } else {
1171	xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
1172		"%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1173    }
1174    if (nameValue != NULL)
1175	xmlFree(nameValue);
1176    if (uriValue != NULL)
1177	xmlFree(uriValue);
1178    if (base != NULL)
1179	xmlFree(base);
1180    if (URL != NULL)
1181	xmlFree(URL);
1182    return(ret);
1183}
1184
1185/**
1186 * xmlParseXMLCatalogNode:
1187 * @cur:  the XML node
1188 * @prefer:  the PUBLIC vs. SYSTEM current preference value
1189 * @parent:  the parent Catalog entry
1190 * @cgroup:  the group which includes this node
1191 *
1192 * Examines an XML tree node of a catalog and build
1193 * a Catalog entry from it adding it to its parent. The examination can
1194 * be recursive.
1195 */
1196static void
1197xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1198	               xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
1199{
1200    xmlChar *uri = NULL;
1201    xmlChar *URL = NULL;
1202    xmlChar *base = NULL;
1203    xmlCatalogEntryPtr entry = NULL;
1204
1205    if (cur == NULL)
1206        return;
1207    if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1208        xmlChar *prop;
1209	xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
1210
1211        prop = xmlGetProp(cur, BAD_CAST "prefer");
1212        if (prop != NULL) {
1213            if (xmlStrEqual(prop, BAD_CAST "system")) {
1214                prefer = XML_CATA_PREFER_SYSTEM;
1215            } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1216                prefer = XML_CATA_PREFER_PUBLIC;
1217            } else {
1218		xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
1219                              "Invalid value for prefer: '%s'\n",
1220			      prop, NULL, NULL);
1221            }
1222            xmlFree(prop);
1223	    pref = prefer;
1224        }
1225	prop = xmlGetProp(cur, BAD_CAST "id");
1226	base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
1227	entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
1228    } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1229	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1230		BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
1231    } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1232	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1233		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
1234    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1235	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1236		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1237		BAD_CAST "rewritePrefix", prefer, cgroup);
1238    } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1239	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1240		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1241		BAD_CAST "catalog", prefer, cgroup);
1242    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1243	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1244		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1245		BAD_CAST "catalog", prefer, cgroup);
1246    } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1247	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1248		BAD_CAST "uri", BAD_CAST "name",
1249		BAD_CAST "uri", prefer, cgroup);
1250    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1251	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1252		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1253		BAD_CAST "rewritePrefix", prefer, cgroup);
1254    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1255	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1256		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1257		BAD_CAST "catalog", prefer, cgroup);
1258    } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1259	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1260		BAD_CAST "nextCatalog", NULL,
1261		BAD_CAST "catalog", prefer, cgroup);
1262    }
1263    if ((entry != NULL) && (parent != NULL)) {
1264	entry->parent = parent;
1265	if (parent->children == NULL)
1266	    parent->children = entry;
1267	else {
1268	    xmlCatalogEntryPtr prev;
1269
1270	    prev = parent->children;
1271	    while (prev->next != NULL)
1272		prev = prev->next;
1273	    prev->next = entry;
1274	}
1275    }
1276    if (entry->type == XML_CATA_GROUP) {
1277	/*
1278	 * Recurse to propagate prefer to the subtree
1279	 * (xml:base handling is automated)
1280	 */
1281        xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
1282    }
1283    if (base != NULL)
1284	xmlFree(base);
1285    if (uri != NULL)
1286	xmlFree(uri);
1287    if (URL != NULL)
1288	xmlFree(URL);
1289}
1290
1291/**
1292 * xmlParseXMLCatalogNodeList:
1293 * @cur:  the XML node list of siblings
1294 * @prefer:  the PUBLIC vs. SYSTEM current preference value
1295 * @parent:  the parent Catalog entry
1296 * @cgroup:  the group which includes this list
1297 *
1298 * Examines a list of XML sibling nodes of a catalog and build
1299 * a list of Catalog entry from it adding it to the parent.
1300 * The examination will recurse to examine node subtrees.
1301 */
1302static void
1303xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1304	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
1305    while (cur != NULL) {
1306	if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1307	    (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1308	    xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
1309	}
1310	cur = cur->next;
1311    }
1312    /* TODO: sort the list according to REWRITE lengths and prefer value */
1313}
1314
1315/**
1316 * xmlParseXMLCatalogFile:
1317 * @prefer:  the PUBLIC vs. SYSTEM current preference value
1318 * @filename:  the filename for the catalog
1319 *
1320 * Parses the catalog file to extract the XML tree and then analyze the
1321 * tree to build a list of Catalog entries corresponding to this catalog
1322 *
1323 * Returns the resulting Catalog entries list
1324 */
1325static xmlCatalogEntryPtr
1326xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1327    xmlDocPtr doc;
1328    xmlNodePtr cur;
1329    xmlChar *prop;
1330    xmlCatalogEntryPtr parent = NULL;
1331
1332    if (filename == NULL)
1333        return(NULL);
1334
1335    doc = xmlParseCatalogFile((const char *) filename);
1336    if (doc == NULL) {
1337	if (xmlDebugCatalogs)
1338	    xmlGenericError(xmlGenericErrorContext,
1339		    "Failed to parse catalog %s\n", filename);
1340	return(NULL);
1341    }
1342
1343    if (xmlDebugCatalogs)
1344	xmlGenericError(xmlGenericErrorContext,
1345		"%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1346
1347    cur = xmlDocGetRootElement(doc);
1348    if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1349	(cur->ns != NULL) && (cur->ns->href != NULL) &&
1350	(xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1351
1352	parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1353				    (const xmlChar *)filename, NULL, prefer, NULL);
1354        if (parent == NULL) {
1355	    xmlFreeDoc(doc);
1356	    return(NULL);
1357	}
1358
1359	prop = xmlGetProp(cur, BAD_CAST "prefer");
1360	if (prop != NULL) {
1361	    if (xmlStrEqual(prop, BAD_CAST "system")) {
1362		prefer = XML_CATA_PREFER_SYSTEM;
1363	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1364		prefer = XML_CATA_PREFER_PUBLIC;
1365	    } else {
1366		xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
1367			      "Invalid value for prefer: '%s'\n",
1368			      prop, NULL, NULL);
1369	    }
1370	    xmlFree(prop);
1371	}
1372	cur = cur->children;
1373	xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
1374    } else {
1375	xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
1376		      "File %s is not an XML Catalog\n",
1377		      filename, NULL, NULL);
1378	xmlFreeDoc(doc);
1379	return(NULL);
1380    }
1381    xmlFreeDoc(doc);
1382    return(parent);
1383}
1384
1385/**
1386 * xmlFetchXMLCatalogFile:
1387 * @catal:  an existing but incomplete catalog entry
1388 *
1389 * Fetch and parse the subcatalog referenced by an entry
1390 *
1391 * Returns 0 in case of success, -1 otherwise
1392 */
1393static int
1394xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1395    xmlCatalogEntryPtr doc;
1396
1397    if (catal == NULL)
1398	return(-1);
1399    if (catal->URL == NULL)
1400	return(-1);
1401    if (catal->children != NULL)
1402	return(-1);
1403
1404    /*
1405     * lock the whole catalog for modification
1406     */
1407    xmlRMutexLock(xmlCatalogMutex);
1408    if (catal->children != NULL) {
1409	/* Okay someone else did it in the meantime */
1410	xmlRMutexUnlock(xmlCatalogMutex);
1411	return(0);
1412    }
1413
1414    if (xmlCatalogXMLFiles != NULL) {
1415	doc = (xmlCatalogEntryPtr)
1416	    xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1417	if (doc != NULL) {
1418	    if (xmlDebugCatalogs)
1419		xmlGenericError(xmlGenericErrorContext,
1420		    "Found %s in file hash\n", catal->URL);
1421
1422	    if (catal->type == XML_CATA_CATALOG)
1423		catal->children = doc->children;
1424	    else
1425		catal->children = doc;
1426	    catal->dealloc = 0;
1427	    xmlRMutexUnlock(xmlCatalogMutex);
1428	    return(0);
1429	}
1430	if (xmlDebugCatalogs)
1431	    xmlGenericError(xmlGenericErrorContext,
1432		"%s not found in file hash\n", catal->URL);
1433    }
1434
1435    /*
1436     * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1437     * use the existing catalog, there is no recursion allowed at
1438     * that level.
1439     */
1440    doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1441    if (doc == NULL) {
1442	catal->type = XML_CATA_BROKEN_CATALOG;
1443	xmlRMutexUnlock(xmlCatalogMutex);
1444	return(-1);
1445    }
1446
1447    if (catal->type == XML_CATA_CATALOG)
1448	catal->children = doc->children;
1449    else
1450	catal->children = doc;
1451
1452    doc->dealloc = 1;
1453
1454    if (xmlCatalogXMLFiles == NULL)
1455	xmlCatalogXMLFiles = xmlHashCreate(10);
1456    if (xmlCatalogXMLFiles != NULL) {
1457	if (xmlDebugCatalogs)
1458	    xmlGenericError(xmlGenericErrorContext,
1459		"%s added to file hash\n", catal->URL);
1460	xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1461    }
1462    xmlRMutexUnlock(xmlCatalogMutex);
1463    return(0);
1464}
1465
1466/************************************************************************
1467 *									*
1468 *			XML Catalog handling				*
1469 *									*
1470 ************************************************************************/
1471
1472/**
1473 * xmlAddXMLCatalog:
1474 * @catal:  top of an XML catalog
1475 * @type:  the type of record to add to the catalog
1476 * @orig:  the system, public or prefix to match (or NULL)
1477 * @replace:  the replacement value for the match
1478 *
1479 * Add an entry in the XML catalog, it may overwrite existing but
1480 * different entries.
1481 *
1482 * Returns 0 if successful, -1 otherwise
1483 */
1484static int
1485xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1486	      const xmlChar *orig, const xmlChar *replace) {
1487    xmlCatalogEntryPtr cur;
1488    xmlCatalogEntryType typ;
1489    int doregister = 0;
1490
1491    if ((catal == NULL) ||
1492	((catal->type != XML_CATA_CATALOG) &&
1493	 (catal->type != XML_CATA_BROKEN_CATALOG)))
1494	return(-1);
1495    if (catal->children == NULL) {
1496	xmlFetchXMLCatalogFile(catal);
1497    }
1498    if (catal->children == NULL)
1499	doregister = 1;
1500
1501    typ = xmlGetXMLCatalogEntryType(type);
1502    if (typ == XML_CATA_NONE) {
1503	if (xmlDebugCatalogs)
1504	    xmlGenericError(xmlGenericErrorContext,
1505		    "Failed to add unknown element %s to catalog\n", type);
1506	return(-1);
1507    }
1508
1509    cur = catal->children;
1510    /*
1511     * Might be a simple "update in place"
1512     */
1513    if (cur != NULL) {
1514	while (cur != NULL) {
1515	    if ((orig != NULL) && (cur->type == typ) &&
1516		(xmlStrEqual(orig, cur->name))) {
1517		if (xmlDebugCatalogs)
1518		    xmlGenericError(xmlGenericErrorContext,
1519			    "Updating element %s to catalog\n", type);
1520		if (cur->value != NULL)
1521		    xmlFree(cur->value);
1522		if (cur->URL != NULL)
1523		    xmlFree(cur->URL);
1524		cur->value = xmlStrdup(replace);
1525		cur->URL = xmlStrdup(replace);
1526		return(0);
1527	    }
1528	    if (cur->next == NULL)
1529		break;
1530	    cur = cur->next;
1531	}
1532    }
1533    if (xmlDebugCatalogs)
1534	xmlGenericError(xmlGenericErrorContext,
1535		"Adding element %s to catalog\n", type);
1536    if (cur == NULL)
1537	catal->children = xmlNewCatalogEntry(typ, orig, replace,
1538		                             NULL, catal->prefer, NULL);
1539    else
1540	cur->next = xmlNewCatalogEntry(typ, orig, replace,
1541		                       NULL, catal->prefer, NULL);
1542    if (doregister) {
1543	cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1544	if (cur != NULL)
1545	    cur->children = catal->children;
1546    }
1547
1548    return(0);
1549}
1550
1551/**
1552 * xmlDelXMLCatalog:
1553 * @catal:  top of an XML catalog
1554 * @value:  the value to remove from the catalog
1555 *
1556 * Remove entries in the XML catalog where the value or the URI
1557 * is equal to @value
1558 *
1559 * Returns the number of entries removed if successful, -1 otherwise
1560 */
1561static int
1562xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1563    xmlCatalogEntryPtr cur;
1564    int ret = 0;
1565
1566    if ((catal == NULL) ||
1567	((catal->type != XML_CATA_CATALOG) &&
1568	 (catal->type != XML_CATA_BROKEN_CATALOG)))
1569	return(-1);
1570    if (value == NULL)
1571	return(-1);
1572    if (catal->children == NULL) {
1573	xmlFetchXMLCatalogFile(catal);
1574    }
1575
1576    /*
1577     * Scan the children
1578     */
1579    cur = catal->children;
1580    while (cur != NULL) {
1581	if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1582	    (xmlStrEqual(value, cur->value))) {
1583	    if (xmlDebugCatalogs) {
1584		if (cur->name != NULL)
1585		    xmlGenericError(xmlGenericErrorContext,
1586			    "Removing element %s from catalog\n", cur->name);
1587		else
1588		    xmlGenericError(xmlGenericErrorContext,
1589			    "Removing element %s from catalog\n", cur->value);
1590	    }
1591	    cur->type = XML_CATA_REMOVED;
1592	}
1593	cur = cur->next;
1594    }
1595    return(ret);
1596}
1597
1598/**
1599 * xmlCatalogXMLResolve:
1600 * @catal:  a catalog list
1601 * @pubID:  the public ID string
1602 * @sysID:  the system ID string
1603 *
1604 * Do a complete resolution lookup of an External Identifier for a
1605 * list of catalog entries.
1606 *
1607 * Implements (or tries to) 7.1. External Identifier Resolution
1608 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1609 *
1610 * Returns the URI of the resource or NULL if not found
1611 */
1612static xmlChar *
1613xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1614	              const xmlChar *sysID) {
1615    xmlChar *ret = NULL;
1616    xmlCatalogEntryPtr cur;
1617    int haveDelegate = 0;
1618    int haveNext = 0;
1619
1620    /*
1621     * protection against loops
1622     */
1623    if (catal->depth > MAX_CATAL_DEPTH) {
1624	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1625		      "Detected recursion in catalog %s\n",
1626		      catal->name, NULL, NULL);
1627	return(NULL);
1628    }
1629    catal->depth++;
1630
1631    /*
1632     * First tries steps 2/ 3/ 4/ if a system ID is provided.
1633     */
1634    if (sysID != NULL) {
1635	xmlCatalogEntryPtr rewrite = NULL;
1636	int lenrewrite = 0, len;
1637	cur = catal;
1638	haveDelegate = 0;
1639	while (cur != NULL) {
1640	    switch (cur->type) {
1641		case XML_CATA_SYSTEM:
1642		    if (xmlStrEqual(sysID, cur->name)) {
1643			if (xmlDebugCatalogs)
1644			    xmlGenericError(xmlGenericErrorContext,
1645				    "Found system match %s\n", cur->name);
1646			catal->depth--;
1647			return(xmlStrdup(cur->URL));
1648		    }
1649		    break;
1650		case XML_CATA_REWRITE_SYSTEM:
1651		    len = xmlStrlen(cur->name);
1652		    if ((len > lenrewrite) &&
1653			(!xmlStrncmp(sysID, cur->name, len))) {
1654			lenrewrite = len;
1655			rewrite = cur;
1656		    }
1657		    break;
1658		case XML_CATA_DELEGATE_SYSTEM:
1659		    if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1660			haveDelegate++;
1661		    break;
1662		case XML_CATA_NEXT_CATALOG:
1663		    haveNext++;
1664		    break;
1665		default:
1666		    break;
1667	    }
1668	    cur = cur->next;
1669	}
1670	if (rewrite != NULL) {
1671	    if (xmlDebugCatalogs)
1672		xmlGenericError(xmlGenericErrorContext,
1673			"Using rewriting rule %s\n", rewrite->name);
1674	    ret = xmlStrdup(rewrite->URL);
1675	    if (ret != NULL)
1676		ret = xmlStrcat(ret, &sysID[lenrewrite]);
1677	    catal->depth--;
1678	    return(ret);
1679	}
1680	if (haveDelegate) {
1681	    const xmlChar *delegates[MAX_DELEGATE];
1682	    int nbList = 0, i;
1683
1684	    /*
1685	     * Assume the entries have been sorted by decreasing substring
1686	     * matches when the list was produced.
1687	     */
1688	    cur = catal;
1689	    while (cur != NULL) {
1690		if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1691		    (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1692		    for (i = 0;i < nbList;i++)
1693			if (xmlStrEqual(cur->URL, delegates[i]))
1694			    break;
1695		    if (i < nbList) {
1696			cur = cur->next;
1697			continue;
1698		    }
1699		    if (nbList < MAX_DELEGATE)
1700			delegates[nbList++] = cur->URL;
1701
1702		    if (cur->children == NULL) {
1703			xmlFetchXMLCatalogFile(cur);
1704		    }
1705		    if (cur->children != NULL) {
1706			if (xmlDebugCatalogs)
1707			    xmlGenericError(xmlGenericErrorContext,
1708				    "Trying system delegate %s\n", cur->URL);
1709			ret = xmlCatalogListXMLResolve(
1710				cur->children, NULL, sysID);
1711			if (ret != NULL) {
1712			    catal->depth--;
1713			    return(ret);
1714			}
1715		    }
1716		}
1717		cur = cur->next;
1718	    }
1719	    /*
1720	     * Apply the cut algorithm explained in 4/
1721	     */
1722	    catal->depth--;
1723	    return(XML_CATAL_BREAK);
1724	}
1725    }
1726    /*
1727     * Then tries 5/ 6/ if a public ID is provided
1728     */
1729    if (pubID != NULL) {
1730	cur = catal;
1731	haveDelegate = 0;
1732	while (cur != NULL) {
1733	    switch (cur->type) {
1734		case XML_CATA_PUBLIC:
1735		    if (xmlStrEqual(pubID, cur->name)) {
1736			if (xmlDebugCatalogs)
1737			    xmlGenericError(xmlGenericErrorContext,
1738				    "Found public match %s\n", cur->name);
1739			catal->depth--;
1740			return(xmlStrdup(cur->URL));
1741		    }
1742		    break;
1743		case XML_CATA_DELEGATE_PUBLIC:
1744		    if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1745			(cur->prefer == XML_CATA_PREFER_PUBLIC))
1746			haveDelegate++;
1747		    break;
1748		case XML_CATA_NEXT_CATALOG:
1749		    if (sysID == NULL)
1750			haveNext++;
1751		    break;
1752		default:
1753		    break;
1754	    }
1755	    cur = cur->next;
1756	}
1757	if (haveDelegate) {
1758	    const xmlChar *delegates[MAX_DELEGATE];
1759	    int nbList = 0, i;
1760
1761	    /*
1762	     * Assume the entries have been sorted by decreasing substring
1763	     * matches when the list was produced.
1764	     */
1765	    cur = catal;
1766	    while (cur != NULL) {
1767		if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1768		    (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1769		    (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1770
1771		    for (i = 0;i < nbList;i++)
1772			if (xmlStrEqual(cur->URL, delegates[i]))
1773			    break;
1774		    if (i < nbList) {
1775			cur = cur->next;
1776			continue;
1777		    }
1778		    if (nbList < MAX_DELEGATE)
1779			delegates[nbList++] = cur->URL;
1780
1781		    if (cur->children == NULL) {
1782			xmlFetchXMLCatalogFile(cur);
1783		    }
1784		    if (cur->children != NULL) {
1785			if (xmlDebugCatalogs)
1786			    xmlGenericError(xmlGenericErrorContext,
1787				    "Trying public delegate %s\n", cur->URL);
1788			ret = xmlCatalogListXMLResolve(
1789				cur->children, pubID, NULL);
1790			if (ret != NULL) {
1791			    catal->depth--;
1792			    return(ret);
1793			}
1794		    }
1795		}
1796		cur = cur->next;
1797	    }
1798	    /*
1799	     * Apply the cut algorithm explained in 4/
1800	     */
1801	    catal->depth--;
1802	    return(XML_CATAL_BREAK);
1803	}
1804    }
1805    if (haveNext) {
1806	cur = catal;
1807	while (cur != NULL) {
1808	    if (cur->type == XML_CATA_NEXT_CATALOG) {
1809		if (cur->children == NULL) {
1810		    xmlFetchXMLCatalogFile(cur);
1811		}
1812		if (cur->children != NULL) {
1813		    ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1814		    if (ret != NULL) {
1815			catal->depth--;
1816			return(ret);
1817		    }
1818		}
1819	    }
1820	    cur = cur->next;
1821	}
1822    }
1823
1824    catal->depth--;
1825    return(NULL);
1826}
1827
1828/**
1829 * xmlCatalogXMLResolveURI:
1830 * @catal:  a catalog list
1831 * @URI:  the URI
1832 * @sysID:  the system ID string
1833 *
1834 * Do a complete resolution lookup of an External Identifier for a
1835 * list of catalog entries.
1836 *
1837 * Implements (or tries to) 7.2.2. URI Resolution
1838 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1839 *
1840 * Returns the URI of the resource or NULL if not found
1841 */
1842static xmlChar *
1843xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1844    xmlChar *ret = NULL;
1845    xmlCatalogEntryPtr cur;
1846    int haveDelegate = 0;
1847    int haveNext = 0;
1848    xmlCatalogEntryPtr rewrite = NULL;
1849    int lenrewrite = 0, len;
1850
1851    if (catal == NULL)
1852	return(NULL);
1853
1854    if (URI == NULL)
1855	return(NULL);
1856
1857    /*
1858     * First tries steps 2/ 3/ 4/ if a system ID is provided.
1859     */
1860    cur = catal;
1861    haveDelegate = 0;
1862    while (cur != NULL) {
1863	switch (cur->type) {
1864	    case XML_CATA_URI:
1865		if (xmlStrEqual(URI, cur->name)) {
1866		    if (xmlDebugCatalogs)
1867			xmlGenericError(xmlGenericErrorContext,
1868				"Found URI match %s\n", cur->name);
1869		    return(xmlStrdup(cur->URL));
1870		}
1871		break;
1872	    case XML_CATA_REWRITE_URI:
1873		len = xmlStrlen(cur->name);
1874		if ((len > lenrewrite) &&
1875		    (!xmlStrncmp(URI, cur->name, len))) {
1876		    lenrewrite = len;
1877		    rewrite = cur;
1878		}
1879		break;
1880	    case XML_CATA_DELEGATE_URI:
1881		if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1882		    haveDelegate++;
1883		break;
1884	    case XML_CATA_NEXT_CATALOG:
1885		haveNext++;
1886		break;
1887	    default:
1888		break;
1889	}
1890	cur = cur->next;
1891    }
1892    if (rewrite != NULL) {
1893	if (xmlDebugCatalogs)
1894	    xmlGenericError(xmlGenericErrorContext,
1895		    "Using rewriting rule %s\n", rewrite->name);
1896	ret = xmlStrdup(rewrite->URL);
1897	if (ret != NULL)
1898	    ret = xmlStrcat(ret, &URI[lenrewrite]);
1899	return(ret);
1900    }
1901    if (haveDelegate) {
1902	const xmlChar *delegates[MAX_DELEGATE];
1903	int nbList = 0, i;
1904
1905	/*
1906	 * Assume the entries have been sorted by decreasing substring
1907	 * matches when the list was produced.
1908	 */
1909	cur = catal;
1910	while (cur != NULL) {
1911	    if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1912	         (cur->type == XML_CATA_DELEGATE_URI)) &&
1913		(!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1914		for (i = 0;i < nbList;i++)
1915		    if (xmlStrEqual(cur->URL, delegates[i]))
1916			break;
1917		if (i < nbList) {
1918		    cur = cur->next;
1919		    continue;
1920		}
1921		if (nbList < MAX_DELEGATE)
1922		    delegates[nbList++] = cur->URL;
1923
1924		if (cur->children == NULL) {
1925		    xmlFetchXMLCatalogFile(cur);
1926		}
1927		if (cur->children != NULL) {
1928		    if (xmlDebugCatalogs)
1929			xmlGenericError(xmlGenericErrorContext,
1930				"Trying URI delegate %s\n", cur->URL);
1931		    ret = xmlCatalogListXMLResolveURI(
1932			    cur->children, URI);
1933		    if (ret != NULL)
1934			return(ret);
1935		}
1936	    }
1937	    cur = cur->next;
1938	}
1939	/*
1940	 * Apply the cut algorithm explained in 4/
1941	 */
1942	return(XML_CATAL_BREAK);
1943    }
1944    if (haveNext) {
1945	cur = catal;
1946	while (cur != NULL) {
1947	    if (cur->type == XML_CATA_NEXT_CATALOG) {
1948		if (cur->children == NULL) {
1949		    xmlFetchXMLCatalogFile(cur);
1950		}
1951		if (cur->children != NULL) {
1952		    ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1953		    if (ret != NULL)
1954			return(ret);
1955		}
1956	    }
1957	    cur = cur->next;
1958	}
1959    }
1960
1961    return(NULL);
1962}
1963
1964/**
1965 * xmlCatalogListXMLResolve:
1966 * @catal:  a catalog list
1967 * @pubID:  the public ID string
1968 * @sysID:  the system ID string
1969 *
1970 * Do a complete resolution lookup of an External Identifier for a
1971 * list of catalogs
1972 *
1973 * Implements (or tries to) 7.1. External Identifier Resolution
1974 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1975 *
1976 * Returns the URI of the resource or NULL if not found
1977 */
1978static xmlChar *
1979xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1980	              const xmlChar *sysID) {
1981    xmlChar *ret = NULL;
1982    xmlChar *urnID = NULL;
1983    xmlChar *normid;
1984
1985    if (catal == NULL)
1986        return(NULL);
1987    if ((pubID == NULL) && (sysID == NULL))
1988	return(NULL);
1989
1990    normid = xmlCatalogNormalizePublic(pubID);
1991    if (normid != NULL)
1992        pubID = (*normid != 0 ? normid : NULL);
1993
1994    if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
1995	urnID = xmlCatalogUnWrapURN(pubID);
1996	if (xmlDebugCatalogs) {
1997	    if (urnID == NULL)
1998		xmlGenericError(xmlGenericErrorContext,
1999			"Public URN ID %s expanded to NULL\n", pubID);
2000	    else
2001		xmlGenericError(xmlGenericErrorContext,
2002			"Public URN ID expanded to %s\n", urnID);
2003	}
2004	ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
2005	if (urnID != NULL)
2006	    xmlFree(urnID);
2007	if (normid != NULL)
2008	    xmlFree(normid);
2009	return(ret);
2010    }
2011    if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2012	urnID = xmlCatalogUnWrapURN(sysID);
2013	if (xmlDebugCatalogs) {
2014	    if (urnID == NULL)
2015		xmlGenericError(xmlGenericErrorContext,
2016			"System URN ID %s expanded to NULL\n", sysID);
2017	    else
2018		xmlGenericError(xmlGenericErrorContext,
2019			"System URN ID expanded to %s\n", urnID);
2020	}
2021	if (pubID == NULL)
2022	    ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2023	else if (xmlStrEqual(pubID, urnID))
2024	    ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
2025	else {
2026	    ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
2027	}
2028	if (urnID != NULL)
2029	    xmlFree(urnID);
2030	if (normid != NULL)
2031	    xmlFree(normid);
2032	return(ret);
2033    }
2034    while (catal != NULL) {
2035	if (catal->type == XML_CATA_CATALOG) {
2036	    if (catal->children == NULL) {
2037		xmlFetchXMLCatalogFile(catal);
2038	    }
2039	    if (catal->children != NULL) {
2040		ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
2041		if (ret != NULL) {
2042                    if (normid != NULL)
2043                        xmlFree(normid);
2044		    return(ret);
2045                }
2046	    }
2047	}
2048	catal = catal->next;
2049    }
2050	if (normid != NULL)
2051	    xmlFree(normid);
2052    return(ret);
2053}
2054
2055/**
2056 * xmlCatalogListXMLResolveURI:
2057 * @catal:  a catalog list
2058 * @URI:  the URI
2059 *
2060 * Do a complete resolution lookup of an URI for a list of catalogs
2061 *
2062 * Implements (or tries to) 7.2. URI Resolution
2063 * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
2064 *
2065 * Returns the URI of the resource or NULL if not found
2066 */
2067static xmlChar *
2068xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
2069    xmlChar *ret = NULL;
2070    xmlChar *urnID = NULL;
2071
2072    if (catal == NULL)
2073        return(NULL);
2074    if (URI == NULL)
2075	return(NULL);
2076
2077    if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2078	urnID = xmlCatalogUnWrapURN(URI);
2079	if (xmlDebugCatalogs) {
2080	    if (urnID == NULL)
2081		xmlGenericError(xmlGenericErrorContext,
2082			"URN ID %s expanded to NULL\n", URI);
2083	    else
2084		xmlGenericError(xmlGenericErrorContext,
2085			"URN ID expanded to %s\n", urnID);
2086	}
2087	ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2088	if (urnID != NULL)
2089	    xmlFree(urnID);
2090	return(ret);
2091    }
2092    while (catal != NULL) {
2093	if (catal->type == XML_CATA_CATALOG) {
2094	    if (catal->children == NULL) {
2095		xmlFetchXMLCatalogFile(catal);
2096	    }
2097	    if (catal->children != NULL) {
2098		ret = xmlCatalogXMLResolveURI(catal->children, URI);
2099		if (ret != NULL)
2100		    return(ret);
2101	    }
2102	}
2103	catal = catal->next;
2104    }
2105    return(ret);
2106}
2107
2108/************************************************************************
2109 *									*
2110 *			The SGML Catalog parser				*
2111 *									*
2112 ************************************************************************/
2113
2114
2115#define RAW *cur
2116#define NEXT cur++;
2117#define SKIP(x) cur += x;
2118
2119#define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
2120
2121/**
2122 * xmlParseSGMLCatalogComment:
2123 * @cur:  the current character
2124 *
2125 * Skip a comment in an SGML catalog
2126 *
2127 * Returns new current character
2128 */
2129static const xmlChar *
2130xmlParseSGMLCatalogComment(const xmlChar *cur) {
2131    if ((cur[0] != '-') || (cur[1] != '-'))
2132	return(cur);
2133    SKIP(2);
2134    while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
2135	NEXT;
2136    if (cur[0] == 0) {
2137	return(NULL);
2138    }
2139    return(cur + 2);
2140}
2141
2142/**
2143 * xmlParseSGMLCatalogPubid:
2144 * @cur:  the current character
2145 * @id:  the return location
2146 *
2147 * Parse an SGML catalog ID
2148 *
2149 * Returns new current character and store the value in @id
2150 */
2151static const xmlChar *
2152xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
2153    xmlChar *buf = NULL, *tmp;
2154    int len = 0;
2155    int size = 50;
2156    xmlChar stop;
2157    int count = 0;
2158
2159    *id = NULL;
2160
2161    if (RAW == '"') {
2162        NEXT;
2163	stop = '"';
2164    } else if (RAW == '\'') {
2165        NEXT;
2166	stop = '\'';
2167    } else {
2168	stop = ' ';
2169    }
2170    buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
2171    if (buf == NULL) {
2172        xmlCatalogErrMemory("allocating public ID");
2173	return(NULL);
2174    }
2175    while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
2176	if ((*cur == stop) && (stop != ' '))
2177	    break;
2178	if ((stop == ' ') && (IS_BLANK_CH(*cur)))
2179	    break;
2180	if (len + 1 >= size) {
2181	    size *= 2;
2182	    tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
2183	    if (tmp == NULL) {
2184		xmlCatalogErrMemory("allocating public ID");
2185		xmlFree(buf);
2186		return(NULL);
2187	    }
2188	    buf = tmp;
2189	}
2190	buf[len++] = *cur;
2191	count++;
2192	NEXT;
2193    }
2194    buf[len] = 0;
2195    if (stop == ' ') {
2196	if (!IS_BLANK_CH(*cur)) {
2197	    xmlFree(buf);
2198	    return(NULL);
2199	}
2200    } else {
2201	if (*cur != stop) {
2202	    xmlFree(buf);
2203	    return(NULL);
2204	}
2205	NEXT;
2206    }
2207    *id = buf;
2208    return(cur);
2209}
2210
2211/**
2212 * xmlParseSGMLCatalogName:
2213 * @cur:  the current character
2214 * @name:  the return location
2215 *
2216 * Parse an SGML catalog name
2217 *
2218 * Returns new current character and store the value in @name
2219 */
2220static const xmlChar *
2221xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2222    xmlChar buf[XML_MAX_NAMELEN + 5];
2223    int len = 0;
2224    int c;
2225
2226    *name = NULL;
2227
2228    /*
2229     * Handler for more complex cases
2230     */
2231    c = *cur;
2232    if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2233	return(NULL);
2234    }
2235
2236    while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2237            (c == '.') || (c == '-') ||
2238	    (c == '_') || (c == ':'))) {
2239	buf[len++] = c;
2240	cur++;
2241	c = *cur;
2242	if (len >= XML_MAX_NAMELEN)
2243	    return(NULL);
2244    }
2245    *name = xmlStrndup(buf, len);
2246    return(cur);
2247}
2248
2249/**
2250 * xmlGetSGMLCatalogEntryType:
2251 * @name:  the entry name
2252 *
2253 * Get the Catalog entry type for a given SGML Catalog name
2254 *
2255 * Returns Catalog entry type
2256 */
2257static xmlCatalogEntryType
2258xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2259    xmlCatalogEntryType type = XML_CATA_NONE;
2260    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2261	type = SGML_CATA_SYSTEM;
2262    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2263	type = SGML_CATA_PUBLIC;
2264    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2265	type = SGML_CATA_DELEGATE;
2266    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2267	type = SGML_CATA_ENTITY;
2268    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2269	type = SGML_CATA_DOCTYPE;
2270    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2271	type = SGML_CATA_LINKTYPE;
2272    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2273	type = SGML_CATA_NOTATION;
2274    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2275	type = SGML_CATA_SGMLDECL;
2276    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2277	type = SGML_CATA_DOCUMENT;
2278    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2279	type = SGML_CATA_CATALOG;
2280    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2281	type = SGML_CATA_BASE;
2282    return(type);
2283}
2284
2285/**
2286 * xmlParseSGMLCatalog:
2287 * @catal:  the SGML Catalog
2288 * @value:  the content of the SGML Catalog serialization
2289 * @file:  the filepath for the catalog
2290 * @super:  should this be handled as a Super Catalog in which case
2291 *          parsing is not recursive
2292 *
2293 * Parse an SGML catalog content and fill up the @catal hash table with
2294 * the new entries found.
2295 *
2296 * Returns 0 in case of success, -1 in case of error.
2297 */
2298static int
2299xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2300	            const char *file, int super) {
2301    const xmlChar *cur = value;
2302    xmlChar *base = NULL;
2303    int res;
2304
2305    if ((cur == NULL) || (file == NULL))
2306        return(-1);
2307    base = xmlStrdup((const xmlChar *) file);
2308
2309    while ((cur != NULL) && (cur[0] != 0)) {
2310	SKIP_BLANKS;
2311	if (cur[0] == 0)
2312	    break;
2313	if ((cur[0] == '-') && (cur[1] == '-')) {
2314	    cur = xmlParseSGMLCatalogComment(cur);
2315	    if (cur == NULL) {
2316		/* error */
2317		break;
2318	    }
2319	} else {
2320	    xmlChar *sysid = NULL;
2321	    xmlChar *name = NULL;
2322	    xmlCatalogEntryType type = XML_CATA_NONE;
2323
2324	    cur = xmlParseSGMLCatalogName(cur, &name);
2325	    if (name == NULL) {
2326		/* error */
2327		break;
2328	    }
2329	    if (!IS_BLANK_CH(*cur)) {
2330		/* error */
2331		break;
2332	    }
2333	    SKIP_BLANKS;
2334	    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2335                type = SGML_CATA_SYSTEM;
2336	    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2337                type = SGML_CATA_PUBLIC;
2338	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2339                type = SGML_CATA_DELEGATE;
2340	    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2341                type = SGML_CATA_ENTITY;
2342	    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2343                type = SGML_CATA_DOCTYPE;
2344	    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2345                type = SGML_CATA_LINKTYPE;
2346	    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2347                type = SGML_CATA_NOTATION;
2348	    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2349                type = SGML_CATA_SGMLDECL;
2350	    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2351                type = SGML_CATA_DOCUMENT;
2352	    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2353                type = SGML_CATA_CATALOG;
2354	    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2355                type = SGML_CATA_BASE;
2356	    else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2357		xmlFree(name);
2358		cur = xmlParseSGMLCatalogName(cur, &name);
2359		if (name == NULL) {
2360		    /* error */
2361		    break;
2362		}
2363		xmlFree(name);
2364		continue;
2365	    }
2366	    xmlFree(name);
2367	    name = NULL;
2368
2369	    switch(type) {
2370		case SGML_CATA_ENTITY:
2371		    if (*cur == '%')
2372			type = SGML_CATA_PENTITY;
2373		case SGML_CATA_PENTITY:
2374		case SGML_CATA_DOCTYPE:
2375		case SGML_CATA_LINKTYPE:
2376		case SGML_CATA_NOTATION:
2377		    cur = xmlParseSGMLCatalogName(cur, &name);
2378		    if (cur == NULL) {
2379			/* error */
2380			break;
2381		    }
2382		    if (!IS_BLANK_CH(*cur)) {
2383			/* error */
2384			break;
2385		    }
2386		    SKIP_BLANKS;
2387		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2388		    if (cur == NULL) {
2389			/* error */
2390			break;
2391		    }
2392		    break;
2393		case SGML_CATA_PUBLIC:
2394		case SGML_CATA_SYSTEM:
2395		case SGML_CATA_DELEGATE:
2396		    cur = xmlParseSGMLCatalogPubid(cur, &name);
2397		    if (cur == NULL) {
2398			/* error */
2399			break;
2400		    }
2401		    if (type != SGML_CATA_SYSTEM) {
2402		        xmlChar *normid;
2403
2404		        normid = xmlCatalogNormalizePublic(name);
2405		        if (normid != NULL) {
2406		            if (name != NULL)
2407		                xmlFree(name);
2408		            if (*normid != 0)
2409		                name = normid;
2410		            else {
2411		                xmlFree(normid);
2412		                name = NULL;
2413		            }
2414		        }
2415		    }
2416		    if (!IS_BLANK_CH(*cur)) {
2417			/* error */
2418			break;
2419		    }
2420		    SKIP_BLANKS;
2421		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2422		    if (cur == NULL) {
2423			/* error */
2424			break;
2425		    }
2426		    break;
2427		case SGML_CATA_BASE:
2428		case SGML_CATA_CATALOG:
2429		case SGML_CATA_DOCUMENT:
2430		case SGML_CATA_SGMLDECL:
2431		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2432		    if (cur == NULL) {
2433			/* error */
2434			break;
2435		    }
2436		    break;
2437		default:
2438		    break;
2439	    }
2440	    if (cur == NULL) {
2441		if (name != NULL)
2442		    xmlFree(name);
2443		if (sysid != NULL)
2444		    xmlFree(sysid);
2445		break;
2446	    } else if (type == SGML_CATA_BASE) {
2447		if (base != NULL)
2448		    xmlFree(base);
2449		base = xmlStrdup(sysid);
2450	    } else if ((type == SGML_CATA_PUBLIC) ||
2451		       (type == SGML_CATA_SYSTEM)) {
2452		xmlChar *filename;
2453
2454		filename = xmlBuildURI(sysid, base);
2455		if (filename != NULL) {
2456		    xmlCatalogEntryPtr entry;
2457
2458		    entry = xmlNewCatalogEntry(type, name, filename,
2459			                       NULL, XML_CATA_PREFER_NONE, NULL);
2460		    res = xmlHashAddEntry(catal->sgml, name, entry);
2461		    if (res < 0) {
2462			xmlFreeCatalogEntry(entry);
2463		    }
2464		    xmlFree(filename);
2465		}
2466
2467	    } else if (type == SGML_CATA_CATALOG) {
2468		if (super) {
2469		    xmlCatalogEntryPtr entry;
2470
2471		    entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2472			                       XML_CATA_PREFER_NONE, NULL);
2473		    res = xmlHashAddEntry(catal->sgml, sysid, entry);
2474		    if (res < 0) {
2475			xmlFreeCatalogEntry(entry);
2476		    }
2477		} else {
2478		    xmlChar *filename;
2479
2480		    filename = xmlBuildURI(sysid, base);
2481		    if (filename != NULL) {
2482			xmlExpandCatalog(catal, (const char *)filename);
2483			xmlFree(filename);
2484		    }
2485		}
2486	    }
2487	    /*
2488	     * drop anything else we won't handle it
2489	     */
2490	    if (name != NULL)
2491		xmlFree(name);
2492	    if (sysid != NULL)
2493		xmlFree(sysid);
2494	}
2495    }
2496    if (base != NULL)
2497	xmlFree(base);
2498    if (cur == NULL)
2499	return(-1);
2500    return(0);
2501}
2502
2503/************************************************************************
2504 *									*
2505 *			SGML Catalog handling				*
2506 *									*
2507 ************************************************************************/
2508
2509/**
2510 * xmlCatalogGetSGMLPublic:
2511 * @catal:  an SGML catalog hash
2512 * @pubID:  the public ID string
2513 *
2514 * Try to lookup the catalog local reference associated to a public ID
2515 *
2516 * Returns the local resource if found or NULL otherwise.
2517 */
2518static const xmlChar *
2519xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2520    xmlCatalogEntryPtr entry;
2521    xmlChar *normid;
2522
2523    if (catal == NULL)
2524	return(NULL);
2525
2526    normid = xmlCatalogNormalizePublic(pubID);
2527    if (normid != NULL)
2528        pubID = (*normid != 0 ? normid : NULL);
2529
2530    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2531    if (entry == NULL) {
2532	if (normid != NULL)
2533	    xmlFree(normid);
2534	return(NULL);
2535    }
2536    if (entry->type == SGML_CATA_PUBLIC) {
2537	if (normid != NULL)
2538	    xmlFree(normid);
2539	return(entry->URL);
2540    }
2541    if (normid != NULL)
2542        xmlFree(normid);
2543    return(NULL);
2544}
2545
2546/**
2547 * xmlCatalogGetSGMLSystem:
2548 * @catal:  an SGML catalog hash
2549 * @sysID:  the system ID string
2550 *
2551 * Try to lookup the catalog local reference for a system ID
2552 *
2553 * Returns the local resource if found or NULL otherwise.
2554 */
2555static const xmlChar *
2556xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2557    xmlCatalogEntryPtr entry;
2558
2559    if (catal == NULL)
2560	return(NULL);
2561
2562    entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2563    if (entry == NULL)
2564	return(NULL);
2565    if (entry->type == SGML_CATA_SYSTEM)
2566	return(entry->URL);
2567    return(NULL);
2568}
2569
2570/**
2571 * xmlCatalogSGMLResolve:
2572 * @catal:  the SGML catalog
2573 * @pubID:  the public ID string
2574 * @sysID:  the system ID string
2575 *
2576 * Do a complete resolution lookup of an External Identifier
2577 *
2578 * Returns the URI of the resource or NULL if not found
2579 */
2580static const xmlChar *
2581xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2582	              const xmlChar *sysID) {
2583    const xmlChar *ret = NULL;
2584
2585    if (catal->sgml == NULL)
2586	return(NULL);
2587
2588    if (pubID != NULL)
2589	ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2590    if (ret != NULL)
2591	return(ret);
2592    if (sysID != NULL)
2593	ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2594    return(NULL);
2595}
2596
2597/************************************************************************
2598 *									*
2599 *			Specific Public interfaces			*
2600 *									*
2601 ************************************************************************/
2602
2603/**
2604 * xmlLoadSGMLSuperCatalog:
2605 * @filename:  a file path
2606 *
2607 * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2608 * references. This is only needed for manipulating SGML Super Catalogs
2609 * like adding and removing CATALOG or DELEGATE entries.
2610 *
2611 * Returns the catalog parsed or NULL in case of error
2612 */
2613xmlCatalogPtr
2614xmlLoadSGMLSuperCatalog(const char *filename)
2615{
2616    xmlChar *content;
2617    xmlCatalogPtr catal;
2618    int ret;
2619
2620    content = xmlLoadFileContent(filename);
2621    if (content == NULL)
2622        return(NULL);
2623
2624    catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2625    if (catal == NULL) {
2626	xmlFree(content);
2627	return(NULL);
2628    }
2629
2630    ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2631    xmlFree(content);
2632    if (ret < 0) {
2633	xmlFreeCatalog(catal);
2634	return(NULL);
2635    }
2636    return (catal);
2637}
2638
2639/**
2640 * xmlLoadACatalog:
2641 * @filename:  a file path
2642 *
2643 * Load the catalog and build the associated data structures.
2644 * This can be either an XML Catalog or an SGML Catalog
2645 * It will recurse in SGML CATALOG entries. On the other hand XML
2646 * Catalogs are not handled recursively.
2647 *
2648 * Returns the catalog parsed or NULL in case of error
2649 */
2650xmlCatalogPtr
2651xmlLoadACatalog(const char *filename)
2652{
2653    xmlChar *content;
2654    xmlChar *first;
2655    xmlCatalogPtr catal;
2656    int ret;
2657
2658    content = xmlLoadFileContent(filename);
2659    if (content == NULL)
2660        return(NULL);
2661
2662
2663    first = content;
2664
2665    while ((*first != 0) && (*first != '-') && (*first != '<') &&
2666	   (!(((*first >= 'A') && (*first <= 'Z')) ||
2667	      ((*first >= 'a') && (*first <= 'z')))))
2668	first++;
2669
2670    if (*first != '<') {
2671	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2672	if (catal == NULL) {
2673	    xmlFree(content);
2674	    return(NULL);
2675	}
2676        ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2677	if (ret < 0) {
2678	    xmlFreeCatalog(catal);
2679	    xmlFree(content);
2680	    return(NULL);
2681	}
2682    } else {
2683	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2684	if (catal == NULL) {
2685	    xmlFree(content);
2686	    return(NULL);
2687	}
2688        catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2689		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2690    }
2691    xmlFree(content);
2692    return (catal);
2693}
2694
2695/**
2696 * xmlExpandCatalog:
2697 * @catal:  a catalog
2698 * @filename:  a file path
2699 *
2700 * Load the catalog and expand the existing catal structure.
2701 * This can be either an XML Catalog or an SGML Catalog
2702 *
2703 * Returns 0 in case of success, -1 in case of error
2704 */
2705static int
2706xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2707{
2708    int ret;
2709
2710    if ((catal == NULL) || (filename == NULL))
2711	return(-1);
2712
2713
2714    if (catal->type == XML_SGML_CATALOG_TYPE) {
2715	xmlChar *content;
2716
2717	content = xmlLoadFileContent(filename);
2718	if (content == NULL)
2719	    return(-1);
2720
2721        ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2722	if (ret < 0) {
2723	    xmlFree(content);
2724	    return(-1);
2725	}
2726	xmlFree(content);
2727    } else {
2728	xmlCatalogEntryPtr tmp, cur;
2729	tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2730		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2731
2732	cur = catal->xml;
2733	if (cur == NULL) {
2734	    catal->xml = tmp;
2735	} else {
2736	    while (cur->next != NULL) cur = cur->next;
2737	    cur->next = tmp;
2738	}
2739    }
2740    return (0);
2741}
2742
2743/**
2744 * xmlACatalogResolveSystem:
2745 * @catal:  a Catalog
2746 * @sysID:  the system ID string
2747 *
2748 * Try to lookup the catalog resource for a system ID
2749 *
2750 * Returns the resource if found or NULL otherwise, the value returned
2751 *      must be freed by the caller.
2752 */
2753xmlChar *
2754xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2755    xmlChar *ret = NULL;
2756
2757    if ((sysID == NULL) || (catal == NULL))
2758	return(NULL);
2759
2760    if (xmlDebugCatalogs)
2761	xmlGenericError(xmlGenericErrorContext,
2762		"Resolve sysID %s\n", sysID);
2763
2764    if (catal->type == XML_XML_CATALOG_TYPE) {
2765	ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2766	if (ret == XML_CATAL_BREAK)
2767	    ret = NULL;
2768    } else {
2769	const xmlChar *sgml;
2770
2771	sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2772	if (sgml != NULL)
2773	    ret = xmlStrdup(sgml);
2774    }
2775    return(ret);
2776}
2777
2778/**
2779 * xmlACatalogResolvePublic:
2780 * @catal:  a Catalog
2781 * @pubID:  the public ID string
2782 *
2783 * Try to lookup the catalog local reference associated to a public ID in that catalog
2784 *
2785 * Returns the local resource if found or NULL otherwise, the value returned
2786 *      must be freed by the caller.
2787 */
2788xmlChar *
2789xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2790    xmlChar *ret = NULL;
2791
2792    if ((pubID == NULL) || (catal == NULL))
2793	return(NULL);
2794
2795    if (xmlDebugCatalogs)
2796	xmlGenericError(xmlGenericErrorContext,
2797		"Resolve pubID %s\n", pubID);
2798
2799    if (catal->type == XML_XML_CATALOG_TYPE) {
2800	ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2801	if (ret == XML_CATAL_BREAK)
2802	    ret = NULL;
2803    } else {
2804	const xmlChar *sgml;
2805
2806	sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2807	if (sgml != NULL)
2808	    ret = xmlStrdup(sgml);
2809    }
2810    return(ret);
2811}
2812
2813/**
2814 * xmlACatalogResolve:
2815 * @catal:  a Catalog
2816 * @pubID:  the public ID string
2817 * @sysID:  the system ID string
2818 *
2819 * Do a complete resolution lookup of an External Identifier
2820 *
2821 * Returns the URI of the resource or NULL if not found, it must be freed
2822 *      by the caller.
2823 */
2824xmlChar *
2825xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2826                   const xmlChar * sysID)
2827{
2828    xmlChar *ret = NULL;
2829
2830    if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2831        return (NULL);
2832
2833    if (xmlDebugCatalogs) {
2834         if ((pubID != NULL) && (sysID != NULL)) {
2835             xmlGenericError(xmlGenericErrorContext,
2836                             "Resolve: pubID %s sysID %s\n", pubID, sysID);
2837         } else if (pubID != NULL) {
2838             xmlGenericError(xmlGenericErrorContext,
2839                             "Resolve: pubID %s\n", pubID);
2840         } else {
2841             xmlGenericError(xmlGenericErrorContext,
2842                             "Resolve: sysID %s\n", sysID);
2843         }
2844    }
2845
2846    if (catal->type == XML_XML_CATALOG_TYPE) {
2847        ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2848	if (ret == XML_CATAL_BREAK)
2849	    ret = NULL;
2850    } else {
2851        const xmlChar *sgml;
2852
2853        sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2854        if (sgml != NULL)
2855            ret = xmlStrdup(sgml);
2856    }
2857    return (ret);
2858}
2859
2860/**
2861 * xmlACatalogResolveURI:
2862 * @catal:  a Catalog
2863 * @URI:  the URI
2864 *
2865 * Do a complete resolution lookup of an URI
2866 *
2867 * Returns the URI of the resource or NULL if not found, it must be freed
2868 *      by the caller.
2869 */
2870xmlChar *
2871xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2872    xmlChar *ret = NULL;
2873
2874    if ((URI == NULL) || (catal == NULL))
2875	return(NULL);
2876
2877    if (xmlDebugCatalogs)
2878	xmlGenericError(xmlGenericErrorContext,
2879		"Resolve URI %s\n", URI);
2880
2881    if (catal->type == XML_XML_CATALOG_TYPE) {
2882	ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2883	if (ret == XML_CATAL_BREAK)
2884	    ret = NULL;
2885    } else {
2886	const xmlChar *sgml;
2887
2888	sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2889	if (sgml != NULL)
2890            sgml = xmlStrdup(sgml);
2891    }
2892    return(ret);
2893}
2894
2895#ifdef LIBXML_OUTPUT_ENABLED
2896/**
2897 * xmlACatalogDump:
2898 * @catal:  a Catalog
2899 * @out:  the file.
2900 *
2901 * Dump the given catalog to the given file.
2902 */
2903void
2904xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2905    if ((out == NULL) || (catal == NULL))
2906	return;
2907
2908    if (catal->type == XML_XML_CATALOG_TYPE) {
2909	xmlDumpXMLCatalog(out, catal->xml);
2910    } else {
2911	xmlHashScan(catal->sgml,
2912		    (xmlHashScanner) xmlCatalogDumpEntry, out);
2913    }
2914}
2915#endif /* LIBXML_OUTPUT_ENABLED */
2916
2917/**
2918 * xmlACatalogAdd:
2919 * @catal:  a Catalog
2920 * @type:  the type of record to add to the catalog
2921 * @orig:  the system, public or prefix to match
2922 * @replace:  the replacement value for the match
2923 *
2924 * Add an entry in the catalog, it may overwrite existing but
2925 * different entries.
2926 *
2927 * Returns 0 if successful, -1 otherwise
2928 */
2929int
2930xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2931              const xmlChar * orig, const xmlChar * replace)
2932{
2933    int res = -1;
2934
2935    if (catal == NULL)
2936	return(-1);
2937
2938    if (catal->type == XML_XML_CATALOG_TYPE) {
2939        res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2940    } else {
2941        xmlCatalogEntryType cattype;
2942
2943        cattype = xmlGetSGMLCatalogEntryType(type);
2944        if (cattype != XML_CATA_NONE) {
2945            xmlCatalogEntryPtr entry;
2946
2947            entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2948                                       XML_CATA_PREFER_NONE, NULL);
2949	    if (catal->sgml == NULL)
2950		catal->sgml = xmlHashCreate(10);
2951            res = xmlHashAddEntry(catal->sgml, orig, entry);
2952        }
2953    }
2954    return (res);
2955}
2956
2957/**
2958 * xmlACatalogRemove:
2959 * @catal:  a Catalog
2960 * @value:  the value to remove
2961 *
2962 * Remove an entry from the catalog
2963 *
2964 * Returns the number of entries removed if successful, -1 otherwise
2965 */
2966int
2967xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2968    int res = -1;
2969
2970    if ((catal == NULL) || (value == NULL))
2971	return(-1);
2972
2973    if (catal->type == XML_XML_CATALOG_TYPE) {
2974	res = xmlDelXMLCatalog(catal->xml, value);
2975    } else {
2976	res = xmlHashRemoveEntry(catal->sgml, value,
2977		(xmlHashDeallocator) xmlFreeCatalogEntry);
2978	if (res == 0)
2979	    res = 1;
2980    }
2981    return(res);
2982}
2983
2984/**
2985 * xmlNewCatalog:
2986 * @sgml:  should this create an SGML catalog
2987 *
2988 * create a new Catalog.
2989 *
2990 * Returns the xmlCatalogPtr or NULL in case of error
2991 */
2992xmlCatalogPtr
2993xmlNewCatalog(int sgml) {
2994    xmlCatalogPtr catal = NULL;
2995
2996    if (sgml) {
2997	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
2998		                    xmlCatalogDefaultPrefer);
2999        if ((catal != NULL) && (catal->sgml == NULL))
3000	    catal->sgml = xmlHashCreate(10);
3001    } else
3002	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3003		                    xmlCatalogDefaultPrefer);
3004    return(catal);
3005}
3006
3007/**
3008 * xmlCatalogIsEmpty:
3009 * @catal:  should this create an SGML catalog
3010 *
3011 * Check is a catalog is empty
3012 *
3013 * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
3014 */
3015int
3016xmlCatalogIsEmpty(xmlCatalogPtr catal) {
3017    if (catal == NULL)
3018	return(-1);
3019
3020    if (catal->type == XML_XML_CATALOG_TYPE) {
3021	if (catal->xml == NULL)
3022	    return(1);
3023	if ((catal->xml->type != XML_CATA_CATALOG) &&
3024	    (catal->xml->type != XML_CATA_BROKEN_CATALOG))
3025	    return(-1);
3026	if (catal->xml->children == NULL)
3027	    return(1);
3028        return(0);
3029    } else {
3030	int res;
3031
3032	if (catal->sgml == NULL)
3033	    return(1);
3034	res = xmlHashSize(catal->sgml);
3035	if (res == 0)
3036	    return(1);
3037	if (res < 0)
3038	    return(-1);
3039    }
3040    return(0);
3041}
3042
3043/************************************************************************
3044 *									*
3045 *   Public interfaces manipulating the global shared default catalog	*
3046 *									*
3047 ************************************************************************/
3048
3049/**
3050 * xmlInitializeCatalogData:
3051 *
3052 * Do the catalog initialization only of global data, doesn't try to load
3053 * any catalog actually.
3054 * this function is not thread safe, catalog initialization should
3055 * preferably be done once at startup
3056 */
3057static void
3058xmlInitializeCatalogData(void) {
3059    if (xmlCatalogInitialized != 0)
3060	return;
3061
3062    if (getenv("XML_DEBUG_CATALOG"))
3063	xmlDebugCatalogs = 1;
3064    xmlCatalogMutex = xmlNewRMutex();
3065
3066    xmlCatalogInitialized = 1;
3067}
3068/**
3069 * xmlInitializeCatalog:
3070 *
3071 * Do the catalog initialization.
3072 * this function is not thread safe, catalog initialization should
3073 * preferably be done once at startup
3074 */
3075void
3076xmlInitializeCatalog(void) {
3077    if (xmlCatalogInitialized != 0)
3078	return;
3079
3080    xmlInitializeCatalogData();
3081    xmlRMutexLock(xmlCatalogMutex);
3082
3083    if (getenv("XML_DEBUG_CATALOG"))
3084	xmlDebugCatalogs = 1;
3085
3086    if (xmlDefaultCatalog == NULL) {
3087	const char *catalogs;
3088	char *path;
3089	const char *cur, *paths;
3090	xmlCatalogPtr catal;
3091	xmlCatalogEntryPtr *nextent;
3092
3093	catalogs = (const char *) getenv("XML_CATALOG_FILES");
3094	if (catalogs == NULL)
3095#if defined(_WIN32) && defined(_MSC_VER)
3096    {
3097		void* hmodule;
3098		hmodule = GetModuleHandleA("libxml2.dll");
3099		if (hmodule == NULL)
3100			hmodule = GetModuleHandleA(NULL);
3101		if (hmodule != NULL) {
3102			char buf[256];
3103			unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
3104			if (len != 0) {
3105				char* p = &(buf[len]);
3106				while (*p != '\\' && p > buf)
3107					p--;
3108				if (p != buf) {
3109					xmlChar* uri;
3110					strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
3111					uri = xmlCanonicPath(buf);
3112					if (uri != NULL) {
3113						strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
3114						xmlFree(uri);
3115					}
3116				}
3117			}
3118		}
3119		catalogs = XML_XML_DEFAULT_CATALOG;
3120    }
3121#else
3122	    catalogs = XML_XML_DEFAULT_CATALOG;
3123#endif
3124
3125	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3126		xmlCatalogDefaultPrefer);
3127	if (catal != NULL) {
3128	    /* the XML_CATALOG_FILES envvar is allowed to contain a
3129	       space-separated list of entries. */
3130	    cur = catalogs;
3131	    nextent = &catal->xml;
3132	    while (*cur != '\0') {
3133		while (xmlIsBlank_ch(*cur))
3134		    cur++;
3135		if (*cur != 0) {
3136		    paths = cur;
3137		    while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
3138			cur++;
3139		    path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
3140		    if (path != NULL) {
3141			*nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3142				NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
3143			if (*nextent != NULL)
3144			    nextent = &((*nextent)->next);
3145			xmlFree(path);
3146		    }
3147		}
3148	    }
3149	    xmlDefaultCatalog = catal;
3150	}
3151    }
3152
3153    xmlRMutexUnlock(xmlCatalogMutex);
3154}
3155
3156
3157/**
3158 * xmlLoadCatalog:
3159 * @filename:  a file path
3160 *
3161 * Load the catalog and makes its definitions effective for the default
3162 * external entity loader. It will recurse in SGML CATALOG entries.
3163 * this function is not thread safe, catalog initialization should
3164 * preferably be done once at startup
3165 *
3166 * Returns 0 in case of success -1 in case of error
3167 */
3168int
3169xmlLoadCatalog(const char *filename)
3170{
3171    int ret;
3172    xmlCatalogPtr catal;
3173
3174    if (!xmlCatalogInitialized)
3175	xmlInitializeCatalogData();
3176
3177    xmlRMutexLock(xmlCatalogMutex);
3178
3179    if (xmlDefaultCatalog == NULL) {
3180	catal = xmlLoadACatalog(filename);
3181	if (catal == NULL) {
3182	    xmlRMutexUnlock(xmlCatalogMutex);
3183	    return(-1);
3184	}
3185
3186	xmlDefaultCatalog = catal;
3187	xmlRMutexUnlock(xmlCatalogMutex);
3188	return(0);
3189    }
3190
3191    ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
3192    xmlRMutexUnlock(xmlCatalogMutex);
3193    return(ret);
3194}
3195
3196/**
3197 * xmlLoadCatalogs:
3198 * @pathss:  a list of directories separated by a colon or a space.
3199 *
3200 * Load the catalogs and makes their definitions effective for the default
3201 * external entity loader.
3202 * this function is not thread safe, catalog initialization should
3203 * preferably be done once at startup
3204 */
3205void
3206xmlLoadCatalogs(const char *pathss) {
3207    const char *cur;
3208    const char *paths;
3209    xmlChar *path;
3210
3211    if (pathss == NULL)
3212	return;
3213
3214    cur = pathss;
3215    while ((cur != NULL) && (*cur != 0)) {
3216	while (xmlIsBlank_ch(*cur)) cur++;
3217	if (*cur != 0) {
3218	    paths = cur;
3219	    while ((*cur != 0) && (*cur != ':') && (!xmlIsBlank_ch(*cur)))
3220		cur++;
3221	    path = xmlStrndup((const xmlChar *)paths, cur - paths);
3222	    if (path != NULL) {
3223		xmlLoadCatalog((const char *) path);
3224		xmlFree(path);
3225	    }
3226	}
3227	while (*cur == ':')
3228	    cur++;
3229    }
3230}
3231
3232/**
3233 * xmlCatalogCleanup:
3234 *
3235 * Free up all the memory associated with catalogs
3236 */
3237void
3238xmlCatalogCleanup(void) {
3239    if (xmlCatalogInitialized == 0)
3240        return;
3241
3242    xmlRMutexLock(xmlCatalogMutex);
3243    if (xmlDebugCatalogs)
3244	xmlGenericError(xmlGenericErrorContext,
3245		"Catalogs cleanup\n");
3246    if (xmlCatalogXMLFiles != NULL)
3247	xmlHashFree(xmlCatalogXMLFiles,
3248		    (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
3249    xmlCatalogXMLFiles = NULL;
3250    if (xmlDefaultCatalog != NULL)
3251	xmlFreeCatalog(xmlDefaultCatalog);
3252    xmlDefaultCatalog = NULL;
3253    xmlDebugCatalogs = 0;
3254    xmlCatalogInitialized = 0;
3255    xmlRMutexUnlock(xmlCatalogMutex);
3256    xmlFreeRMutex(xmlCatalogMutex);
3257}
3258
3259/**
3260 * xmlCatalogResolveSystem:
3261 * @sysID:  the system ID string
3262 *
3263 * Try to lookup the catalog resource for a system ID
3264 *
3265 * Returns the resource if found or NULL otherwise, the value returned
3266 *      must be freed by the caller.
3267 */
3268xmlChar *
3269xmlCatalogResolveSystem(const xmlChar *sysID) {
3270    xmlChar *ret;
3271
3272    if (!xmlCatalogInitialized)
3273	xmlInitializeCatalog();
3274
3275    ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3276    return(ret);
3277}
3278
3279/**
3280 * xmlCatalogResolvePublic:
3281 * @pubID:  the public ID string
3282 *
3283 * Try to lookup the catalog reference associated to a public ID
3284 *
3285 * Returns the resource if found or NULL otherwise, the value returned
3286 *      must be freed by the caller.
3287 */
3288xmlChar *
3289xmlCatalogResolvePublic(const xmlChar *pubID) {
3290    xmlChar *ret;
3291
3292    if (!xmlCatalogInitialized)
3293	xmlInitializeCatalog();
3294
3295    ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3296    return(ret);
3297}
3298
3299/**
3300 * xmlCatalogResolve:
3301 * @pubID:  the public ID string
3302 * @sysID:  the system ID string
3303 *
3304 * Do a complete resolution lookup of an External Identifier
3305 *
3306 * Returns the URI of the resource or NULL if not found, it must be freed
3307 *      by the caller.
3308 */
3309xmlChar *
3310xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3311    xmlChar *ret;
3312
3313    if (!xmlCatalogInitialized)
3314	xmlInitializeCatalog();
3315
3316    ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3317    return(ret);
3318}
3319
3320/**
3321 * xmlCatalogResolveURI:
3322 * @URI:  the URI
3323 *
3324 * Do a complete resolution lookup of an URI
3325 *
3326 * Returns the URI of the resource or NULL if not found, it must be freed
3327 *      by the caller.
3328 */
3329xmlChar *
3330xmlCatalogResolveURI(const xmlChar *URI) {
3331    xmlChar *ret;
3332
3333    if (!xmlCatalogInitialized)
3334	xmlInitializeCatalog();
3335
3336    ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3337    return(ret);
3338}
3339
3340#ifdef LIBXML_OUTPUT_ENABLED
3341/**
3342 * xmlCatalogDump:
3343 * @out:  the file.
3344 *
3345 * Dump all the global catalog content to the given file.
3346 */
3347void
3348xmlCatalogDump(FILE *out) {
3349    if (out == NULL)
3350	return;
3351
3352    if (!xmlCatalogInitialized)
3353	xmlInitializeCatalog();
3354
3355    xmlACatalogDump(xmlDefaultCatalog, out);
3356}
3357#endif /* LIBXML_OUTPUT_ENABLED */
3358
3359/**
3360 * xmlCatalogAdd:
3361 * @type:  the type of record to add to the catalog
3362 * @orig:  the system, public or prefix to match
3363 * @replace:  the replacement value for the match
3364 *
3365 * Add an entry in the catalog, it may overwrite existing but
3366 * different entries.
3367 * If called before any other catalog routine, allows to override the
3368 * default shared catalog put in place by xmlInitializeCatalog();
3369 *
3370 * Returns 0 if successful, -1 otherwise
3371 */
3372int
3373xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3374    int res = -1;
3375
3376    if (!xmlCatalogInitialized)
3377	xmlInitializeCatalogData();
3378
3379    xmlRMutexLock(xmlCatalogMutex);
3380    /*
3381     * Specific case where one want to override the default catalog
3382     * put in place by xmlInitializeCatalog();
3383     */
3384    if ((xmlDefaultCatalog == NULL) &&
3385	(xmlStrEqual(type, BAD_CAST "catalog"))) {
3386	xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3387		                          xmlCatalogDefaultPrefer);
3388	xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3389				    orig, NULL,  xmlCatalogDefaultPrefer, NULL);
3390
3391	xmlRMutexUnlock(xmlCatalogMutex);
3392	return(0);
3393    }
3394
3395    res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3396    xmlRMutexUnlock(xmlCatalogMutex);
3397    return(res);
3398}
3399
3400/**
3401 * xmlCatalogRemove:
3402 * @value:  the value to remove
3403 *
3404 * Remove an entry from the catalog
3405 *
3406 * Returns the number of entries removed if successful, -1 otherwise
3407 */
3408int
3409xmlCatalogRemove(const xmlChar *value) {
3410    int res;
3411
3412    if (!xmlCatalogInitialized)
3413	xmlInitializeCatalog();
3414
3415    xmlRMutexLock(xmlCatalogMutex);
3416    res = xmlACatalogRemove(xmlDefaultCatalog, value);
3417    xmlRMutexUnlock(xmlCatalogMutex);
3418    return(res);
3419}
3420
3421/**
3422 * xmlCatalogConvert:
3423 *
3424 * Convert all the SGML catalog entries as XML ones
3425 *
3426 * Returns the number of entries converted if successful, -1 otherwise
3427 */
3428int
3429xmlCatalogConvert(void) {
3430    int res = -1;
3431
3432    if (!xmlCatalogInitialized)
3433	xmlInitializeCatalog();
3434
3435    xmlRMutexLock(xmlCatalogMutex);
3436    res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3437    xmlRMutexUnlock(xmlCatalogMutex);
3438    return(res);
3439}
3440
3441/************************************************************************
3442 *									*
3443 *	Public interface manipulating the common preferences		*
3444 *									*
3445 ************************************************************************/
3446
3447/**
3448 * xmlCatalogGetDefaults:
3449 *
3450 * Used to get the user preference w.r.t. to what catalogs should
3451 * be accepted
3452 *
3453 * Returns the current xmlCatalogAllow value
3454 */
3455xmlCatalogAllow
3456xmlCatalogGetDefaults(void) {
3457    return(xmlCatalogDefaultAllow);
3458}
3459
3460/**
3461 * xmlCatalogSetDefaults:
3462 * @allow:  what catalogs should be accepted
3463 *
3464 * Used to set the user preference w.r.t. to what catalogs should
3465 * be accepted
3466 */
3467void
3468xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3469    if (xmlDebugCatalogs) {
3470	switch (allow) {
3471	    case XML_CATA_ALLOW_NONE:
3472		xmlGenericError(xmlGenericErrorContext,
3473			"Disabling catalog usage\n");
3474		break;
3475	    case XML_CATA_ALLOW_GLOBAL:
3476		xmlGenericError(xmlGenericErrorContext,
3477			"Allowing only global catalogs\n");
3478		break;
3479	    case XML_CATA_ALLOW_DOCUMENT:
3480		xmlGenericError(xmlGenericErrorContext,
3481			"Allowing only catalogs from the document\n");
3482		break;
3483	    case XML_CATA_ALLOW_ALL:
3484		xmlGenericError(xmlGenericErrorContext,
3485			"Allowing all catalogs\n");
3486		break;
3487	}
3488    }
3489    xmlCatalogDefaultAllow = allow;
3490}
3491
3492/**
3493 * xmlCatalogSetDefaultPrefer:
3494 * @prefer:  the default preference for delegation
3495 *
3496 * Allows to set the preference between public and system for deletion
3497 * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3498 * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3499 *
3500 * Returns the previous value of the default preference for delegation
3501 */
3502xmlCatalogPrefer
3503xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3504    xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3505
3506    if (prefer == XML_CATA_PREFER_NONE)
3507	return(ret);
3508
3509    if (xmlDebugCatalogs) {
3510	switch (prefer) {
3511	    case XML_CATA_PREFER_PUBLIC:
3512		xmlGenericError(xmlGenericErrorContext,
3513			"Setting catalog preference to PUBLIC\n");
3514		break;
3515	    case XML_CATA_PREFER_SYSTEM:
3516		xmlGenericError(xmlGenericErrorContext,
3517			"Setting catalog preference to SYSTEM\n");
3518		break;
3519	    case XML_CATA_PREFER_NONE:
3520		break;
3521	}
3522    }
3523    xmlCatalogDefaultPrefer = prefer;
3524    return(ret);
3525}
3526
3527/**
3528 * xmlCatalogSetDebug:
3529 * @level:  the debug level of catalogs required
3530 *
3531 * Used to set the debug level for catalog operation, 0 disable
3532 * debugging, 1 enable it
3533 *
3534 * Returns the previous value of the catalog debugging level
3535 */
3536int
3537xmlCatalogSetDebug(int level) {
3538    int ret = xmlDebugCatalogs;
3539
3540    if (level <= 0)
3541        xmlDebugCatalogs = 0;
3542    else
3543	xmlDebugCatalogs = level;
3544    return(ret);
3545}
3546
3547/************************************************************************
3548 *									*
3549 *   Minimal interfaces used for per-document catalogs by the parser	*
3550 *									*
3551 ************************************************************************/
3552
3553/**
3554 * xmlCatalogFreeLocal:
3555 * @catalogs:  a document's list of catalogs
3556 *
3557 * Free up the memory associated to the catalog list
3558 */
3559void
3560xmlCatalogFreeLocal(void *catalogs) {
3561    xmlCatalogEntryPtr catal;
3562
3563    if (!xmlCatalogInitialized)
3564	xmlInitializeCatalog();
3565
3566    catal = (xmlCatalogEntryPtr) catalogs;
3567    if (catal != NULL)
3568	xmlFreeCatalogEntryList(catal);
3569}
3570
3571
3572/**
3573 * xmlCatalogAddLocal:
3574 * @catalogs:  a document's list of catalogs
3575 * @URL:  the URL to a new local catalog
3576 *
3577 * Add the new entry to the catalog list
3578 *
3579 * Returns the updated list
3580 */
3581void *
3582xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3583    xmlCatalogEntryPtr catal, add;
3584
3585    if (!xmlCatalogInitialized)
3586	xmlInitializeCatalog();
3587
3588    if (URL == NULL)
3589	return(catalogs);
3590
3591    if (xmlDebugCatalogs)
3592	xmlGenericError(xmlGenericErrorContext,
3593		"Adding document catalog %s\n", URL);
3594
3595    add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3596	                     xmlCatalogDefaultPrefer, NULL);
3597    if (add == NULL)
3598	return(catalogs);
3599
3600    catal = (xmlCatalogEntryPtr) catalogs;
3601    if (catal == NULL)
3602	return((void *) add);
3603
3604    while (catal->next != NULL)
3605	catal = catal->next;
3606    catal->next = add;
3607    return(catalogs);
3608}
3609
3610/**
3611 * xmlCatalogLocalResolve:
3612 * @catalogs:  a document's list of catalogs
3613 * @pubID:  the public ID string
3614 * @sysID:  the system ID string
3615 *
3616 * Do a complete resolution lookup of an External Identifier using a
3617 * document's private catalog list
3618 *
3619 * Returns the URI of the resource or NULL if not found, it must be freed
3620 *      by the caller.
3621 */
3622xmlChar *
3623xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3624	               const xmlChar *sysID) {
3625    xmlCatalogEntryPtr catal;
3626    xmlChar *ret;
3627
3628    if (!xmlCatalogInitialized)
3629	xmlInitializeCatalog();
3630
3631    if ((pubID == NULL) && (sysID == NULL))
3632	return(NULL);
3633
3634    if (xmlDebugCatalogs) {
3635        if ((pubID != NULL) && (sysID != NULL)) {
3636            xmlGenericError(xmlGenericErrorContext,
3637                            "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
3638        } else if (pubID != NULL) {
3639            xmlGenericError(xmlGenericErrorContext,
3640                            "Local Resolve: pubID %s\n", pubID);
3641        } else {
3642            xmlGenericError(xmlGenericErrorContext,
3643                            "Local Resolve: sysID %s\n", sysID);
3644        }
3645    }
3646
3647    catal = (xmlCatalogEntryPtr) catalogs;
3648    if (catal == NULL)
3649	return(NULL);
3650    ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3651    if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3652	return(ret);
3653    return(NULL);
3654}
3655
3656/**
3657 * xmlCatalogLocalResolveURI:
3658 * @catalogs:  a document's list of catalogs
3659 * @URI:  the URI
3660 *
3661 * Do a complete resolution lookup of an URI using a
3662 * document's private catalog list
3663 *
3664 * Returns the URI of the resource or NULL if not found, it must be freed
3665 *      by the caller.
3666 */
3667xmlChar *
3668xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3669    xmlCatalogEntryPtr catal;
3670    xmlChar *ret;
3671
3672    if (!xmlCatalogInitialized)
3673	xmlInitializeCatalog();
3674
3675    if (URI == NULL)
3676	return(NULL);
3677
3678    if (xmlDebugCatalogs)
3679	xmlGenericError(xmlGenericErrorContext,
3680		"Resolve URI %s\n", URI);
3681
3682    catal = (xmlCatalogEntryPtr) catalogs;
3683    if (catal == NULL)
3684	return(NULL);
3685    ret = xmlCatalogListXMLResolveURI(catal, URI);
3686    if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3687	return(ret);
3688    return(NULL);
3689}
3690
3691/************************************************************************
3692 *									*
3693 *			Deprecated interfaces				*
3694 *									*
3695 ************************************************************************/
3696/**
3697 * xmlCatalogGetSystem:
3698 * @sysID:  the system ID string
3699 *
3700 * Try to lookup the catalog reference associated to a system ID
3701 * DEPRECATED, use xmlCatalogResolveSystem()
3702 *
3703 * Returns the resource if found or NULL otherwise.
3704 */
3705const xmlChar *
3706xmlCatalogGetSystem(const xmlChar *sysID) {
3707    xmlChar *ret;
3708    static xmlChar result[1000];
3709    static int msg = 0;
3710
3711    if (!xmlCatalogInitialized)
3712	xmlInitializeCatalog();
3713
3714    if (msg == 0) {
3715	xmlGenericError(xmlGenericErrorContext,
3716		"Use of deprecated xmlCatalogGetSystem() call\n");
3717	msg++;
3718    }
3719
3720    if (sysID == NULL)
3721	return(NULL);
3722
3723    /*
3724     * Check first the XML catalogs
3725     */
3726    if (xmlDefaultCatalog != NULL) {
3727	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3728	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3729	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3730	    result[sizeof(result) - 1] = 0;
3731	    return(result);
3732	}
3733    }
3734
3735    if (xmlDefaultCatalog != NULL)
3736	return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3737    return(NULL);
3738}
3739
3740/**
3741 * xmlCatalogGetPublic:
3742 * @pubID:  the public ID string
3743 *
3744 * Try to lookup the catalog reference associated to a public ID
3745 * DEPRECATED, use xmlCatalogResolvePublic()
3746 *
3747 * Returns the resource if found or NULL otherwise.
3748 */
3749const xmlChar *
3750xmlCatalogGetPublic(const xmlChar *pubID) {
3751    xmlChar *ret;
3752    static xmlChar result[1000];
3753    static int msg = 0;
3754
3755    if (!xmlCatalogInitialized)
3756	xmlInitializeCatalog();
3757
3758    if (msg == 0) {
3759	xmlGenericError(xmlGenericErrorContext,
3760		"Use of deprecated xmlCatalogGetPublic() call\n");
3761	msg++;
3762    }
3763
3764    if (pubID == NULL)
3765	return(NULL);
3766
3767    /*
3768     * Check first the XML catalogs
3769     */
3770    if (xmlDefaultCatalog != NULL) {
3771	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3772	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3773	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3774	    result[sizeof(result) - 1] = 0;
3775	    return(result);
3776	}
3777    }
3778
3779    if (xmlDefaultCatalog != NULL)
3780	return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3781    return(NULL);
3782}
3783
3784#endif /* LIBXML_CATALOG_ENABLED */
3785