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