source: trunk/third/libxml2/catalog.c @ 21532

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