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