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