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