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