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