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