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