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