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