fix perl
[platform/upstream/libxml2.git] / catalog.c
1 /**
2  * catalog.c: set of generic Catalog related routines 
3  *
4  * Reference:  SGML Open Technical Resolution TR9401:1997.
5  *             http://www.jclark.com/sp/catalog.htm
6  *
7  *             XML Catalogs Working Draft 06 August 2001
8  *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
9  *
10  * See Copyright for the status of this software.
11  *
12  * Daniel.Veillard@imag.fr
13  */
14
15 #define IN_LIBXML
16 #include "libxml.h"
17
18 #ifdef LIBXML_CATALOG_ENABLED
19 #ifdef HAVE_SYS_TYPES_H
20 #include <sys/types.h>
21 #endif
22 #ifdef HAVE_SYS_STAT_H
23 #include <sys/stat.h>
24 #endif
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #ifdef HAVE_FCNTL_H
29 #include <fcntl.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #include <string.h>
35 #include <libxml/xmlmemory.h>
36 #include <libxml/hash.h>
37 #include <libxml/uri.h>
38 #include <libxml/parserInternals.h>
39 #include <libxml/catalog.h>
40 #include <libxml/xmlerror.h>
41 #include <libxml/threads.h>
42 #include <libxml/globals.h>
43
44 #define MAX_DELEGATE    50
45 #define MAX_CATAL_DEPTH 50
46
47 #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     close(fd);
1001 #else
1002     len = fread(content, 1, size, fd);
1003     fclose(fd);
1004 #endif
1005     if (len < 0) {
1006         xmlFree(content);
1007         return (NULL);
1008     }
1009     content[len] = 0;
1010
1011     return(content);
1012 }
1013
1014 /**
1015  * xmlCatalogNormalizePublic:
1016  * @pubID:  the public ID string
1017  *
1018  *  Normalizes the Public Identifier
1019  *
1020  * Implements 6.2. Public Identifier Normalization
1021  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1022  *
1023  * Returns the new string or NULL, the string must be deallocated
1024  *         by the caller.
1025  */
1026 static xmlChar *
1027 xmlCatalogNormalizePublic(const xmlChar *pubID)
1028 {
1029     int ok = 1;
1030     int white;
1031     const xmlChar *p;
1032     xmlChar *ret;
1033     xmlChar *q;
1034
1035     if (pubID == NULL)
1036         return(NULL);
1037
1038     white = 1;
1039     for (p = pubID;*p != 0 && ok;p++) {
1040         if (!xmlIsBlank_ch(*p))
1041             white = 0;
1042         else if (*p == 0x20 && !white)
1043             white = 1;
1044         else
1045             ok = 0;
1046     }
1047     if (ok && !white)   /* is normalized */
1048         return(NULL);
1049
1050     ret = xmlStrdup(pubID);
1051     q = ret;
1052     white = 0;
1053     for (p = pubID;*p != 0;p++) {
1054         if (xmlIsBlank_ch(*p)) {
1055             if (q != ret)
1056                 white = 1;
1057         } else {
1058             if (white) {
1059                 *(q++) = 0x20;
1060                 white = 0;
1061             }
1062             *(q++) = *p;
1063         }
1064     }
1065     *q = 0;
1066     return(ret);
1067 }
1068
1069 /************************************************************************
1070  *                                                                      *
1071  *                      The XML Catalog parser                          *
1072  *                                                                      *
1073  ************************************************************************/
1074
1075 static xmlCatalogEntryPtr
1076 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
1077 static void
1078 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1079                            xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
1080 static xmlChar *
1081 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1082                       const xmlChar *sysID);
1083 static xmlChar *
1084 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
1085
1086
1087 /**
1088  * xmlGetXMLCatalogEntryType:
1089  * @name:  the name
1090  *
1091  * lookup the internal type associated to an XML catalog entry name
1092  *
1093  * Returns the type associated with that name
1094  */
1095 static xmlCatalogEntryType
1096 xmlGetXMLCatalogEntryType(const xmlChar *name) {
1097     xmlCatalogEntryType type = XML_CATA_NONE;
1098     if (xmlStrEqual(name, (const xmlChar *) "system"))
1099         type = XML_CATA_SYSTEM;
1100     else if (xmlStrEqual(name, (const xmlChar *) "public"))
1101         type = XML_CATA_PUBLIC;
1102     else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
1103         type = XML_CATA_REWRITE_SYSTEM;
1104     else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
1105         type = XML_CATA_DELEGATE_PUBLIC;
1106     else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
1107         type = XML_CATA_DELEGATE_SYSTEM;
1108     else if (xmlStrEqual(name, (const xmlChar *) "uri"))
1109         type = XML_CATA_URI;
1110     else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
1111         type = XML_CATA_REWRITE_URI;
1112     else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
1113         type = XML_CATA_DELEGATE_URI;
1114     else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
1115         type = XML_CATA_NEXT_CATALOG;
1116     else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
1117         type = XML_CATA_CATALOG;
1118     return(type);
1119 }
1120
1121 /**
1122  * xmlParseXMLCatalogOneNode:
1123  * @cur:  the XML node
1124  * @type:  the type of Catalog entry
1125  * @name:  the name of the node
1126  * @attrName:  the attribute holding the value
1127  * @uriAttrName:  the attribute holding the URI-Reference
1128  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1129  * @cgroup:  the group which includes this node
1130  *
1131  * Finishes the examination of an XML tree node of a catalog and build
1132  * a Catalog entry from it.
1133  *
1134  * Returns the new Catalog entry node or NULL in case of error.
1135  */
1136 static xmlCatalogEntryPtr
1137 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
1138                           const xmlChar *name, const xmlChar *attrName,
1139                           const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
1140                           xmlCatalogEntryPtr cgroup) {
1141     int ok = 1;
1142     xmlChar *uriValue;
1143     xmlChar *nameValue = NULL;
1144     xmlChar *base = NULL;
1145     xmlChar *URL = NULL;
1146     xmlCatalogEntryPtr ret = NULL;
1147
1148     if (attrName != NULL) {
1149         nameValue = xmlGetProp(cur, attrName);
1150         if (nameValue == NULL) {
1151             xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1152                           "%s entry lacks '%s'\n", name, attrName, NULL);
1153             ok = 0;
1154         }
1155     }
1156     uriValue = xmlGetProp(cur, uriAttrName);
1157     if (uriValue == NULL) {
1158         xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
1159                 "%s entry lacks '%s'\n", name, uriAttrName, NULL);
1160         ok = 0;
1161     }
1162     if (!ok) {
1163         if (nameValue != NULL)
1164             xmlFree(nameValue);
1165         if (uriValue != NULL)
1166             xmlFree(uriValue);
1167         return(NULL);
1168     }
1169
1170     base = xmlNodeGetBase(cur->doc, cur);
1171     URL = xmlBuildURI(uriValue, base);
1172     if (URL != NULL) {
1173         if (xmlDebugCatalogs > 1) {
1174             if (nameValue != NULL)
1175                 xmlGenericError(xmlGenericErrorContext,
1176                         "Found %s: '%s' '%s'\n", name, nameValue, URL);
1177             else
1178                 xmlGenericError(xmlGenericErrorContext,
1179                         "Found %s: '%s'\n", name, URL);
1180         }
1181         ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
1182     } else {
1183         xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
1184                 "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
1185     }
1186     if (nameValue != NULL)
1187         xmlFree(nameValue);
1188     if (uriValue != NULL)
1189         xmlFree(uriValue);
1190     if (base != NULL)
1191         xmlFree(base);
1192     if (URL != NULL)
1193         xmlFree(URL);
1194     return(ret);
1195 }
1196
1197 /**
1198  * xmlParseXMLCatalogNode:
1199  * @cur:  the XML node
1200  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1201  * @parent:  the parent Catalog entry
1202  * @cgroup:  the group which includes this node
1203  *
1204  * Examines an XML tree node of a catalog and build
1205  * a Catalog entry from it adding it to its parent. The examination can
1206  * be recursive.
1207  */
1208 static void
1209 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
1210                        xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
1211 {
1212     xmlChar *base = NULL;
1213     xmlCatalogEntryPtr entry = NULL;
1214
1215     if (cur == NULL)
1216         return;
1217     if (xmlStrEqual(cur->name, BAD_CAST "group")) {
1218         xmlChar *prop;
1219         xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
1220
1221         prop = xmlGetProp(cur, BAD_CAST "prefer");
1222         if (prop != NULL) {
1223             if (xmlStrEqual(prop, BAD_CAST "system")) {
1224                 prefer = XML_CATA_PREFER_SYSTEM;
1225             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1226                 prefer = XML_CATA_PREFER_PUBLIC;
1227             } else {
1228                 xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
1229                               "Invalid value for prefer: '%s'\n",
1230                               prop, NULL, NULL);
1231             }
1232             xmlFree(prop);
1233             pref = prefer;
1234         }
1235         prop = xmlGetProp(cur, BAD_CAST "id");
1236         base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
1237         entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
1238         xmlFree(prop);
1239     } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
1240         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
1241                 BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
1242     } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
1243         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
1244                 BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
1245     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
1246         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
1247                 BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
1248                 BAD_CAST "rewritePrefix", prefer, cgroup);
1249     } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
1250         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
1251                 BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
1252                 BAD_CAST "catalog", prefer, cgroup);
1253     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
1254         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
1255                 BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
1256                 BAD_CAST "catalog", prefer, cgroup);
1257     } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
1258         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
1259                 BAD_CAST "uri", BAD_CAST "name",
1260                 BAD_CAST "uri", prefer, cgroup);
1261     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
1262         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
1263                 BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
1264                 BAD_CAST "rewritePrefix", prefer, cgroup);
1265     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
1266         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
1267                 BAD_CAST "delegateURI", BAD_CAST "uriStartString",
1268                 BAD_CAST "catalog", prefer, cgroup);
1269     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
1270         entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
1271                 BAD_CAST "nextCatalog", NULL,
1272                 BAD_CAST "catalog", prefer, cgroup);
1273     }
1274     if (entry != NULL) {
1275         if (parent != NULL) {
1276             entry->parent = parent;
1277             if (parent->children == NULL)
1278                 parent->children = entry;
1279             else {
1280                 xmlCatalogEntryPtr prev;
1281
1282                 prev = parent->children;
1283                 while (prev->next != NULL)
1284                     prev = prev->next;
1285                 prev->next = entry;
1286             }
1287         }
1288         if (entry->type == XML_CATA_GROUP) {
1289             /*
1290              * Recurse to propagate prefer to the subtree
1291              * (xml:base handling is automated)
1292              */
1293             xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
1294         }
1295     }
1296     if (base != NULL)
1297         xmlFree(base);
1298 }
1299
1300 /**
1301  * xmlParseXMLCatalogNodeList:
1302  * @cur:  the XML node list of siblings
1303  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1304  * @parent:  the parent Catalog entry
1305  * @cgroup:  the group which includes this list
1306  *
1307  * Examines a list of XML sibling nodes of a catalog and build
1308  * a list of Catalog entry from it adding it to the parent.
1309  * The examination will recurse to examine node subtrees.
1310  */
1311 static void
1312 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
1313                            xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
1314     while (cur != NULL) {
1315         if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
1316             (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1317             xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
1318         }
1319         cur = cur->next;
1320     }
1321     /* TODO: sort the list according to REWRITE lengths and prefer value */
1322 }
1323
1324 /**
1325  * xmlParseXMLCatalogFile:
1326  * @prefer:  the PUBLIC vs. SYSTEM current preference value
1327  * @filename:  the filename for the catalog
1328  *
1329  * Parses the catalog file to extract the XML tree and then analyze the
1330  * tree to build a list of Catalog entries corresponding to this catalog
1331  * 
1332  * Returns the resulting Catalog entries list
1333  */
1334 static xmlCatalogEntryPtr
1335 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
1336     xmlDocPtr doc;
1337     xmlNodePtr cur;
1338     xmlChar *prop;
1339     xmlCatalogEntryPtr parent = NULL;
1340
1341     if (filename == NULL)
1342         return(NULL);
1343
1344     doc = xmlParseCatalogFile((const char *) filename);
1345     if (doc == NULL) {
1346         if (xmlDebugCatalogs)
1347             xmlGenericError(xmlGenericErrorContext,
1348                     "Failed to parse catalog %s\n", filename);
1349         return(NULL);
1350     }
1351
1352     if (xmlDebugCatalogs)
1353         xmlGenericError(xmlGenericErrorContext,
1354                 "%d Parsing catalog %s\n", xmlGetThreadId(), filename);
1355
1356     cur = xmlDocGetRootElement(doc);
1357     if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
1358         (cur->ns != NULL) && (cur->ns->href != NULL) &&
1359         (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
1360
1361         parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
1362                                     (const xmlChar *)filename, NULL, prefer, NULL);
1363         if (parent == NULL) {
1364             xmlFreeDoc(doc);
1365             return(NULL);
1366         }
1367
1368         prop = xmlGetProp(cur, BAD_CAST "prefer");
1369         if (prop != NULL) {
1370             if (xmlStrEqual(prop, BAD_CAST "system")) {
1371                 prefer = XML_CATA_PREFER_SYSTEM;
1372             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
1373                 prefer = XML_CATA_PREFER_PUBLIC;
1374             } else {
1375                 xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
1376                               "Invalid value for prefer: '%s'\n",
1377                               prop, NULL, NULL);
1378             }
1379             xmlFree(prop);
1380         }
1381         cur = cur->children;
1382         xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
1383     } else {
1384         xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
1385                       "File %s is not an XML Catalog\n",
1386                       filename, NULL, NULL);
1387         xmlFreeDoc(doc);
1388         return(NULL);
1389     }
1390     xmlFreeDoc(doc);
1391     return(parent);
1392 }
1393
1394 /**
1395  * xmlFetchXMLCatalogFile:
1396  * @catal:  an existing but incomplete catalog entry
1397  *
1398  * Fetch and parse the subcatalog referenced by an entry
1399  * 
1400  * Returns 0 in case of success, -1 otherwise
1401  */
1402 static int
1403 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
1404     xmlCatalogEntryPtr doc;
1405
1406     if (catal == NULL) 
1407         return(-1);
1408     if (catal->URL == NULL)
1409         return(-1);
1410
1411     /*
1412      * lock the whole catalog for modification
1413      */
1414     xmlRMutexLock(xmlCatalogMutex);
1415     if (catal->children != NULL) {
1416         /* Okay someone else did it in the meantime */
1417         xmlRMutexUnlock(xmlCatalogMutex);
1418         return(0);
1419     }
1420
1421     if (xmlCatalogXMLFiles != NULL) {
1422         doc = (xmlCatalogEntryPtr)
1423             xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1424         if (doc != NULL) {
1425             if (xmlDebugCatalogs)
1426                 xmlGenericError(xmlGenericErrorContext,
1427                     "Found %s in file hash\n", catal->URL);
1428
1429             if (catal->type == XML_CATA_CATALOG)
1430                 catal->children = doc->children;
1431             else
1432                 catal->children = doc;
1433             catal->dealloc = 0;
1434             xmlRMutexUnlock(xmlCatalogMutex);
1435             return(0);
1436         }
1437         if (xmlDebugCatalogs)
1438             xmlGenericError(xmlGenericErrorContext,
1439                 "%s not found in file hash\n", catal->URL);
1440     }
1441
1442     /*
1443      * Fetch and parse. Note that xmlParseXMLCatalogFile does not
1444      * use the existing catalog, there is no recursion allowed at
1445      * that level.
1446      */
1447     doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
1448     if (doc == NULL) {
1449         catal->type = XML_CATA_BROKEN_CATALOG;
1450         xmlRMutexUnlock(xmlCatalogMutex);
1451         return(-1);
1452     }
1453
1454     if (catal->type == XML_CATA_CATALOG)
1455         catal->children = doc->children;
1456     else
1457         catal->children = doc;
1458
1459     doc->dealloc = 1;
1460
1461     if (xmlCatalogXMLFiles == NULL)
1462         xmlCatalogXMLFiles = xmlHashCreate(10);
1463     if (xmlCatalogXMLFiles != NULL) {
1464         if (xmlDebugCatalogs)
1465             xmlGenericError(xmlGenericErrorContext,
1466                 "%s added to file hash\n", catal->URL);
1467         xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
1468     }
1469     xmlRMutexUnlock(xmlCatalogMutex);
1470     return(0);
1471 }
1472
1473 /************************************************************************
1474  *                                                                      *
1475  *                      XML Catalog handling                            *
1476  *                                                                      *
1477  ************************************************************************/
1478
1479 /**
1480  * xmlAddXMLCatalog:
1481  * @catal:  top of an XML catalog
1482  * @type:  the type of record to add to the catalog
1483  * @orig:  the system, public or prefix to match (or NULL)
1484  * @replace:  the replacement value for the match
1485  *
1486  * Add an entry in the XML catalog, it may overwrite existing but
1487  * different entries.
1488  *
1489  * Returns 0 if successful, -1 otherwise
1490  */
1491 static int
1492 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
1493               const xmlChar *orig, const xmlChar *replace) {
1494     xmlCatalogEntryPtr cur;
1495     xmlCatalogEntryType typ;
1496     int doregister = 0;
1497
1498     if ((catal == NULL) || 
1499         ((catal->type != XML_CATA_CATALOG) &&
1500          (catal->type != XML_CATA_BROKEN_CATALOG)))
1501         return(-1);
1502     if (catal->children == NULL) {
1503         xmlFetchXMLCatalogFile(catal);
1504     }
1505     if (catal->children == NULL)
1506         doregister = 1;
1507
1508     typ = xmlGetXMLCatalogEntryType(type);
1509     if (typ == XML_CATA_NONE) {
1510         if (xmlDebugCatalogs)
1511             xmlGenericError(xmlGenericErrorContext,
1512                     "Failed to add unknown element %s to catalog\n", type);
1513         return(-1);
1514     }
1515
1516     cur = catal->children;
1517     /*
1518      * Might be a simple "update in place"
1519      */
1520     if (cur != NULL) {
1521         while (cur != NULL) {
1522             if ((orig != NULL) && (cur->type == typ) &&
1523                 (xmlStrEqual(orig, cur->name))) {
1524                 if (xmlDebugCatalogs)
1525                     xmlGenericError(xmlGenericErrorContext,
1526                             "Updating element %s to catalog\n", type);
1527                 if (cur->value != NULL)
1528                     xmlFree(cur->value);
1529                 if (cur->URL != NULL)
1530                     xmlFree(cur->URL);
1531                 cur->value = xmlStrdup(replace);
1532                 cur->URL = xmlStrdup(replace);
1533                 return(0);
1534             }
1535             if (cur->next == NULL)
1536                 break;
1537             cur = cur->next;
1538         }
1539     }
1540     if (xmlDebugCatalogs)
1541         xmlGenericError(xmlGenericErrorContext,
1542                 "Adding element %s to catalog\n", type);
1543     if (cur == NULL)
1544         catal->children = xmlNewCatalogEntry(typ, orig, replace,
1545                                              NULL, catal->prefer, NULL);
1546     else
1547         cur->next = xmlNewCatalogEntry(typ, orig, replace,
1548                                        NULL, catal->prefer, NULL);
1549     if (doregister) {
1550         catal->type = XML_CATA_CATALOG;
1551         cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
1552         if (cur != NULL)
1553             cur->children = catal->children;
1554     }
1555
1556     return(0);
1557 }
1558
1559 /**
1560  * xmlDelXMLCatalog:
1561  * @catal:  top of an XML catalog
1562  * @value:  the value to remove from the catalog
1563  *
1564  * Remove entries in the XML catalog where the value or the URI
1565  * is equal to @value
1566  *
1567  * Returns the number of entries removed if successful, -1 otherwise
1568  */
1569 static int
1570 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
1571     xmlCatalogEntryPtr cur;
1572     int ret = 0;
1573
1574     if ((catal == NULL) || 
1575         ((catal->type != XML_CATA_CATALOG) &&
1576          (catal->type != XML_CATA_BROKEN_CATALOG)))
1577         return(-1);
1578     if (value == NULL)
1579         return(-1);
1580     if (catal->children == NULL) {
1581         xmlFetchXMLCatalogFile(catal);
1582     }
1583
1584     /*
1585      * Scan the children
1586      */
1587     cur = catal->children;
1588     while (cur != NULL) {
1589         if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
1590             (xmlStrEqual(value, cur->value))) {
1591             if (xmlDebugCatalogs) {
1592                 if (cur->name != NULL)
1593                     xmlGenericError(xmlGenericErrorContext,
1594                             "Removing element %s from catalog\n", cur->name);
1595                 else
1596                     xmlGenericError(xmlGenericErrorContext,
1597                             "Removing element %s from catalog\n", cur->value);
1598             }
1599             cur->type = XML_CATA_REMOVED;
1600         }
1601         cur = cur->next;
1602     }
1603     return(ret);
1604 }
1605
1606 /**
1607  * xmlCatalogXMLResolve:
1608  * @catal:  a catalog list
1609  * @pubID:  the public ID string
1610  * @sysID:  the system ID string
1611  *
1612  * Do a complete resolution lookup of an External Identifier for a
1613  * list of catalog entries.
1614  *
1615  * Implements (or tries to) 7.1. External Identifier Resolution
1616  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1617  *
1618  * Returns the URI of the resource or NULL if not found
1619  */
1620 static xmlChar *
1621 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1622                       const xmlChar *sysID) {
1623     xmlChar *ret = NULL;
1624     xmlCatalogEntryPtr cur;
1625     int haveDelegate = 0;
1626     int haveNext = 0;
1627
1628     /*
1629      * protection against loops
1630      */
1631     if (catal->depth > MAX_CATAL_DEPTH) {
1632         xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1633                       "Detected recursion in catalog %s\n",
1634                       catal->name, NULL, NULL);
1635         return(NULL);
1636     }
1637     catal->depth++;
1638
1639     /*
1640      * First tries steps 2/ 3/ 4/ if a system ID is provided.
1641      */
1642     if (sysID != NULL) {
1643         xmlCatalogEntryPtr rewrite = NULL;
1644         int lenrewrite = 0, len;
1645         cur = catal;
1646         haveDelegate = 0;
1647         while (cur != NULL) {
1648             switch (cur->type) {
1649                 case XML_CATA_SYSTEM:
1650                     if (xmlStrEqual(sysID, cur->name)) {
1651                         if (xmlDebugCatalogs)
1652                             xmlGenericError(xmlGenericErrorContext,
1653                                     "Found system match %s, using %s\n",
1654                                             cur->name, cur->URL);
1655                         catal->depth--;
1656                         return(xmlStrdup(cur->URL));
1657                     }
1658                     break;
1659                 case XML_CATA_REWRITE_SYSTEM:
1660                     len = xmlStrlen(cur->name);
1661                     if ((len > lenrewrite) &&
1662                         (!xmlStrncmp(sysID, cur->name, len))) {
1663                         lenrewrite = len;
1664                         rewrite = cur;
1665                     }
1666                     break;
1667                 case XML_CATA_DELEGATE_SYSTEM:
1668                     if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
1669                         haveDelegate++;
1670                     break;
1671                 case XML_CATA_NEXT_CATALOG:
1672                     haveNext++;
1673                     break;
1674                 default:
1675                     break;
1676             }
1677             cur = cur->next;
1678         }
1679         if (rewrite != NULL) {
1680             if (xmlDebugCatalogs)
1681                 xmlGenericError(xmlGenericErrorContext,
1682                         "Using rewriting rule %s\n", rewrite->name);
1683             ret = xmlStrdup(rewrite->URL);
1684             if (ret != NULL)
1685                 ret = xmlStrcat(ret, &sysID[lenrewrite]);
1686             catal->depth--;
1687             return(ret);
1688         }
1689         if (haveDelegate) {
1690             const xmlChar *delegates[MAX_DELEGATE];
1691             int nbList = 0, i;
1692
1693             /*
1694              * Assume the entries have been sorted by decreasing substring
1695              * matches when the list was produced.
1696              */
1697             cur = catal;
1698             while (cur != NULL) {
1699                 if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
1700                     (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
1701                     for (i = 0;i < nbList;i++)
1702                         if (xmlStrEqual(cur->URL, delegates[i]))
1703                             break;
1704                     if (i < nbList) {
1705                         cur = cur->next;
1706                         continue;
1707                     }
1708                     if (nbList < MAX_DELEGATE)
1709                         delegates[nbList++] = cur->URL;
1710
1711                     if (cur->children == NULL) {
1712                         xmlFetchXMLCatalogFile(cur);
1713                     }
1714                     if (cur->children != NULL) {
1715                         if (xmlDebugCatalogs)
1716                             xmlGenericError(xmlGenericErrorContext,
1717                                     "Trying system delegate %s\n", cur->URL);
1718                         ret = xmlCatalogListXMLResolve(
1719                                 cur->children, NULL, sysID);
1720                         if (ret != NULL) {
1721                             catal->depth--;
1722                             return(ret);
1723                         }
1724                     }
1725                 }
1726                 cur = cur->next;
1727             }
1728             /*
1729              * Apply the cut algorithm explained in 4/
1730              */
1731             catal->depth--;
1732             return(XML_CATAL_BREAK);
1733         }
1734     }
1735     /*
1736      * Then tries 5/ 6/ if a public ID is provided
1737      */
1738     if (pubID != NULL) {
1739         cur = catal;
1740         haveDelegate = 0;
1741         while (cur != NULL) {
1742             switch (cur->type) {
1743                 case XML_CATA_PUBLIC:
1744                     if (xmlStrEqual(pubID, cur->name)) {
1745                         if (xmlDebugCatalogs)
1746                             xmlGenericError(xmlGenericErrorContext,
1747                                     "Found public match %s\n", cur->name);
1748                         catal->depth--;
1749                         return(xmlStrdup(cur->URL));
1750                     }
1751                     break;
1752                 case XML_CATA_DELEGATE_PUBLIC:
1753                     if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
1754                         (cur->prefer == XML_CATA_PREFER_PUBLIC))
1755                         haveDelegate++;
1756                     break;
1757                 case XML_CATA_NEXT_CATALOG:
1758                     if (sysID == NULL)
1759                         haveNext++;
1760                     break;
1761                 default:
1762                     break;
1763             }
1764             cur = cur->next;
1765         }
1766         if (haveDelegate) {
1767             const xmlChar *delegates[MAX_DELEGATE];
1768             int nbList = 0, i;
1769
1770             /*
1771              * Assume the entries have been sorted by decreasing substring
1772              * matches when the list was produced.
1773              */
1774             cur = catal;
1775             while (cur != NULL) {
1776                 if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
1777                     (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
1778                     (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
1779
1780                     for (i = 0;i < nbList;i++)
1781                         if (xmlStrEqual(cur->URL, delegates[i]))
1782                             break;
1783                     if (i < nbList) {
1784                         cur = cur->next;
1785                         continue;
1786                     }
1787                     if (nbList < MAX_DELEGATE)
1788                         delegates[nbList++] = cur->URL;
1789                             
1790                     if (cur->children == NULL) {
1791                         xmlFetchXMLCatalogFile(cur);
1792                     }
1793                     if (cur->children != NULL) {
1794                         if (xmlDebugCatalogs)
1795                             xmlGenericError(xmlGenericErrorContext,
1796                                     "Trying public delegate %s\n", cur->URL);
1797                         ret = xmlCatalogListXMLResolve(
1798                                 cur->children, pubID, NULL);
1799                         if (ret != NULL) {
1800                             catal->depth--;
1801                             return(ret);
1802                         }
1803                     }
1804                 }
1805                 cur = cur->next;
1806             }
1807             /*
1808              * Apply the cut algorithm explained in 4/
1809              */
1810             catal->depth--;
1811             return(XML_CATAL_BREAK);
1812         }
1813     }
1814     if (haveNext) {
1815         cur = catal;
1816         while (cur != NULL) {
1817             if (cur->type == XML_CATA_NEXT_CATALOG) {
1818                 if (cur->children == NULL) {
1819                     xmlFetchXMLCatalogFile(cur);
1820                 }
1821                 if (cur->children != NULL) {
1822                     ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
1823                     if (ret != NULL) {
1824                         catal->depth--;
1825                         return(ret);
1826                     } else if (catal->depth > MAX_CATAL_DEPTH) {
1827                         return(NULL);
1828                     }
1829                 }
1830             }
1831             cur = cur->next;
1832         }
1833     }
1834
1835     catal->depth--;
1836     return(NULL);
1837 }
1838
1839 /**
1840  * xmlCatalogXMLResolveURI:
1841  * @catal:  a catalog list
1842  * @URI:  the URI
1843  * @sysID:  the system ID string
1844  *
1845  * Do a complete resolution lookup of an External Identifier for a
1846  * list of catalog entries.
1847  *
1848  * Implements (or tries to) 7.2.2. URI Resolution
1849  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1850  *
1851  * Returns the URI of the resource or NULL if not found
1852  */
1853 static xmlChar *
1854 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
1855     xmlChar *ret = NULL;
1856     xmlCatalogEntryPtr cur;
1857     int haveDelegate = 0;
1858     int haveNext = 0;
1859     xmlCatalogEntryPtr rewrite = NULL;
1860     int lenrewrite = 0, len;
1861
1862     if (catal == NULL)
1863         return(NULL);
1864
1865     if (URI == NULL)
1866         return(NULL);
1867
1868     if (catal->depth > MAX_CATAL_DEPTH) {
1869         xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
1870                       "Detected recursion in catalog %s\n",
1871                       catal->name, NULL, NULL);
1872         return(NULL);
1873     }
1874
1875     /*
1876      * First tries steps 2/ 3/ 4/ if a system ID is provided.
1877      */
1878     cur = catal;
1879     haveDelegate = 0;
1880     while (cur != NULL) {
1881         switch (cur->type) {
1882             case XML_CATA_URI:
1883                 if (xmlStrEqual(URI, cur->name)) {
1884                     if (xmlDebugCatalogs)
1885                         xmlGenericError(xmlGenericErrorContext,
1886                                 "Found URI match %s\n", cur->name);
1887                     return(xmlStrdup(cur->URL));
1888                 }
1889                 break;
1890             case XML_CATA_REWRITE_URI:
1891                 len = xmlStrlen(cur->name);
1892                 if ((len > lenrewrite) &&
1893                     (!xmlStrncmp(URI, cur->name, len))) {
1894                     lenrewrite = len;
1895                     rewrite = cur;
1896                 }
1897                 break;
1898             case XML_CATA_DELEGATE_URI:
1899                 if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
1900                     haveDelegate++;
1901                 break;
1902             case XML_CATA_NEXT_CATALOG:
1903                 haveNext++;
1904                 break;
1905             default:
1906                 break;
1907         }
1908         cur = cur->next;
1909     }
1910     if (rewrite != NULL) {
1911         if (xmlDebugCatalogs)
1912             xmlGenericError(xmlGenericErrorContext,
1913                     "Using rewriting rule %s\n", rewrite->name);
1914         ret = xmlStrdup(rewrite->URL);
1915         if (ret != NULL)
1916             ret = xmlStrcat(ret, &URI[lenrewrite]);
1917         return(ret);
1918     }
1919     if (haveDelegate) {
1920         const xmlChar *delegates[MAX_DELEGATE];
1921         int nbList = 0, i;
1922
1923         /*
1924          * Assume the entries have been sorted by decreasing substring
1925          * matches when the list was produced.
1926          */
1927         cur = catal;
1928         while (cur != NULL) {
1929             if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
1930                  (cur->type == XML_CATA_DELEGATE_URI)) &&
1931                 (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
1932                 for (i = 0;i < nbList;i++)
1933                     if (xmlStrEqual(cur->URL, delegates[i]))
1934                         break;
1935                 if (i < nbList) {
1936                     cur = cur->next;
1937                     continue;
1938                 }
1939                 if (nbList < MAX_DELEGATE)
1940                     delegates[nbList++] = cur->URL;
1941
1942                 if (cur->children == NULL) {
1943                     xmlFetchXMLCatalogFile(cur);
1944                 }
1945                 if (cur->children != NULL) {
1946                     if (xmlDebugCatalogs)
1947                         xmlGenericError(xmlGenericErrorContext,
1948                                 "Trying URI delegate %s\n", cur->URL);
1949                     ret = xmlCatalogListXMLResolveURI(
1950                             cur->children, URI);
1951                     if (ret != NULL)
1952                         return(ret);
1953                 }
1954             }
1955             cur = cur->next;
1956         }
1957         /*
1958          * Apply the cut algorithm explained in 4/
1959          */
1960         return(XML_CATAL_BREAK);
1961     }
1962     if (haveNext) {
1963         cur = catal;
1964         while (cur != NULL) {
1965             if (cur->type == XML_CATA_NEXT_CATALOG) {
1966                 if (cur->children == NULL) {
1967                     xmlFetchXMLCatalogFile(cur);
1968                 }
1969                 if (cur->children != NULL) {
1970                     ret = xmlCatalogListXMLResolveURI(cur->children, URI);
1971                     if (ret != NULL)
1972                         return(ret);
1973                 }
1974             }
1975             cur = cur->next;
1976         }
1977     }
1978
1979     return(NULL);
1980 }
1981
1982 /**
1983  * xmlCatalogListXMLResolve:
1984  * @catal:  a catalog list
1985  * @pubID:  the public ID string
1986  * @sysID:  the system ID string
1987  *
1988  * Do a complete resolution lookup of an External Identifier for a
1989  * list of catalogs
1990  *
1991  * Implements (or tries to) 7.1. External Identifier Resolution
1992  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
1993  *
1994  * Returns the URI of the resource or NULL if not found
1995  */
1996 static xmlChar *
1997 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
1998                       const xmlChar *sysID) {
1999     xmlChar *ret = NULL;
2000     xmlChar *urnID = NULL;
2001     xmlChar *normid;
2002     
2003     if (catal == NULL)
2004         return(NULL);
2005     if ((pubID == NULL) && (sysID == NULL))
2006         return(NULL);
2007
2008     normid = xmlCatalogNormalizePublic(pubID);
2009     if (normid != NULL)
2010         pubID = (*normid != 0 ? normid : NULL);
2011     
2012     if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2013         urnID = xmlCatalogUnWrapURN(pubID);
2014         if (xmlDebugCatalogs) {
2015             if (urnID == NULL)
2016                 xmlGenericError(xmlGenericErrorContext,
2017                         "Public URN ID %s expanded to NULL\n", pubID);
2018             else
2019                 xmlGenericError(xmlGenericErrorContext,
2020                         "Public URN ID expanded to %s\n", urnID);
2021         }
2022         ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
2023         if (urnID != NULL)
2024             xmlFree(urnID);
2025         if (normid != NULL)
2026             xmlFree(normid);
2027         return(ret);
2028     }
2029     if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2030         urnID = xmlCatalogUnWrapURN(sysID);
2031         if (xmlDebugCatalogs) {
2032             if (urnID == NULL)
2033                 xmlGenericError(xmlGenericErrorContext,
2034                         "System URN ID %s expanded to NULL\n", sysID);
2035             else
2036                 xmlGenericError(xmlGenericErrorContext,
2037                         "System URN ID expanded to %s\n", urnID);
2038         }
2039         if (pubID == NULL)
2040             ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2041         else if (xmlStrEqual(pubID, urnID))
2042             ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
2043         else {
2044             ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
2045         }
2046         if (urnID != NULL)
2047             xmlFree(urnID);
2048         if (normid != NULL)
2049             xmlFree(normid);
2050         return(ret);
2051     }
2052     while (catal != NULL) {
2053         if (catal->type == XML_CATA_CATALOG) {
2054             if (catal->children == NULL) {
2055                 xmlFetchXMLCatalogFile(catal);
2056             }
2057             if (catal->children != NULL) {
2058                 ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
2059                 if (ret != NULL) {
2060                     break;
2061                 } else if ((catal->children != NULL) &&
2062                            (catal->children->depth > MAX_CATAL_DEPTH)) {
2063                     ret = NULL;
2064                     break;
2065                 }
2066             }
2067         }
2068         catal = catal->next;
2069     }
2070     if (normid != NULL)
2071         xmlFree(normid);
2072     return(ret);
2073 }
2074
2075 /**
2076  * xmlCatalogListXMLResolveURI:
2077  * @catal:  a catalog list
2078  * @URI:  the URI
2079  *
2080  * Do a complete resolution lookup of an URI for a list of catalogs
2081  *
2082  * Implements (or tries to) 7.2. URI Resolution
2083  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
2084  *
2085  * Returns the URI of the resource or NULL if not found
2086  */
2087 static xmlChar *
2088 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
2089     xmlChar *ret = NULL;
2090     xmlChar *urnID = NULL;
2091     
2092     if (catal == NULL)
2093         return(NULL);
2094     if (URI == NULL)
2095         return(NULL);
2096
2097     if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
2098         urnID = xmlCatalogUnWrapURN(URI);
2099         if (xmlDebugCatalogs) {
2100             if (urnID == NULL)
2101                 xmlGenericError(xmlGenericErrorContext,
2102                         "URN ID %s expanded to NULL\n", URI);
2103             else
2104                 xmlGenericError(xmlGenericErrorContext,
2105                         "URN ID expanded to %s\n", urnID);
2106         }
2107         ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
2108         if (urnID != NULL)
2109             xmlFree(urnID);
2110         return(ret);
2111     }
2112     while (catal != NULL) {
2113         if (catal->type == XML_CATA_CATALOG) {
2114             if (catal->children == NULL) {
2115                 xmlFetchXMLCatalogFile(catal);
2116             }
2117             if (catal->children != NULL) {
2118                 ret = xmlCatalogXMLResolveURI(catal->children, URI);
2119                 if (ret != NULL)
2120                     return(ret);
2121             }
2122         }
2123         catal = catal->next;
2124     }
2125     return(ret);
2126 }
2127
2128 /************************************************************************
2129  *                                                                      *
2130  *                      The SGML Catalog parser                         *
2131  *                                                                      *
2132  ************************************************************************/
2133
2134
2135 #define RAW *cur
2136 #define NEXT cur++;
2137 #define SKIP(x) cur += x;
2138
2139 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
2140
2141 /**
2142  * xmlParseSGMLCatalogComment:
2143  * @cur:  the current character
2144  *
2145  * Skip a comment in an SGML catalog
2146  *
2147  * Returns new current character
2148  */
2149 static const xmlChar *
2150 xmlParseSGMLCatalogComment(const xmlChar *cur) {
2151     if ((cur[0] != '-') || (cur[1] != '-')) 
2152         return(cur);
2153     SKIP(2);
2154     while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
2155         NEXT;
2156     if (cur[0] == 0) {
2157         return(NULL);
2158     }
2159     return(cur + 2);
2160 }
2161
2162 /**
2163  * xmlParseSGMLCatalogPubid:
2164  * @cur:  the current character
2165  * @id:  the return location
2166  *
2167  * Parse an SGML catalog ID
2168  *
2169  * Returns new current character and store the value in @id
2170  */
2171 static const xmlChar *
2172 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
2173     xmlChar *buf = NULL, *tmp;
2174     int len = 0;
2175     int size = 50;
2176     xmlChar stop;
2177     int count = 0;
2178
2179     *id = NULL;
2180
2181     if (RAW == '"') {
2182         NEXT;
2183         stop = '"';
2184     } else if (RAW == '\'') {
2185         NEXT;
2186         stop = '\'';
2187     } else {
2188         stop = ' ';
2189     }
2190     buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
2191     if (buf == NULL) {
2192         xmlCatalogErrMemory("allocating public ID");
2193         return(NULL);
2194     }
2195     while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
2196         if ((*cur == stop) && (stop != ' '))
2197             break;
2198         if ((stop == ' ') && (IS_BLANK_CH(*cur)))
2199             break;
2200         if (len + 1 >= size) {
2201             size *= 2;
2202             tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
2203             if (tmp == NULL) {
2204                 xmlCatalogErrMemory("allocating public ID");
2205                 xmlFree(buf);
2206                 return(NULL);
2207             }
2208             buf = tmp;
2209         }
2210         buf[len++] = *cur;
2211         count++;
2212         NEXT;
2213     }
2214     buf[len] = 0;
2215     if (stop == ' ') {
2216         if (!IS_BLANK_CH(*cur)) {
2217             xmlFree(buf);
2218             return(NULL);
2219         }
2220     } else {
2221         if (*cur != stop) {
2222             xmlFree(buf);
2223             return(NULL);
2224         }
2225         NEXT;
2226     }
2227     *id = buf;
2228     return(cur);
2229 }
2230
2231 /**
2232  * xmlParseSGMLCatalogName:
2233  * @cur:  the current character
2234  * @name:  the return location
2235  *
2236  * Parse an SGML catalog name
2237  *
2238  * Returns new current character and store the value in @name
2239  */
2240 static const xmlChar *
2241 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
2242     xmlChar buf[XML_MAX_NAMELEN + 5];
2243     int len = 0;
2244     int c;
2245
2246     *name = NULL;
2247
2248     /*
2249      * Handler for more complex cases
2250      */
2251     c = *cur;
2252     if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
2253         return(NULL);
2254     }
2255
2256     while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
2257             (c == '.') || (c == '-') ||
2258             (c == '_') || (c == ':'))) {
2259         buf[len++] = c;
2260         cur++;
2261         c = *cur;
2262         if (len >= XML_MAX_NAMELEN)
2263             return(NULL);
2264     }
2265     *name = xmlStrndup(buf, len);
2266     return(cur);
2267 }
2268
2269 /**
2270  * xmlGetSGMLCatalogEntryType:
2271  * @name:  the entry name
2272  *
2273  * Get the Catalog entry type for a given SGML Catalog name
2274  *
2275  * Returns Catalog entry type
2276  */
2277 static xmlCatalogEntryType
2278 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
2279     xmlCatalogEntryType type = XML_CATA_NONE;
2280     if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2281         type = SGML_CATA_SYSTEM;
2282     else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2283         type = SGML_CATA_PUBLIC;
2284     else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2285         type = SGML_CATA_DELEGATE;
2286     else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2287         type = SGML_CATA_ENTITY;
2288     else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2289         type = SGML_CATA_DOCTYPE;
2290     else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2291         type = SGML_CATA_LINKTYPE;
2292     else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2293         type = SGML_CATA_NOTATION;
2294     else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2295         type = SGML_CATA_SGMLDECL;
2296     else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2297         type = SGML_CATA_DOCUMENT;
2298     else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2299         type = SGML_CATA_CATALOG;
2300     else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2301         type = SGML_CATA_BASE;
2302     return(type);
2303 }
2304
2305 /**
2306  * xmlParseSGMLCatalog:
2307  * @catal:  the SGML Catalog
2308  * @value:  the content of the SGML Catalog serialization
2309  * @file:  the filepath for the catalog
2310  * @super:  should this be handled as a Super Catalog in which case
2311  *          parsing is not recursive
2312  *
2313  * Parse an SGML catalog content and fill up the @catal hash table with
2314  * the new entries found.
2315  *
2316  * Returns 0 in case of success, -1 in case of error.
2317  */
2318 static int
2319 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
2320                     const char *file, int super) {
2321     const xmlChar *cur = value;
2322     xmlChar *base = NULL;
2323     int res;
2324
2325     if ((cur == NULL) || (file == NULL))
2326         return(-1);
2327     base = xmlStrdup((const xmlChar *) file);
2328
2329     while ((cur != NULL) && (cur[0] != 0)) {
2330         SKIP_BLANKS;
2331         if (cur[0] == 0)
2332             break;
2333         if ((cur[0] == '-') && (cur[1] == '-')) {
2334             cur = xmlParseSGMLCatalogComment(cur);
2335             if (cur == NULL) {
2336                 /* error */
2337                 break;
2338             }
2339         } else {
2340             xmlChar *sysid = NULL;
2341             xmlChar *name = NULL;
2342             xmlCatalogEntryType type = XML_CATA_NONE;
2343
2344             cur = xmlParseSGMLCatalogName(cur, &name);
2345             if (name == NULL) {
2346                 /* error */
2347                 break;
2348             }
2349             if (!IS_BLANK_CH(*cur)) {
2350                 /* error */
2351                 break;
2352             }
2353             SKIP_BLANKS;
2354             if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
2355                 type = SGML_CATA_SYSTEM;
2356             else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
2357                 type = SGML_CATA_PUBLIC;
2358             else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
2359                 type = SGML_CATA_DELEGATE;
2360             else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
2361                 type = SGML_CATA_ENTITY;
2362             else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
2363                 type = SGML_CATA_DOCTYPE;
2364             else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
2365                 type = SGML_CATA_LINKTYPE;
2366             else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
2367                 type = SGML_CATA_NOTATION;
2368             else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
2369                 type = SGML_CATA_SGMLDECL;
2370             else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
2371                 type = SGML_CATA_DOCUMENT;
2372             else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
2373                 type = SGML_CATA_CATALOG;
2374             else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
2375                 type = SGML_CATA_BASE;
2376             else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
2377                 xmlFree(name);
2378                 cur = xmlParseSGMLCatalogName(cur, &name);
2379                 if (name == NULL) {
2380                     /* error */
2381                     break;
2382                 }
2383                 xmlFree(name);
2384                 continue;
2385             }
2386             xmlFree(name);
2387             name = NULL;
2388
2389             switch(type) {
2390                 case SGML_CATA_ENTITY:
2391                     if (*cur == '%')
2392                         type = SGML_CATA_PENTITY;
2393                 case SGML_CATA_PENTITY:
2394                 case SGML_CATA_DOCTYPE:
2395                 case SGML_CATA_LINKTYPE:
2396                 case SGML_CATA_NOTATION:
2397                     cur = xmlParseSGMLCatalogName(cur, &name);
2398                     if (cur == NULL) {
2399                         /* error */
2400                         break;
2401                     }
2402                     if (!IS_BLANK_CH(*cur)) {
2403                         /* error */
2404                         break;
2405                     }
2406                     SKIP_BLANKS;
2407                     cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2408                     if (cur == NULL) {
2409                         /* error */
2410                         break;
2411                     }
2412                     break;
2413                 case SGML_CATA_PUBLIC:
2414                 case SGML_CATA_SYSTEM:
2415                 case SGML_CATA_DELEGATE:
2416                     cur = xmlParseSGMLCatalogPubid(cur, &name);
2417                     if (cur == NULL) {
2418                         /* error */
2419                         break;
2420                     }
2421                     if (type != SGML_CATA_SYSTEM) {
2422                         xmlChar *normid;
2423
2424                         normid = xmlCatalogNormalizePublic(name);
2425                         if (normid != NULL) {
2426                             if (name != NULL)
2427                                 xmlFree(name);
2428                             if (*normid != 0)
2429                                 name = normid;
2430                             else {
2431                                 xmlFree(normid);
2432                                 name = NULL;
2433                             }
2434                         }
2435                     }
2436                     if (!IS_BLANK_CH(*cur)) {
2437                         /* error */
2438                         break;
2439                     }
2440                     SKIP_BLANKS;
2441                     cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2442                     if (cur == NULL) {
2443                         /* error */
2444                         break;
2445                     }
2446                     break;
2447                 case SGML_CATA_BASE:
2448                 case SGML_CATA_CATALOG:
2449                 case SGML_CATA_DOCUMENT:
2450                 case SGML_CATA_SGMLDECL:
2451                     cur = xmlParseSGMLCatalogPubid(cur, &sysid);
2452                     if (cur == NULL) {
2453                         /* error */
2454                         break;
2455                     }
2456                     break;
2457                 default:
2458                     break;
2459             }
2460             if (cur == NULL) {
2461                 if (name != NULL)
2462                     xmlFree(name);
2463                 if (sysid != NULL)
2464                     xmlFree(sysid);
2465                 break;
2466             } else if (type == SGML_CATA_BASE) {
2467                 if (base != NULL)
2468                     xmlFree(base);
2469                 base = xmlStrdup(sysid);
2470             } else if ((type == SGML_CATA_PUBLIC) ||
2471                        (type == SGML_CATA_SYSTEM)) {
2472                 xmlChar *filename;
2473
2474                 filename = xmlBuildURI(sysid, base);
2475                 if (filename != NULL) {
2476                     xmlCatalogEntryPtr entry;
2477
2478                     entry = xmlNewCatalogEntry(type, name, filename,
2479                                                NULL, XML_CATA_PREFER_NONE, NULL);
2480                     res = xmlHashAddEntry(catal->sgml, name, entry);
2481                     if (res < 0) {
2482                         xmlFreeCatalogEntry(entry);
2483                     }
2484                     xmlFree(filename);
2485                 }
2486
2487             } else if (type == SGML_CATA_CATALOG) {
2488                 if (super) {
2489                     xmlCatalogEntryPtr entry;
2490
2491                     entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
2492                                                XML_CATA_PREFER_NONE, NULL);
2493                     res = xmlHashAddEntry(catal->sgml, sysid, entry);
2494                     if (res < 0) {
2495                         xmlFreeCatalogEntry(entry);
2496                     }
2497                 } else {
2498                     xmlChar *filename;
2499
2500                     filename = xmlBuildURI(sysid, base);
2501                     if (filename != NULL) {
2502                         xmlExpandCatalog(catal, (const char *)filename);
2503                         xmlFree(filename);
2504                     }
2505                 }
2506             }
2507             /*
2508              * drop anything else we won't handle it
2509              */
2510             if (name != NULL)
2511                 xmlFree(name);
2512             if (sysid != NULL)
2513                 xmlFree(sysid);
2514         }
2515     }
2516     if (base != NULL)
2517         xmlFree(base);
2518     if (cur == NULL)
2519         return(-1);
2520     return(0);
2521 }
2522
2523 /************************************************************************
2524  *                                                                      *
2525  *                      SGML Catalog handling                           *
2526  *                                                                      *
2527  ************************************************************************/
2528
2529 /**
2530  * xmlCatalogGetSGMLPublic:
2531  * @catal:  an SGML catalog hash
2532  * @pubID:  the public ID string
2533  *
2534  * Try to lookup the catalog local reference associated to a public ID
2535  *
2536  * Returns the local resource if found or NULL otherwise.
2537  */
2538 static const xmlChar *
2539 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
2540     xmlCatalogEntryPtr entry;
2541     xmlChar *normid;
2542
2543     if (catal == NULL)
2544         return(NULL);
2545
2546     normid = xmlCatalogNormalizePublic(pubID);
2547     if (normid != NULL)
2548         pubID = (*normid != 0 ? normid : NULL);
2549
2550     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
2551     if (entry == NULL) {
2552         if (normid != NULL)
2553             xmlFree(normid);
2554         return(NULL);
2555     }
2556     if (entry->type == SGML_CATA_PUBLIC) {
2557         if (normid != NULL)
2558             xmlFree(normid);
2559         return(entry->URL);
2560     }
2561     if (normid != NULL)
2562         xmlFree(normid);
2563     return(NULL);
2564 }
2565
2566 /**
2567  * xmlCatalogGetSGMLSystem:
2568  * @catal:  an SGML catalog hash
2569  * @sysID:  the system ID string
2570  *
2571  * Try to lookup the catalog local reference for a system ID
2572  *
2573  * Returns the local resource if found or NULL otherwise.
2574  */
2575 static const xmlChar *
2576 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
2577     xmlCatalogEntryPtr entry;
2578
2579     if (catal == NULL)
2580         return(NULL);
2581
2582     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
2583     if (entry == NULL)
2584         return(NULL);
2585     if (entry->type == SGML_CATA_SYSTEM)
2586         return(entry->URL);
2587     return(NULL);
2588 }
2589
2590 /**
2591  * xmlCatalogSGMLResolve:
2592  * @catal:  the SGML catalog
2593  * @pubID:  the public ID string
2594  * @sysID:  the system ID string
2595  *
2596  * Do a complete resolution lookup of an External Identifier
2597  *
2598  * Returns the URI of the resource or NULL if not found
2599  */
2600 static const xmlChar *
2601 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
2602                       const xmlChar *sysID) {
2603     const xmlChar *ret = NULL;
2604
2605     if (catal->sgml == NULL)
2606         return(NULL);
2607
2608     if (pubID != NULL)
2609         ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2610     if (ret != NULL)
2611         return(ret);
2612     if (sysID != NULL)
2613         ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2614     if (ret != NULL)
2615         return(ret);
2616     return(NULL);
2617 }
2618
2619 /************************************************************************
2620  *                                                                      *
2621  *                      Specific Public interfaces                      *
2622  *                                                                      *
2623  ************************************************************************/
2624
2625 /**
2626  * xmlLoadSGMLSuperCatalog:
2627  * @filename:  a file path
2628  *
2629  * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
2630  * references. This is only needed for manipulating SGML Super Catalogs
2631  * like adding and removing CATALOG or DELEGATE entries.
2632  *
2633  * Returns the catalog parsed or NULL in case of error
2634  */
2635 xmlCatalogPtr
2636 xmlLoadSGMLSuperCatalog(const char *filename)
2637 {
2638     xmlChar *content;
2639     xmlCatalogPtr catal;
2640     int ret;
2641
2642     content = xmlLoadFileContent(filename);
2643     if (content == NULL)
2644         return(NULL);
2645
2646     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2647     if (catal == NULL) {
2648         xmlFree(content);
2649         return(NULL);
2650     }
2651
2652     ret = xmlParseSGMLCatalog(catal, content, filename, 1);
2653     xmlFree(content);
2654     if (ret < 0) {
2655         xmlFreeCatalog(catal);
2656         return(NULL);
2657     }
2658     return (catal);
2659 }
2660
2661 /**
2662  * xmlLoadACatalog:
2663  * @filename:  a file path
2664  *
2665  * Load the catalog and build the associated data structures.
2666  * This can be either an XML Catalog or an SGML Catalog
2667  * It will recurse in SGML CATALOG entries. On the other hand XML
2668  * Catalogs are not handled recursively.
2669  *
2670  * Returns the catalog parsed or NULL in case of error
2671  */
2672 xmlCatalogPtr
2673 xmlLoadACatalog(const char *filename)
2674 {
2675     xmlChar *content;
2676     xmlChar *first;
2677     xmlCatalogPtr catal;
2678     int ret;
2679
2680     content = xmlLoadFileContent(filename);
2681     if (content == NULL)
2682         return(NULL);
2683
2684
2685     first = content;
2686    
2687     while ((*first != 0) && (*first != '-') && (*first != '<') &&
2688            (!(((*first >= 'A') && (*first <= 'Z')) ||
2689               ((*first >= 'a') && (*first <= 'z')))))
2690         first++;
2691
2692     if (*first != '<') {
2693         catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2694         if (catal == NULL) {
2695             xmlFree(content);
2696             return(NULL);
2697         }
2698         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2699         if (ret < 0) {
2700             xmlFreeCatalog(catal);
2701             xmlFree(content);
2702             return(NULL);
2703         }
2704     } else {
2705         catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
2706         if (catal == NULL) {
2707             xmlFree(content);
2708             return(NULL);
2709         }
2710         catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2711                        NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2712     }
2713     xmlFree(content);
2714     return (catal);
2715 }
2716
2717 /**
2718  * xmlExpandCatalog:
2719  * @catal:  a catalog
2720  * @filename:  a file path
2721  *
2722  * Load the catalog and expand the existing catal structure.
2723  * This can be either an XML Catalog or an SGML Catalog
2724  *
2725  * Returns 0 in case of success, -1 in case of error
2726  */
2727 static int
2728 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
2729 {
2730     int ret;
2731
2732     if ((catal == NULL) || (filename == NULL))
2733         return(-1);
2734
2735
2736     if (catal->type == XML_SGML_CATALOG_TYPE) {
2737         xmlChar *content;
2738
2739         content = xmlLoadFileContent(filename);
2740         if (content == NULL)
2741             return(-1);
2742
2743         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
2744         if (ret < 0) {
2745             xmlFree(content);
2746             return(-1);
2747         }
2748         xmlFree(content);
2749     } else {
2750         xmlCatalogEntryPtr tmp, cur;
2751         tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
2752                        NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
2753
2754         cur = catal->xml;
2755         if (cur == NULL) {
2756             catal->xml = tmp;
2757         } else {
2758             while (cur->next != NULL) cur = cur->next;
2759             cur->next = tmp;
2760         }
2761     }
2762     return (0);
2763 }
2764
2765 /**
2766  * xmlACatalogResolveSystem:
2767  * @catal:  a Catalog
2768  * @sysID:  the system ID string
2769  *
2770  * Try to lookup the catalog resource for a system ID
2771  *
2772  * Returns the resource if found or NULL otherwise, the value returned
2773  *      must be freed by the caller.
2774  */
2775 xmlChar *
2776 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
2777     xmlChar *ret = NULL;
2778
2779     if ((sysID == NULL) || (catal == NULL))
2780         return(NULL);
2781     
2782     if (xmlDebugCatalogs)
2783         xmlGenericError(xmlGenericErrorContext,
2784                 "Resolve sysID %s\n", sysID);
2785
2786     if (catal->type == XML_XML_CATALOG_TYPE) {
2787         ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
2788         if (ret == XML_CATAL_BREAK)
2789             ret = NULL;
2790     } else {
2791         const xmlChar *sgml;
2792
2793         sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
2794         if (sgml != NULL)
2795             ret = xmlStrdup(sgml);
2796     }
2797     return(ret);
2798 }
2799
2800 /**
2801  * xmlACatalogResolvePublic:
2802  * @catal:  a Catalog
2803  * @pubID:  the public ID string
2804  *
2805  * Try to lookup the catalog local reference associated to a public ID in that catalog
2806  *
2807  * Returns the local resource if found or NULL otherwise, the value returned
2808  *      must be freed by the caller.
2809  */
2810 xmlChar *
2811 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
2812     xmlChar *ret = NULL;
2813
2814     if ((pubID == NULL) || (catal == NULL))
2815         return(NULL);
2816     
2817     if (xmlDebugCatalogs)
2818         xmlGenericError(xmlGenericErrorContext,
2819                 "Resolve pubID %s\n", pubID);
2820
2821     if (catal->type == XML_XML_CATALOG_TYPE) {
2822         ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
2823         if (ret == XML_CATAL_BREAK)
2824             ret = NULL;
2825     } else {
2826         const xmlChar *sgml;
2827
2828         sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
2829         if (sgml != NULL)
2830             ret = xmlStrdup(sgml);
2831     }
2832     return(ret);
2833 }
2834
2835 /**
2836  * xmlACatalogResolve:
2837  * @catal:  a Catalog
2838  * @pubID:  the public ID string
2839  * @sysID:  the system ID string
2840  *
2841  * Do a complete resolution lookup of an External Identifier
2842  *
2843  * Returns the URI of the resource or NULL if not found, it must be freed
2844  *      by the caller.
2845  */
2846 xmlChar *
2847 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
2848                    const xmlChar * sysID)
2849 {
2850     xmlChar *ret = NULL;
2851
2852     if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
2853         return (NULL);
2854
2855     if (xmlDebugCatalogs) {
2856          if ((pubID != NULL) && (sysID != NULL)) {
2857              xmlGenericError(xmlGenericErrorContext,
2858                              "Resolve: pubID %s sysID %s\n", pubID, sysID);
2859          } else if (pubID != NULL) {
2860              xmlGenericError(xmlGenericErrorContext,
2861                              "Resolve: pubID %s\n", pubID);
2862          } else {
2863              xmlGenericError(xmlGenericErrorContext,
2864                              "Resolve: sysID %s\n", sysID);
2865          }
2866     }
2867
2868     if (catal->type == XML_XML_CATALOG_TYPE) {
2869         ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
2870         if (ret == XML_CATAL_BREAK)
2871             ret = NULL;
2872     } else {
2873         const xmlChar *sgml;
2874
2875         sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
2876         if (sgml != NULL)
2877             ret = xmlStrdup(sgml);
2878     }
2879     return (ret);
2880 }
2881
2882 /**
2883  * xmlACatalogResolveURI:
2884  * @catal:  a Catalog
2885  * @URI:  the URI
2886  *
2887  * Do a complete resolution lookup of an URI
2888  *
2889  * Returns the URI of the resource or NULL if not found, it must be freed
2890  *      by the caller.
2891  */
2892 xmlChar *
2893 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
2894     xmlChar *ret = NULL;
2895
2896     if ((URI == NULL) || (catal == NULL))
2897         return(NULL);
2898
2899     if (xmlDebugCatalogs)
2900         xmlGenericError(xmlGenericErrorContext,
2901                 "Resolve URI %s\n", URI);
2902
2903     if (catal->type == XML_XML_CATALOG_TYPE) {
2904         ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
2905         if (ret == XML_CATAL_BREAK)
2906             ret = NULL;
2907     } else {
2908         const xmlChar *sgml;
2909
2910         sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
2911         if (sgml != NULL)
2912             ret = xmlStrdup(sgml);
2913     }
2914     return(ret);
2915 }
2916
2917 #ifdef LIBXML_OUTPUT_ENABLED
2918 /**
2919  * xmlACatalogDump:
2920  * @catal:  a Catalog
2921  * @out:  the file.
2922  *
2923  * Dump the given catalog to the given file.
2924  */
2925 void
2926 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
2927     if ((out == NULL) || (catal == NULL))
2928         return;
2929
2930     if (catal->type == XML_XML_CATALOG_TYPE) {
2931         xmlDumpXMLCatalog(out, catal->xml);
2932     } else {
2933         xmlHashScan(catal->sgml,
2934                     (xmlHashScanner) xmlCatalogDumpEntry, out);
2935     } 
2936 }
2937 #endif /* LIBXML_OUTPUT_ENABLED */
2938
2939 /**
2940  * xmlACatalogAdd:
2941  * @catal:  a Catalog
2942  * @type:  the type of record to add to the catalog
2943  * @orig:  the system, public or prefix to match 
2944  * @replace:  the replacement value for the match
2945  *
2946  * Add an entry in the catalog, it may overwrite existing but
2947  * different entries.
2948  *
2949  * Returns 0 if successful, -1 otherwise
2950  */
2951 int
2952 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
2953               const xmlChar * orig, const xmlChar * replace)
2954 {
2955     int res = -1;
2956
2957     if (catal == NULL)
2958         return(-1);
2959
2960     if (catal->type == XML_XML_CATALOG_TYPE) {
2961         res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
2962     } else {
2963         xmlCatalogEntryType cattype;
2964
2965         cattype = xmlGetSGMLCatalogEntryType(type);
2966         if (cattype != XML_CATA_NONE) {
2967             xmlCatalogEntryPtr entry;
2968
2969             entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
2970                                        XML_CATA_PREFER_NONE, NULL);
2971             if (catal->sgml == NULL)
2972                 catal->sgml = xmlHashCreate(10);
2973             res = xmlHashAddEntry(catal->sgml, orig, entry);
2974         }
2975     }
2976     return (res);
2977 }
2978
2979 /**
2980  * xmlACatalogRemove:
2981  * @catal:  a Catalog
2982  * @value:  the value to remove
2983  *
2984  * Remove an entry from the catalog
2985  *
2986  * Returns the number of entries removed if successful, -1 otherwise
2987  */
2988 int
2989 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
2990     int res = -1;
2991
2992     if ((catal == NULL) || (value == NULL))
2993         return(-1);
2994
2995     if (catal->type == XML_XML_CATALOG_TYPE) {
2996         res = xmlDelXMLCatalog(catal->xml, value);
2997     } else {
2998         res = xmlHashRemoveEntry(catal->sgml, value,
2999                 (xmlHashDeallocator) xmlFreeCatalogEntry);
3000         if (res == 0)
3001             res = 1;
3002     } 
3003     return(res);
3004 }
3005
3006 /**
3007  * xmlNewCatalog:
3008  * @sgml:  should this create an SGML catalog
3009  *
3010  * create a new Catalog.
3011  *
3012  * Returns the xmlCatalogPtr or NULL in case of error
3013  */
3014 xmlCatalogPtr
3015 xmlNewCatalog(int sgml) {
3016     xmlCatalogPtr catal = NULL;
3017
3018     if (sgml) {
3019         catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
3020                                     xmlCatalogDefaultPrefer);
3021         if ((catal != NULL) && (catal->sgml == NULL))
3022             catal->sgml = xmlHashCreate(10);
3023     } else
3024         catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3025                                     xmlCatalogDefaultPrefer);
3026     return(catal);
3027 }
3028
3029 /**
3030  * xmlCatalogIsEmpty:
3031  * @catal:  should this create an SGML catalog
3032  *
3033  * Check is a catalog is empty
3034  *
3035  * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
3036  */
3037 int
3038 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
3039     if (catal == NULL)
3040         return(-1);
3041
3042     if (catal->type == XML_XML_CATALOG_TYPE) {
3043         if (catal->xml == NULL)
3044             return(1);
3045         if ((catal->xml->type != XML_CATA_CATALOG) &&
3046             (catal->xml->type != XML_CATA_BROKEN_CATALOG))
3047             return(-1);
3048         if (catal->xml->children == NULL)
3049             return(1);
3050         return(0);
3051     } else {
3052         int res;
3053
3054         if (catal->sgml == NULL)
3055             return(1);
3056         res = xmlHashSize(catal->sgml);
3057         if (res == 0)
3058             return(1);
3059         if (res < 0)
3060             return(-1);
3061     } 
3062     return(0);
3063 }
3064
3065 /************************************************************************
3066  *                                                                      *
3067  *   Public interfaces manipulating the global shared default catalog   *
3068  *                                                                      *
3069  ************************************************************************/
3070
3071 /**
3072  * xmlInitializeCatalogData:
3073  *
3074  * Do the catalog initialization only of global data, doesn't try to load
3075  * any catalog actually.
3076  * this function is not thread safe, catalog initialization should
3077  * preferably be done once at startup
3078  */
3079 static void
3080 xmlInitializeCatalogData(void) {
3081     if (xmlCatalogInitialized != 0)
3082         return;
3083
3084     if (getenv("XML_DEBUG_CATALOG")) 
3085         xmlDebugCatalogs = 1;
3086     xmlCatalogMutex = xmlNewRMutex();
3087
3088     xmlCatalogInitialized = 1;
3089 }
3090 /**
3091  * xmlInitializeCatalog:
3092  *
3093  * Do the catalog initialization.
3094  * this function is not thread safe, catalog initialization should
3095  * preferably be done once at startup
3096  */
3097 void
3098 xmlInitializeCatalog(void) {
3099     if (xmlCatalogInitialized != 0)
3100         return;
3101
3102     xmlInitializeCatalogData();
3103     xmlRMutexLock(xmlCatalogMutex);
3104
3105     if (getenv("XML_DEBUG_CATALOG")) 
3106         xmlDebugCatalogs = 1;
3107
3108     if (xmlDefaultCatalog == NULL) {
3109         const char *catalogs;
3110         char *path;
3111         const char *cur, *paths;
3112         xmlCatalogPtr catal;
3113         xmlCatalogEntryPtr *nextent;
3114
3115         catalogs = (const char *) getenv("XML_CATALOG_FILES");
3116         if (catalogs == NULL)
3117 #if defined(_WIN32) && defined(_MSC_VER)
3118     {
3119                 void* hmodule;
3120                 hmodule = GetModuleHandleA("libxml2.dll");
3121                 if (hmodule == NULL)
3122                         hmodule = GetModuleHandleA(NULL);
3123                 if (hmodule != NULL) {
3124                         char buf[256];
3125                         unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
3126                         if (len != 0) {
3127                                 char* p = &(buf[len]);
3128                                 while (*p != '\\' && p > buf) 
3129                                         p--;
3130                                 if (p != buf) {
3131                                         xmlChar* uri;
3132                                         strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
3133                                         uri = xmlCanonicPath(buf);
3134                                         if (uri != NULL) {
3135                                                 strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
3136                                                 xmlFree(uri);
3137                                         }
3138                                 }
3139                         }
3140                 }
3141                 catalogs = XML_XML_DEFAULT_CATALOG;
3142     }
3143 #else
3144             catalogs = XML_XML_DEFAULT_CATALOG;
3145 #endif
3146
3147         catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, 
3148                 xmlCatalogDefaultPrefer);
3149         if (catal != NULL) {
3150             /* the XML_CATALOG_FILES envvar is allowed to contain a 
3151                space-separated list of entries. */
3152             cur = catalogs;
3153             nextent = &catal->xml;
3154             while (*cur != '\0') {
3155                 while (xmlIsBlank_ch(*cur)) 
3156                     cur++;
3157                 if (*cur != 0) {
3158                     paths = cur;
3159                     while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
3160                         cur++;
3161                     path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
3162                     if (path != NULL) {
3163                         *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3164                                 NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
3165                         if (*nextent != NULL)
3166                             nextent = &((*nextent)->next);
3167                         xmlFree(path);
3168                     }
3169                 }
3170             }
3171             xmlDefaultCatalog = catal;
3172         }
3173     }
3174
3175     xmlRMutexUnlock(xmlCatalogMutex);
3176 }
3177
3178
3179 /**
3180  * xmlLoadCatalog:
3181  * @filename:  a file path
3182  *
3183  * Load the catalog and makes its definitions effective for the default
3184  * external entity loader. It will recurse in SGML CATALOG entries.
3185  * this function is not thread safe, catalog initialization should
3186  * preferably be done once at startup
3187  *
3188  * Returns 0 in case of success -1 in case of error
3189  */
3190 int
3191 xmlLoadCatalog(const char *filename)
3192 {
3193     int ret;
3194     xmlCatalogPtr catal;
3195
3196     if (!xmlCatalogInitialized)
3197         xmlInitializeCatalogData();
3198
3199     xmlRMutexLock(xmlCatalogMutex);
3200
3201     if (xmlDefaultCatalog == NULL) {
3202         catal = xmlLoadACatalog(filename);
3203         if (catal == NULL) {
3204             xmlRMutexUnlock(xmlCatalogMutex);
3205             return(-1);
3206         }
3207
3208         xmlDefaultCatalog = catal;
3209         xmlRMutexUnlock(xmlCatalogMutex);
3210         return(0);
3211     }
3212
3213     ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
3214     xmlRMutexUnlock(xmlCatalogMutex);
3215     return(ret);
3216 }
3217
3218 /**
3219  * xmlLoadCatalogs:
3220  * @pathss:  a list of directories separated by a colon or a space.
3221  *
3222  * Load the catalogs and makes their definitions effective for the default
3223  * external entity loader.
3224  * this function is not thread safe, catalog initialization should
3225  * preferably be done once at startup
3226  */
3227 void
3228 xmlLoadCatalogs(const char *pathss) {
3229     const char *cur;
3230     const char *paths;
3231     xmlChar *path;
3232 #ifdef _WIN32
3233     int i, iLen;
3234 #endif
3235
3236     if (pathss == NULL)
3237         return;
3238
3239     cur = pathss;
3240     while (*cur != 0) {
3241         while (xmlIsBlank_ch(*cur)) cur++;
3242         if (*cur != 0) {
3243             paths = cur;
3244             while ((*cur != 0) && (*cur != PATH_SEAPARATOR) && (!xmlIsBlank_ch(*cur)))
3245                 cur++;
3246             path = xmlStrndup((const xmlChar *)paths, cur - paths);
3247 #ifdef _WIN32
3248         iLen = strlen(path);
3249         for(i = 0; i < iLen; i++) {
3250             if(path[i] == '\\') {
3251                 path[i] = '/';
3252             }
3253         }
3254 #endif
3255             if (path != NULL) {
3256                 xmlLoadCatalog((const char *) path);
3257                 xmlFree(path);
3258             }
3259         }
3260         while (*cur == PATH_SEAPARATOR)
3261             cur++;
3262     }
3263 }
3264
3265 /**
3266  * xmlCatalogCleanup:
3267  *
3268  * Free up all the memory associated with catalogs
3269  */
3270 void
3271 xmlCatalogCleanup(void) {
3272     if (xmlCatalogInitialized == 0)
3273         return;
3274
3275     xmlRMutexLock(xmlCatalogMutex);
3276     if (xmlDebugCatalogs)
3277         xmlGenericError(xmlGenericErrorContext,
3278                 "Catalogs cleanup\n");
3279     if (xmlCatalogXMLFiles != NULL)
3280         xmlHashFree(xmlCatalogXMLFiles, 
3281                     (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
3282     xmlCatalogXMLFiles = NULL;
3283     if (xmlDefaultCatalog != NULL)
3284         xmlFreeCatalog(xmlDefaultCatalog);
3285     xmlDefaultCatalog = NULL;
3286     xmlDebugCatalogs = 0;
3287     xmlCatalogInitialized = 0;
3288     xmlRMutexUnlock(xmlCatalogMutex);
3289     xmlFreeRMutex(xmlCatalogMutex);
3290 }
3291
3292 /**
3293  * xmlCatalogResolveSystem:
3294  * @sysID:  the system ID string
3295  *
3296  * Try to lookup the catalog resource for a system ID
3297  *
3298  * Returns the resource if found or NULL otherwise, the value returned
3299  *      must be freed by the caller.
3300  */
3301 xmlChar *
3302 xmlCatalogResolveSystem(const xmlChar *sysID) {
3303     xmlChar *ret;
3304
3305     if (!xmlCatalogInitialized)
3306         xmlInitializeCatalog();
3307
3308     ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
3309     return(ret);
3310 }
3311
3312 /**
3313  * xmlCatalogResolvePublic:
3314  * @pubID:  the public ID string
3315  *
3316  * Try to lookup the catalog reference associated to a public ID
3317  *
3318  * Returns the resource if found or NULL otherwise, the value returned
3319  *      must be freed by the caller.
3320  */
3321 xmlChar *
3322 xmlCatalogResolvePublic(const xmlChar *pubID) {
3323     xmlChar *ret;
3324
3325     if (!xmlCatalogInitialized)
3326         xmlInitializeCatalog();
3327
3328     ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
3329     return(ret);
3330 }
3331
3332 /**
3333  * xmlCatalogResolve:
3334  * @pubID:  the public ID string
3335  * @sysID:  the system ID string
3336  *
3337  * Do a complete resolution lookup of an External Identifier
3338  *
3339  * Returns the URI of the resource or NULL if not found, it must be freed
3340  *      by the caller.
3341  */
3342 xmlChar *
3343 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
3344     xmlChar *ret;
3345
3346     if (!xmlCatalogInitialized)
3347         xmlInitializeCatalog();
3348
3349     ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
3350     return(ret);
3351 }
3352
3353 /**
3354  * xmlCatalogResolveURI:
3355  * @URI:  the URI
3356  *
3357  * Do a complete resolution lookup of an URI
3358  *
3359  * Returns the URI of the resource or NULL if not found, it must be freed
3360  *      by the caller.
3361  */
3362 xmlChar *
3363 xmlCatalogResolveURI(const xmlChar *URI) {
3364     xmlChar *ret;
3365
3366     if (!xmlCatalogInitialized)
3367         xmlInitializeCatalog();
3368
3369     ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
3370     return(ret);
3371 }
3372
3373 #ifdef LIBXML_OUTPUT_ENABLED
3374 /**
3375  * xmlCatalogDump:
3376  * @out:  the file.
3377  *
3378  * Dump all the global catalog content to the given file.
3379  */
3380 void
3381 xmlCatalogDump(FILE *out) {
3382     if (out == NULL)
3383         return;
3384
3385     if (!xmlCatalogInitialized)
3386         xmlInitializeCatalog();
3387
3388     xmlACatalogDump(xmlDefaultCatalog, out);
3389 }
3390 #endif /* LIBXML_OUTPUT_ENABLED */
3391
3392 /**
3393  * xmlCatalogAdd:
3394  * @type:  the type of record to add to the catalog
3395  * @orig:  the system, public or prefix to match 
3396  * @replace:  the replacement value for the match
3397  *
3398  * Add an entry in the catalog, it may overwrite existing but
3399  * different entries.
3400  * If called before any other catalog routine, allows to override the
3401  * default shared catalog put in place by xmlInitializeCatalog();
3402  *
3403  * Returns 0 if successful, -1 otherwise
3404  */
3405 int
3406 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
3407     int res = -1;
3408
3409     if (!xmlCatalogInitialized)
3410         xmlInitializeCatalogData();
3411
3412     xmlRMutexLock(xmlCatalogMutex);
3413     /*
3414      * Specific case where one want to override the default catalog
3415      * put in place by xmlInitializeCatalog();
3416      */
3417     if ((xmlDefaultCatalog == NULL) &&
3418         (xmlStrEqual(type, BAD_CAST "catalog"))) {
3419         xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
3420                                           xmlCatalogDefaultPrefer);
3421         xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
3422                                     orig, NULL,  xmlCatalogDefaultPrefer, NULL);
3423
3424         xmlRMutexUnlock(xmlCatalogMutex);
3425         return(0);
3426     } 
3427
3428     res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
3429     xmlRMutexUnlock(xmlCatalogMutex);
3430     return(res);
3431 }
3432
3433 /**
3434  * xmlCatalogRemove:
3435  * @value:  the value to remove
3436  *
3437  * Remove an entry from the catalog
3438  *
3439  * Returns the number of entries removed if successful, -1 otherwise
3440  */
3441 int
3442 xmlCatalogRemove(const xmlChar *value) {
3443     int res;
3444
3445     if (!xmlCatalogInitialized)
3446         xmlInitializeCatalog();
3447
3448     xmlRMutexLock(xmlCatalogMutex);
3449     res = xmlACatalogRemove(xmlDefaultCatalog, value);
3450     xmlRMutexUnlock(xmlCatalogMutex);
3451     return(res);
3452 }
3453
3454 /**
3455  * xmlCatalogConvert:
3456  *
3457  * Convert all the SGML catalog entries as XML ones
3458  *
3459  * Returns the number of entries converted if successful, -1 otherwise
3460  */
3461 int
3462 xmlCatalogConvert(void) {
3463     int res = -1;
3464
3465     if (!xmlCatalogInitialized)
3466         xmlInitializeCatalog();
3467
3468     xmlRMutexLock(xmlCatalogMutex);
3469     res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
3470     xmlRMutexUnlock(xmlCatalogMutex);
3471     return(res);
3472 }
3473
3474 /************************************************************************
3475  *                                                                      *
3476  *      Public interface manipulating the common preferences            *
3477  *                                                                      *
3478  ************************************************************************/
3479
3480 /**
3481  * xmlCatalogGetDefaults:
3482  *
3483  * Used to get the user preference w.r.t. to what catalogs should
3484  * be accepted
3485  *
3486  * Returns the current xmlCatalogAllow value
3487  */
3488 xmlCatalogAllow
3489 xmlCatalogGetDefaults(void) {
3490     return(xmlCatalogDefaultAllow);
3491 }
3492
3493 /**
3494  * xmlCatalogSetDefaults:
3495  * @allow:  what catalogs should be accepted
3496  *
3497  * Used to set the user preference w.r.t. to what catalogs should
3498  * be accepted
3499  */
3500 void
3501 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
3502     if (xmlDebugCatalogs) {
3503         switch (allow) {
3504             case XML_CATA_ALLOW_NONE:
3505                 xmlGenericError(xmlGenericErrorContext,
3506                         "Disabling catalog usage\n");
3507                 break;
3508             case XML_CATA_ALLOW_GLOBAL:
3509                 xmlGenericError(xmlGenericErrorContext,
3510                         "Allowing only global catalogs\n");
3511                 break;
3512             case XML_CATA_ALLOW_DOCUMENT:
3513                 xmlGenericError(xmlGenericErrorContext,
3514                         "Allowing only catalogs from the document\n");
3515                 break;
3516             case XML_CATA_ALLOW_ALL:
3517                 xmlGenericError(xmlGenericErrorContext,
3518                         "Allowing all catalogs\n");
3519                 break;
3520         }
3521     }
3522     xmlCatalogDefaultAllow = allow;
3523 }
3524
3525 /**
3526  * xmlCatalogSetDefaultPrefer:
3527  * @prefer:  the default preference for delegation
3528  *
3529  * Allows to set the preference between public and system for deletion
3530  * in XML Catalog resolution. C.f. section 4.1.1 of the spec
3531  * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
3532  *
3533  * Returns the previous value of the default preference for delegation
3534  */
3535 xmlCatalogPrefer
3536 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
3537     xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
3538
3539     if (prefer == XML_CATA_PREFER_NONE)
3540         return(ret);
3541
3542     if (xmlDebugCatalogs) {
3543         switch (prefer) {
3544             case XML_CATA_PREFER_PUBLIC:
3545                 xmlGenericError(xmlGenericErrorContext,
3546                         "Setting catalog preference to PUBLIC\n");
3547                 break;
3548             case XML_CATA_PREFER_SYSTEM:
3549                 xmlGenericError(xmlGenericErrorContext,
3550                         "Setting catalog preference to SYSTEM\n");
3551                 break;
3552             case XML_CATA_PREFER_NONE:
3553                 break;
3554         }
3555     }
3556     xmlCatalogDefaultPrefer = prefer;
3557     return(ret);
3558 }
3559
3560 /**
3561  * xmlCatalogSetDebug:
3562  * @level:  the debug level of catalogs required
3563  *
3564  * Used to set the debug level for catalog operation, 0 disable
3565  * debugging, 1 enable it
3566  *
3567  * Returns the previous value of the catalog debugging level
3568  */
3569 int
3570 xmlCatalogSetDebug(int level) {
3571     int ret = xmlDebugCatalogs;
3572
3573     if (level <= 0)
3574         xmlDebugCatalogs = 0;
3575     else
3576         xmlDebugCatalogs = level;
3577     return(ret);
3578 }
3579
3580 /************************************************************************
3581  *                                                                      *
3582  *   Minimal interfaces used for per-document catalogs by the parser    *
3583  *                                                                      *
3584  ************************************************************************/
3585
3586 /**
3587  * xmlCatalogFreeLocal:
3588  * @catalogs:  a document's list of catalogs
3589  *
3590  * Free up the memory associated to the catalog list
3591  */
3592 void
3593 xmlCatalogFreeLocal(void *catalogs) {
3594     xmlCatalogEntryPtr catal;
3595
3596     if (!xmlCatalogInitialized)
3597         xmlInitializeCatalog();
3598
3599     catal = (xmlCatalogEntryPtr) catalogs;
3600     if (catal != NULL)
3601         xmlFreeCatalogEntryList(catal);
3602 }
3603
3604
3605 /**
3606  * xmlCatalogAddLocal:
3607  * @catalogs:  a document's list of catalogs
3608  * @URL:  the URL to a new local catalog
3609  *
3610  * Add the new entry to the catalog list
3611  *
3612  * Returns the updated list
3613  */
3614 void *  
3615 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
3616     xmlCatalogEntryPtr catal, add;
3617
3618     if (!xmlCatalogInitialized)
3619         xmlInitializeCatalog();
3620
3621     if (URL == NULL)
3622         return(catalogs);
3623
3624     if (xmlDebugCatalogs)
3625         xmlGenericError(xmlGenericErrorContext,
3626                 "Adding document catalog %s\n", URL);
3627
3628     add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
3629                              xmlCatalogDefaultPrefer, NULL);
3630     if (add == NULL)
3631         return(catalogs);
3632
3633     catal = (xmlCatalogEntryPtr) catalogs;
3634     if (catal == NULL) 
3635         return((void *) add);
3636
3637     while (catal->next != NULL)
3638         catal = catal->next;
3639     catal->next = add;
3640     return(catalogs);
3641 }
3642
3643 /**
3644  * xmlCatalogLocalResolve:
3645  * @catalogs:  a document's list of catalogs
3646  * @pubID:  the public ID string
3647  * @sysID:  the system ID string
3648  *
3649  * Do a complete resolution lookup of an External Identifier using a 
3650  * document's private catalog list
3651  *
3652  * Returns the URI of the resource or NULL if not found, it must be freed
3653  *      by the caller.
3654  */
3655 xmlChar *
3656 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
3657                        const xmlChar *sysID) {
3658     xmlCatalogEntryPtr catal;
3659     xmlChar *ret;
3660
3661     if (!xmlCatalogInitialized)
3662         xmlInitializeCatalog();
3663
3664     if ((pubID == NULL) && (sysID == NULL))
3665         return(NULL);
3666
3667     if (xmlDebugCatalogs) {
3668         if ((pubID != NULL) && (sysID != NULL)) {
3669             xmlGenericError(xmlGenericErrorContext,
3670                             "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
3671         } else if (pubID != NULL) {
3672             xmlGenericError(xmlGenericErrorContext,
3673                             "Local Resolve: pubID %s\n", pubID);
3674         } else {
3675             xmlGenericError(xmlGenericErrorContext,
3676                             "Local Resolve: sysID %s\n", sysID);
3677         }
3678     }
3679
3680     catal = (xmlCatalogEntryPtr) catalogs;
3681     if (catal == NULL)
3682         return(NULL);
3683     ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
3684     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3685         return(ret);
3686     return(NULL);
3687 }
3688
3689 /**
3690  * xmlCatalogLocalResolveURI:
3691  * @catalogs:  a document's list of catalogs
3692  * @URI:  the URI
3693  *
3694  * Do a complete resolution lookup of an URI using a 
3695  * document's private catalog list
3696  *
3697  * Returns the URI of the resource or NULL if not found, it must be freed
3698  *      by the caller.
3699  */
3700 xmlChar *
3701 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
3702     xmlCatalogEntryPtr catal;
3703     xmlChar *ret;
3704
3705     if (!xmlCatalogInitialized)
3706         xmlInitializeCatalog();
3707
3708     if (URI == NULL)
3709         return(NULL);
3710
3711     if (xmlDebugCatalogs)
3712         xmlGenericError(xmlGenericErrorContext,
3713                 "Resolve URI %s\n", URI);
3714
3715     catal = (xmlCatalogEntryPtr) catalogs;
3716     if (catal == NULL)
3717         return(NULL);
3718     ret = xmlCatalogListXMLResolveURI(catal, URI);
3719     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
3720         return(ret);
3721     return(NULL);
3722 }
3723
3724 /************************************************************************
3725  *                                                                      *
3726  *                      Deprecated interfaces                           *
3727  *                                                                      *
3728  ************************************************************************/
3729 /**
3730  * xmlCatalogGetSystem:
3731  * @sysID:  the system ID string
3732  *
3733  * Try to lookup the catalog reference associated to a system ID
3734  * DEPRECATED, use xmlCatalogResolveSystem()
3735  *
3736  * Returns the resource if found or NULL otherwise.
3737  */
3738 const xmlChar *
3739 xmlCatalogGetSystem(const xmlChar *sysID) {
3740     xmlChar *ret;
3741     static xmlChar result[1000];
3742     static int msg = 0;
3743
3744     if (!xmlCatalogInitialized)
3745         xmlInitializeCatalog();
3746
3747     if (msg == 0) {
3748         xmlGenericError(xmlGenericErrorContext,
3749                 "Use of deprecated xmlCatalogGetSystem() call\n");
3750         msg++;
3751     }
3752
3753     if (sysID == NULL)
3754         return(NULL);
3755     
3756     /*
3757      * Check first the XML catalogs
3758      */
3759     if (xmlDefaultCatalog != NULL) {
3760         ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
3761         if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3762             snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3763             result[sizeof(result) - 1] = 0;
3764             return(result);
3765         }
3766     }
3767
3768     if (xmlDefaultCatalog != NULL)
3769         return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
3770     return(NULL);
3771 }
3772
3773 /**
3774  * xmlCatalogGetPublic:
3775  * @pubID:  the public ID string
3776  *
3777  * Try to lookup the catalog reference associated to a public ID
3778  * DEPRECATED, use xmlCatalogResolvePublic()
3779  *
3780  * Returns the resource if found or NULL otherwise.
3781  */
3782 const xmlChar *
3783 xmlCatalogGetPublic(const xmlChar *pubID) {
3784     xmlChar *ret;
3785     static xmlChar result[1000];
3786     static int msg = 0;
3787
3788     if (!xmlCatalogInitialized)
3789         xmlInitializeCatalog();
3790
3791     if (msg == 0) {
3792         xmlGenericError(xmlGenericErrorContext,
3793                 "Use of deprecated xmlCatalogGetPublic() call\n");
3794         msg++;
3795     }
3796
3797     if (pubID == NULL)
3798         return(NULL);
3799     
3800     /*
3801      * Check first the XML catalogs
3802      */
3803     if (xmlDefaultCatalog != NULL) {
3804         ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
3805         if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
3806             snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
3807             result[sizeof(result) - 1] = 0;
3808             return(result);
3809         }
3810     }
3811
3812     if (xmlDefaultCatalog != NULL)
3813         return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
3814     return(NULL);
3815 }
3816
3817 #define bottom_catalog
3818 #include "elfgcchack.h"
3819 #endif /* LIBXML_CATALOG_ENABLED */