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