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