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