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