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