Paul Cercueil / libxml2

Dependents:   libiio

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers catalog.c Source File

catalog.c

00001 /**
00002  * catalog.c: set of generic Catalog related routines
00003  *
00004  * Reference:  SGML Open Technical Resolution TR9401:1997.
00005  *             http://www.jclark.com/sp/catalog.htm
00006  *
00007  *             XML Catalogs Working Draft 06 August 2001
00008  *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
00009  *
00010  * See Copyright for the status of this software.
00011  *
00012  * Daniel.Veillard@imag.fr
00013  */
00014 
00015 #define IN_LIBXML
00016 #include "libxml.h"
00017 
00018 #ifdef LIBXML_CATALOG_ENABLED
00019 #ifdef HAVE_SYS_TYPES_H
00020 #include <sys/types.h>
00021 #endif
00022 #ifdef HAVE_SYS_STAT_H
00023 #include <sys/stat.h>
00024 #endif
00025 #ifdef HAVE_UNISTD_H
00026 #include <unistd.h>
00027 #endif
00028 #ifdef HAVE_FCNTL_H
00029 #include <fcntl.h>
00030 #endif
00031 #ifdef HAVE_STDLIB_H
00032 #include <stdlib.h>
00033 #endif
00034 #include <string.h>
00035 #include <libxml/xmlmemory.h>
00036 #include <libxml/hash.h>
00037 #include <libxml/uri.h>
00038 #include <libxml/parserInternals.h>
00039 #include <libxml/catalog.h>
00040 #include <libxml/xmlerror.h>
00041 #include <libxml/threads.h>
00042 #include <libxml/globals.h>
00043 
00044 #include "buf.h"
00045 
00046 #define MAX_DELEGATE    50
00047 #define MAX_CATAL_DEPTH 50
00048 
00049 #ifdef _WIN32
00050 # define PATH_SEAPARATOR ';'
00051 #else
00052 # define PATH_SEAPARATOR ':'
00053 #endif
00054 
00055 /**
00056  * TODO:
00057  *
00058  * macro to flag unimplemented blocks
00059  * XML_CATALOG_PREFER user env to select between system/public prefered
00060  * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
00061  *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
00062  *> values "system" and "public".  I have made the default be "system" to
00063  *> match yours.
00064  */
00065 #define TODO                                \
00066     xmlGenericError(xmlGenericErrorContext,             \
00067         "Unimplemented block at %s:%d\n",               \
00068             __FILE__, __LINE__);
00069 
00070 #define XML_URN_PUBID "urn:publicid:"
00071 #define XML_CATAL_BREAK ((xmlChar *) -1)
00072 #ifndef XML_XML_DEFAULT_CATALOG
00073 #define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
00074 #endif
00075 #ifndef XML_SGML_DEFAULT_CATALOG
00076 #define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog"
00077 #endif
00078 
00079 #if defined(_WIN32) && defined(_MSC_VER)
00080 #undef XML_XML_DEFAULT_CATALOG
00081 static char XML_XML_DEFAULT_CATALOG[256] = "file:///etc/xml/catalog";
00082 #if defined(_WIN32_WCE)
00083 /* Windows CE don't have a A variant */
00084 #define GetModuleHandleA GetModuleHandle
00085 #define GetModuleFileNameA GetModuleFileName
00086 #else
00087 #if !defined(_WINDOWS_)
00088 void* __stdcall GetModuleHandleA(const char*);
00089 unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
00090 #endif
00091 #endif
00092 #endif
00093 
00094 static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
00095 static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
00096 
00097 /************************************************************************
00098  *                                  *
00099  *          Types, all private              *
00100  *                                  *
00101  ************************************************************************/
00102 
00103 typedef enum {
00104     XML_CATA_REMOVED = -1,
00105     XML_CATA_NONE = 0,
00106     XML_CATA_CATALOG,
00107     XML_CATA_BROKEN_CATALOG,
00108     XML_CATA_NEXT_CATALOG,
00109     XML_CATA_GROUP,
00110     XML_CATA_PUBLIC,
00111     XML_CATA_SYSTEM,
00112     XML_CATA_REWRITE_SYSTEM,
00113     XML_CATA_DELEGATE_PUBLIC,
00114     XML_CATA_DELEGATE_SYSTEM,
00115     XML_CATA_URI,
00116     XML_CATA_REWRITE_URI,
00117     XML_CATA_DELEGATE_URI,
00118     SGML_CATA_SYSTEM,
00119     SGML_CATA_PUBLIC,
00120     SGML_CATA_ENTITY,
00121     SGML_CATA_PENTITY,
00122     SGML_CATA_DOCTYPE,
00123     SGML_CATA_LINKTYPE,
00124     SGML_CATA_NOTATION,
00125     SGML_CATA_DELEGATE,
00126     SGML_CATA_BASE,
00127     SGML_CATA_CATALOG,
00128     SGML_CATA_DOCUMENT,
00129     SGML_CATA_SGMLDECL
00130 } xmlCatalogEntryType;
00131 
00132 typedef struct _xmlCatalogEntry xmlCatalogEntry;
00133 typedef xmlCatalogEntry *xmlCatalogEntryPtr;
00134 struct _xmlCatalogEntry {
00135     struct _xmlCatalogEntry *next;
00136     struct _xmlCatalogEntry *parent;
00137     struct _xmlCatalogEntry *children;
00138     xmlCatalogEntryType type;
00139     xmlChar *name;
00140     xmlChar *value;
00141     xmlChar *URL;  /* The expanded URL using the base */
00142     xmlCatalogPrefer prefer;
00143     int dealloc;
00144     int depth;
00145     struct _xmlCatalogEntry *group;
00146 };
00147 
00148 typedef enum {
00149     XML_XML_CATALOG_TYPE = 1,
00150     XML_SGML_CATALOG_TYPE
00151 } xmlCatalogType;
00152 
00153 #define XML_MAX_SGML_CATA_DEPTH 10
00154 struct _xmlCatalog {
00155     xmlCatalogType type;    /* either XML or SGML */
00156 
00157     /*
00158      * SGML Catalogs are stored as a simple hash table of catalog entries
00159      * Catalog stack to check against overflows when building the
00160      * SGML catalog
00161      */
00162     char *catalTab[XML_MAX_SGML_CATA_DEPTH];    /* stack of catals */
00163     int          catalNr;   /* Number of current catal streams */
00164     int          catalMax;  /* Max number of catal streams */
00165     xmlHashTablePtr sgml;
00166 
00167     /*
00168      * XML Catalogs are stored as a tree of Catalog entries
00169      */
00170     xmlCatalogPrefer prefer;
00171     xmlCatalogEntryPtr xml;
00172 };
00173 
00174 /************************************************************************
00175  *                                  *
00176  *          Global variables                *
00177  *                                  *
00178  ************************************************************************/
00179 
00180 /*
00181  * Those are preferences
00182  */
00183 static int xmlDebugCatalogs = 0;   /* used for debugging */
00184 static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
00185 static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
00186 
00187 /*
00188  * Hash table containing all the trees of XML catalogs parsed by
00189  * the application.
00190  */
00191 static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
00192 
00193 /*
00194  * The default catalog in use by the application
00195  */
00196 static xmlCatalogPtr xmlDefaultCatalog = NULL;
00197 
00198 /*
00199  * A mutex for modifying the shared global catalog(s)
00200  * xmlDefaultCatalog tree.
00201  * It also protects xmlCatalogXMLFiles
00202  * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
00203  */
00204 static xmlRMutexPtr xmlCatalogMutex = NULL;
00205 
00206 /*
00207  * Whether the catalog support was initialized.
00208  */
00209 static int xmlCatalogInitialized = 0;
00210 
00211 /************************************************************************
00212  *                                  *
00213  *          Catalog error handlers              *
00214  *                                  *
00215  ************************************************************************/
00216 
00217 /**
00218  * xmlCatalogErrMemory:
00219  * @extra:  extra informations
00220  *
00221  * Handle an out of memory condition
00222  */
00223 static void
00224 xmlCatalogErrMemory(const char *extra)
00225 {
00226     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
00227                     XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
00228             extra, NULL, NULL, 0, 0,
00229             "Memory allocation failed : %s\n", extra);
00230 }
00231 
00232 /**
00233  * xmlCatalogErr:
00234  * @catal: the Catalog entry
00235  * @node: the context node
00236  * @msg:  the error message
00237  * @extra:  extra informations
00238  *
00239  * Handle a catalog error
00240  */
00241 static void
00242 xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
00243                const char *msg, const xmlChar *str1, const xmlChar *str2,
00244            const xmlChar *str3)
00245 {
00246     __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
00247                     error, XML_ERR_ERROR, NULL, 0,
00248             (const char *) str1, (const char *) str2,
00249             (const char *) str3, 0, 0,
00250             msg, str1, str2, str3);
00251 }
00252 
00253 
00254 /************************************************************************
00255  *                                  *
00256  *          Allocation and Freeing              *
00257  *                                  *
00258  ************************************************************************/
00259 
00260 /**
00261  * xmlNewCatalogEntry:
00262  * @type:  type of entry
00263  * @name:  name of the entry
00264  * @value:  value of the entry
00265  * @prefer:  the PUBLIC vs. SYSTEM current preference value
00266  * @group:  for members of a group, the group entry
00267  *
00268  * create a new Catalog entry, this type is shared both by XML and
00269  * SGML catalogs, but the acceptable types values differs.
00270  *
00271  * Returns the xmlCatalogEntryPtr or NULL in case of error
00272  */
00273 static xmlCatalogEntryPtr
00274 xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
00275        const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
00276        xmlCatalogEntryPtr group) {
00277     xmlCatalogEntryPtr ret;
00278     xmlChar *normid = NULL;
00279 
00280     ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
00281     if (ret == NULL) {
00282         xmlCatalogErrMemory("allocating catalog entry");
00283     return(NULL);
00284     }
00285     ret->next = NULL;
00286     ret->parent = NULL;
00287     ret->children = NULL;
00288     ret->type = type;
00289     if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
00290         normid = xmlCatalogNormalizePublic(name);
00291         if (normid != NULL)
00292             name = (*normid != 0 ? normid : NULL);
00293     }
00294     if (name != NULL)
00295     ret->name = xmlStrdup(name);
00296     else
00297     ret->name = NULL;
00298     if (normid != NULL)
00299         xmlFree(normid);
00300     if (value != NULL)
00301     ret->value = xmlStrdup(value);
00302     else
00303     ret->value = NULL;
00304     if (URL == NULL)
00305     URL = value;
00306     if (URL != NULL)
00307     ret->URL = xmlStrdup(URL);
00308     else
00309     ret->URL = NULL;
00310     ret->prefer = prefer;
00311     ret->dealloc = 0;
00312     ret->depth = 0;
00313     ret->group = group;
00314     return(ret);
00315 }
00316 
00317 static void
00318 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
00319 
00320 /**
00321  * xmlFreeCatalogEntry:
00322  * @ret:  a Catalog entry
00323  *
00324  * Free the memory allocated to a Catalog entry
00325  */
00326 static void
00327 xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
00328     if (ret == NULL)
00329     return;
00330     /*
00331      * Entries stored in the file hash must be deallocated
00332      * only by the file hash cleaner !
00333      */
00334     if (ret->dealloc == 1)
00335     return;
00336 
00337     if (xmlDebugCatalogs) {
00338     if (ret->name != NULL)
00339         xmlGenericError(xmlGenericErrorContext,
00340             "Free catalog entry %s\n", ret->name);
00341     else if (ret->value != NULL)
00342         xmlGenericError(xmlGenericErrorContext,
00343             "Free catalog entry %s\n", ret->value);
00344     else
00345         xmlGenericError(xmlGenericErrorContext,
00346             "Free catalog entry\n");
00347     }
00348 
00349     if (ret->name != NULL)
00350     xmlFree(ret->name);
00351     if (ret->value != NULL)
00352     xmlFree(ret->value);
00353     if (ret->URL != NULL)
00354     xmlFree(ret->URL);
00355     xmlFree(ret);
00356 }
00357 
00358 /**
00359  * xmlFreeCatalogEntryList:
00360  * @ret:  a Catalog entry list
00361  *
00362  * Free the memory allocated to a full chained list of Catalog entries
00363  */
00364 static void
00365 xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
00366     xmlCatalogEntryPtr next;
00367 
00368     while (ret != NULL) {
00369     next = ret->next;
00370     xmlFreeCatalogEntry(ret);
00371     ret = next;
00372     }
00373 }
00374 
00375 /**
00376  * xmlFreeCatalogHashEntryList:
00377  * @ret:  a Catalog entry list
00378  *
00379  * Free the memory allocated to list of Catalog entries from the
00380  * catalog file hash.
00381  */
00382 static void
00383 xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) {
00384     xmlCatalogEntryPtr children, next;
00385 
00386     if (catal == NULL)
00387     return;
00388 
00389     children = catal->children;
00390     while (children != NULL) {
00391     next = children->next;
00392     children->dealloc = 0;
00393     children->children = NULL;
00394     xmlFreeCatalogEntry(children);
00395     children = next;
00396     }
00397     catal->dealloc = 0;
00398     xmlFreeCatalogEntry(catal);
00399 }
00400 
00401 /**
00402  * xmlCreateNewCatalog:
00403  * @type:  type of catalog
00404  * @prefer:  the PUBLIC vs. SYSTEM current preference value
00405  *
00406  * create a new Catalog, this type is shared both by XML and
00407  * SGML catalogs, but the acceptable types values differs.
00408  *
00409  * Returns the xmlCatalogPtr or NULL in case of error
00410  */
00411 static xmlCatalogPtr
00412 xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
00413     xmlCatalogPtr ret;
00414 
00415     ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
00416     if (ret == NULL) {
00417         xmlCatalogErrMemory("allocating catalog");
00418     return(NULL);
00419     }
00420     memset(ret, 0, sizeof(xmlCatalog));
00421     ret->type = type;
00422     ret->catalNr = 0;
00423     ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
00424     ret->prefer = prefer;
00425     if (ret->type == XML_SGML_CATALOG_TYPE)
00426     ret->sgml = xmlHashCreate(10);
00427     return(ret);
00428 }
00429 
00430 /**
00431  * xmlFreeCatalog:
00432  * @catal:  a Catalog
00433  *
00434  * Free the memory allocated to a Catalog
00435  */
00436 void
00437 xmlFreeCatalog(xmlCatalogPtr catal) {
00438     if (catal == NULL)
00439     return;
00440     if (catal->xml != NULL)
00441     xmlFreeCatalogEntryList(catal->xml);
00442     if (catal->sgml != NULL)
00443     xmlHashFree(catal->sgml,
00444         (xmlHashDeallocator) xmlFreeCatalogEntry);
00445     xmlFree(catal);
00446 }
00447 
00448 /************************************************************************
00449  *                                  *
00450  *          Serializing Catalogs                *
00451  *                                  *
00452  ************************************************************************/
00453 
00454 #ifdef LIBXML_OUTPUT_ENABLED
00455 /**
00456  * xmlCatalogDumpEntry:
00457  * @entry:  the catalog entry
00458  * @out:  the file.
00459  *
00460  * Serialize an SGML Catalog entry
00461  */
00462 static void
00463 xmlCatalogDumpEntry(xmlCatalogEntryPtr entry, FILE *out) {
00464     if ((entry == NULL) || (out == NULL))
00465     return;
00466     switch (entry->type) {
00467     case SGML_CATA_ENTITY:
00468         fprintf(out, "ENTITY "); break;
00469     case SGML_CATA_PENTITY:
00470         fprintf(out, "ENTITY %%"); break;
00471     case SGML_CATA_DOCTYPE:
00472         fprintf(out, "DOCTYPE "); break;
00473     case SGML_CATA_LINKTYPE:
00474         fprintf(out, "LINKTYPE "); break;
00475     case SGML_CATA_NOTATION:
00476         fprintf(out, "NOTATION "); break;
00477     case SGML_CATA_PUBLIC:
00478         fprintf(out, "PUBLIC "); break;
00479     case SGML_CATA_SYSTEM:
00480         fprintf(out, "SYSTEM "); break;
00481     case SGML_CATA_DELEGATE:
00482         fprintf(out, "DELEGATE "); break;
00483     case SGML_CATA_BASE:
00484         fprintf(out, "BASE "); break;
00485     case SGML_CATA_CATALOG:
00486         fprintf(out, "CATALOG "); break;
00487     case SGML_CATA_DOCUMENT:
00488         fprintf(out, "DOCUMENT "); break;
00489     case SGML_CATA_SGMLDECL:
00490         fprintf(out, "SGMLDECL "); break;
00491     default:
00492         return;
00493     }
00494     switch (entry->type) {
00495     case SGML_CATA_ENTITY:
00496     case SGML_CATA_PENTITY:
00497     case SGML_CATA_DOCTYPE:
00498     case SGML_CATA_LINKTYPE:
00499     case SGML_CATA_NOTATION:
00500         fprintf(out, "%s", (const char *) entry->name); break;
00501     case SGML_CATA_PUBLIC:
00502     case SGML_CATA_SYSTEM:
00503     case SGML_CATA_SGMLDECL:
00504     case SGML_CATA_DOCUMENT:
00505     case SGML_CATA_CATALOG:
00506     case SGML_CATA_BASE:
00507     case SGML_CATA_DELEGATE:
00508         fprintf(out, "\"%s\"", entry->name); break;
00509     default:
00510         break;
00511     }
00512     switch (entry->type) {
00513     case SGML_CATA_ENTITY:
00514     case SGML_CATA_PENTITY:
00515     case SGML_CATA_DOCTYPE:
00516     case SGML_CATA_LINKTYPE:
00517     case SGML_CATA_NOTATION:
00518     case SGML_CATA_PUBLIC:
00519     case SGML_CATA_SYSTEM:
00520     case SGML_CATA_DELEGATE:
00521         fprintf(out, " \"%s\"", entry->value); break;
00522     default:
00523         break;
00524     }
00525     fprintf(out, "\n");
00526 }
00527 
00528 /**
00529  * xmlDumpXMLCatalogNode:
00530  * @catal:  top catalog entry
00531  * @catalog: pointer to the xml tree
00532  * @doc: the containing document
00533  * @ns: the current namespace
00534  * @cgroup: group node for group members
00535  *
00536  * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
00537  * for group entries
00538  */
00539 static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
00540             xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
00541     xmlNodePtr node;
00542     xmlCatalogEntryPtr cur;
00543     /*
00544      * add all the catalog entries
00545      */
00546     cur = catal;
00547     while (cur != NULL) {
00548         if (cur->group == cgroup) {
00549         switch (cur->type) {
00550             case XML_CATA_REMOVED:
00551             break;
00552             case XML_CATA_BROKEN_CATALOG:
00553             case XML_CATA_CATALOG:
00554             if (cur == catal) {
00555             cur = cur->children;
00556                 continue;
00557             }
00558             break;
00559         case XML_CATA_NEXT_CATALOG:
00560             node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
00561             xmlSetProp(node, BAD_CAST "catalog", cur->value);
00562             xmlAddChild(catalog, node);
00563                     break;
00564         case XML_CATA_NONE:
00565             break;
00566         case XML_CATA_GROUP:
00567             node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
00568             xmlSetProp(node, BAD_CAST "id", cur->name);
00569             if (cur->value != NULL) {
00570                 xmlNsPtr xns;
00571             xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
00572             if (xns != NULL)
00573                 xmlSetNsProp(node, xns, BAD_CAST "base",
00574                      cur->value);
00575             }
00576             switch (cur->prefer) {
00577             case XML_CATA_PREFER_NONE:
00578                     break;
00579             case XML_CATA_PREFER_PUBLIC:
00580                     xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
00581                 break;
00582             case XML_CATA_PREFER_SYSTEM:
00583                     xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
00584                 break;
00585             }
00586             xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
00587             xmlAddChild(catalog, node);
00588                 break;
00589         case XML_CATA_PUBLIC:
00590             node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
00591             xmlSetProp(node, BAD_CAST "publicId", cur->name);
00592             xmlSetProp(node, BAD_CAST "uri", cur->value);
00593             xmlAddChild(catalog, node);
00594             break;
00595         case XML_CATA_SYSTEM:
00596             node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
00597             xmlSetProp(node, BAD_CAST "systemId", cur->name);
00598             xmlSetProp(node, BAD_CAST "uri", cur->value);
00599             xmlAddChild(catalog, node);
00600             break;
00601         case XML_CATA_REWRITE_SYSTEM:
00602             node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
00603             xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
00604             xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
00605             xmlAddChild(catalog, node);
00606             break;
00607         case XML_CATA_DELEGATE_PUBLIC:
00608             node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
00609             xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
00610             xmlSetProp(node, BAD_CAST "catalog", cur->value);
00611             xmlAddChild(catalog, node);
00612             break;
00613         case XML_CATA_DELEGATE_SYSTEM:
00614             node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
00615             xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
00616             xmlSetProp(node, BAD_CAST "catalog", cur->value);
00617             xmlAddChild(catalog, node);
00618             break;
00619         case XML_CATA_URI:
00620             node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
00621             xmlSetProp(node, BAD_CAST "name", cur->name);
00622             xmlSetProp(node, BAD_CAST "uri", cur->value);
00623             xmlAddChild(catalog, node);
00624             break;
00625         case XML_CATA_REWRITE_URI:
00626             node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
00627             xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
00628             xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
00629             xmlAddChild(catalog, node);
00630             break;
00631         case XML_CATA_DELEGATE_URI:
00632             node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
00633             xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
00634             xmlSetProp(node, BAD_CAST "catalog", cur->value);
00635             xmlAddChild(catalog, node);
00636             break;
00637         case SGML_CATA_SYSTEM:
00638         case SGML_CATA_PUBLIC:
00639         case SGML_CATA_ENTITY:
00640         case SGML_CATA_PENTITY:
00641         case SGML_CATA_DOCTYPE:
00642         case SGML_CATA_LINKTYPE:
00643         case SGML_CATA_NOTATION:
00644         case SGML_CATA_DELEGATE:
00645         case SGML_CATA_BASE:
00646         case SGML_CATA_CATALOG:
00647         case SGML_CATA_DOCUMENT:
00648         case SGML_CATA_SGMLDECL:
00649             break;
00650         }
00651         }
00652     cur = cur->next;
00653     }
00654 }
00655 
00656 static int
00657 xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
00658     int ret;
00659     xmlDocPtr doc;
00660     xmlNsPtr ns;
00661     xmlDtdPtr dtd;
00662     xmlNodePtr catalog;
00663     xmlOutputBufferPtr buf;
00664 
00665     /*
00666      * Rebuild a catalog
00667      */
00668     doc = xmlNewDoc(NULL);
00669     if (doc == NULL)
00670     return(-1);
00671     dtd = xmlNewDtd(doc, BAD_CAST "catalog",
00672            BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
00673 BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
00674 
00675     xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
00676 
00677     ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
00678     if (ns == NULL) {
00679     xmlFreeDoc(doc);
00680     return(-1);
00681     }
00682     catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
00683     if (catalog == NULL) {
00684     xmlFreeNs(ns);
00685     xmlFreeDoc(doc);
00686     return(-1);
00687     }
00688     catalog->nsDef = ns;
00689     xmlAddChild((xmlNodePtr) doc, catalog);
00690 
00691     xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
00692 
00693     /*
00694      * reserialize it
00695      */
00696     buf = xmlOutputBufferCreateFile(out, NULL);
00697     if (buf == NULL) {
00698     xmlFreeDoc(doc);
00699     return(-1);
00700     }
00701     ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
00702 
00703     /*
00704      * Free it
00705      */
00706     xmlFreeDoc(doc);
00707 
00708     return(ret);
00709 }
00710 #endif /* LIBXML_OUTPUT_ENABLED */
00711 
00712 /************************************************************************
00713  *                                  *
00714  *          Converting SGML Catalogs to XML         *
00715  *                                  *
00716  ************************************************************************/
00717 
00718 /**
00719  * xmlCatalogConvertEntry:
00720  * @entry:  the entry
00721  * @catal:  pointer to the catalog being converted
00722  *
00723  * Convert one entry from the catalog
00724  */
00725 static void
00726 xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) {
00727     if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
00728     (catal->xml == NULL))
00729     return;
00730     switch (entry->type) {
00731     case SGML_CATA_ENTITY:
00732         entry->type = XML_CATA_PUBLIC;
00733         break;
00734     case SGML_CATA_PENTITY:
00735         entry->type = XML_CATA_PUBLIC;
00736         break;
00737     case SGML_CATA_DOCTYPE:
00738         entry->type = XML_CATA_PUBLIC;
00739         break;
00740     case SGML_CATA_LINKTYPE:
00741         entry->type = XML_CATA_PUBLIC;
00742         break;
00743     case SGML_CATA_NOTATION:
00744         entry->type = XML_CATA_PUBLIC;
00745         break;
00746     case SGML_CATA_PUBLIC:
00747         entry->type = XML_CATA_PUBLIC;
00748         break;
00749     case SGML_CATA_SYSTEM:
00750         entry->type = XML_CATA_SYSTEM;
00751         break;
00752     case SGML_CATA_DELEGATE:
00753         entry->type = XML_CATA_DELEGATE_PUBLIC;
00754         break;
00755     case SGML_CATA_CATALOG:
00756         entry->type = XML_CATA_CATALOG;
00757         break;
00758     default:
00759         xmlHashRemoveEntry(catal->sgml, entry->name,
00760                        (xmlHashDeallocator) xmlFreeCatalogEntry);
00761         return;
00762     }
00763     /*
00764      * Conversion successful, remove from the SGML catalog
00765      * and add it to the default XML one
00766      */
00767     xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
00768     entry->parent = catal->xml;
00769     entry->next = NULL;
00770     if (catal->xml->children == NULL)
00771     catal->xml->children = entry;
00772     else {
00773     xmlCatalogEntryPtr prev;
00774 
00775     prev = catal->xml->children;
00776     while (prev->next != NULL)
00777         prev = prev->next;
00778     prev->next = entry;
00779     }
00780 }
00781 
00782 /**
00783  * xmlConvertSGMLCatalog:
00784  * @catal: the catalog
00785  *
00786  * Convert all the SGML catalog entries as XML ones
00787  *
00788  * Returns the number of entries converted if successful, -1 otherwise
00789  */
00790 int
00791 xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
00792 
00793     if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
00794     return(-1);
00795 
00796     if (xmlDebugCatalogs) {
00797     xmlGenericError(xmlGenericErrorContext,
00798         "Converting SGML catalog to XML\n");
00799     }
00800     xmlHashScan(catal->sgml,
00801         (xmlHashScanner) xmlCatalogConvertEntry,
00802         &catal);
00803     return(0);
00804 }
00805 
00806 /************************************************************************
00807  *                                  *
00808  *          Helper function                 *
00809  *                                  *
00810  ************************************************************************/
00811 
00812 /**
00813  * xmlCatalogUnWrapURN:
00814  * @urn:  an "urn:publicid:" to unwrap
00815  *
00816  * Expand the URN into the equivalent Public Identifier
00817  *
00818  * Returns the new identifier or NULL, the string must be deallocated
00819  *         by the caller.
00820  */
00821 static xmlChar *
00822 xmlCatalogUnWrapURN(const xmlChar *urn) {
00823     xmlChar result[2000];
00824     unsigned int i = 0;
00825 
00826     if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
00827     return(NULL);
00828     urn += sizeof(XML_URN_PUBID) - 1;
00829 
00830     while (*urn != 0) {
00831     if (i > sizeof(result) - 4)
00832         break;
00833     if (*urn == '+') {
00834         result[i++] = ' ';
00835         urn++;
00836     } else if (*urn == ':') {
00837         result[i++] = '/';
00838         result[i++] = '/';
00839         urn++;
00840     } else if (*urn == ';') {
00841         result[i++] = ':';
00842         result[i++] = ':';
00843         urn++;
00844     } else if (*urn == '%') {
00845         if ((urn[1] == '2') && (urn[2] == 'B'))
00846         result[i++] = '+';
00847         else if ((urn[1] == '3') && (urn[2] == 'A'))
00848         result[i++] = ':';
00849         else if ((urn[1] == '2') && (urn[2] == 'F'))
00850         result[i++] = '/';
00851         else if ((urn[1] == '3') && (urn[2] == 'B'))
00852         result[i++] = ';';
00853         else if ((urn[1] == '2') && (urn[2] == '7'))
00854         result[i++] = '\'';
00855         else if ((urn[1] == '3') && (urn[2] == 'F'))
00856         result[i++] = '?';
00857         else if ((urn[1] == '2') && (urn[2] == '3'))
00858         result[i++] = '#';
00859         else if ((urn[1] == '2') && (urn[2] == '5'))
00860         result[i++] = '%';
00861         else {
00862         result[i++] = *urn;
00863         urn++;
00864         continue;
00865         }
00866         urn += 3;
00867     } else {
00868         result[i++] = *urn;
00869         urn++;
00870     }
00871     }
00872     result[i] = 0;
00873 
00874     return(xmlStrdup(result));
00875 }
00876 
00877 /**
00878  * xmlParseCatalogFile:
00879  * @filename:  the filename
00880  *
00881  * parse an XML file and build a tree. It's like xmlParseFile()
00882  * except it bypass all catalog lookups.
00883  *
00884  * Returns the resulting document tree or NULL in case of error
00885  */
00886 
00887 xmlDocPtr
00888 xmlParseCatalogFile(const char *filename) {
00889     xmlDocPtr ret;
00890     xmlParserCtxtPtr ctxt;
00891     char *directory = NULL;
00892     xmlParserInputPtr inputStream;
00893     xmlParserInputBufferPtr buf;
00894 
00895     ctxt = xmlNewParserCtxt();
00896     if (ctxt == NULL) {
00897 #ifdef LIBXML_SAX1_ENABLED
00898     if (xmlDefaultSAXHandler.error != NULL) {
00899         xmlDefaultSAXHandler.error(NULL, "out of memory\n");
00900     }
00901 #endif
00902     return(NULL);
00903     }
00904 
00905     buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
00906     if (buf == NULL) {
00907     xmlFreeParserCtxt(ctxt);
00908     return(NULL);
00909     }
00910 
00911     inputStream = xmlNewInputStream(ctxt);
00912     if (inputStream == NULL) {
00913     xmlFreeParserCtxt(ctxt);
00914     return(NULL);
00915     }
00916 
00917     inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
00918     inputStream->buf = buf;
00919     xmlBufResetInput(buf->buffer, inputStream);
00920 
00921     inputPush(ctxt, inputStream);
00922     if ((ctxt->directory == NULL) && (directory == NULL))
00923         directory = xmlParserGetDirectory(filename);
00924     if ((ctxt->directory == NULL) && (directory != NULL))
00925         ctxt->directory = directory;
00926     ctxt->valid = 0;
00927     ctxt->validate = 0;
00928     ctxt->loadsubset = 0;
00929     ctxt->pedantic = 0;
00930     ctxt->dictNames = 1;
00931 
00932     xmlParseDocument(ctxt);
00933 
00934     if (ctxt->wellFormed)
00935     ret = ctxt->myDoc;
00936     else {
00937         ret = NULL;
00938         xmlFreeDoc(ctxt->myDoc);
00939         ctxt->myDoc = NULL;
00940     }
00941     xmlFreeParserCtxt(ctxt);
00942 
00943     return(ret);
00944 }
00945 
00946 /**
00947  * xmlLoadFileContent:
00948  * @filename:  a file path
00949  *
00950  * Load a file content into memory.
00951  *
00952  * Returns a pointer to the 0 terminated string or NULL in case of error
00953  */
00954 static xmlChar *
00955 xmlLoadFileContent(const char *filename)
00956 {
00957 #ifdef HAVE_STAT
00958     int fd;
00959 #else
00960     FILE *fd;
00961 #endif
00962     int len;
00963     long size;
00964 
00965 #ifdef HAVE_STAT
00966     struct stat info;
00967 #endif
00968     xmlChar *content;
00969 
00970     if (filename == NULL)
00971         return (NULL);
00972 
00973 #ifdef HAVE_STAT
00974     if (stat(filename, &info) < 0)
00975         return (NULL);
00976 #endif
00977 
00978 #ifdef HAVE_STAT
00979     if ((fd = open(filename, O_RDONLY)) < 0)
00980 #else
00981     if ((fd = fopen(filename, "rb")) == NULL)
00982 #endif
00983     {
00984         return (NULL);
00985     }
00986 #ifdef HAVE_STAT
00987     size = info.st_size;
00988 #else
00989     if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
00990         fclose(fd);
00991         return (NULL);
00992     }
00993 #endif
00994     content = (xmlChar*)xmlMallocAtomic(size + 10);
00995     if (content == NULL) {
00996         xmlCatalogErrMemory("allocating catalog data");
00997 #ifdef HAVE_STAT
00998     close(fd);
00999 #else
01000     fclose(fd);
01001 #endif
01002         return (NULL);
01003     }
01004 #ifdef HAVE_STAT
01005     len = read(fd, content, size);
01006     close(fd);
01007 #else
01008     len = fread(content, 1, size, fd);
01009     fclose(fd);
01010 #endif
01011     if (len < 0) {
01012         xmlFree(content);
01013         return (NULL);
01014     }
01015     content[len] = 0;
01016 
01017     return(content);
01018 }
01019 
01020 /**
01021  * xmlCatalogNormalizePublic:
01022  * @pubID:  the public ID string
01023  *
01024  *  Normalizes the Public Identifier
01025  *
01026  * Implements 6.2. Public Identifier Normalization
01027  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
01028  *
01029  * Returns the new string or NULL, the string must be deallocated
01030  *         by the caller.
01031  */
01032 static xmlChar *
01033 xmlCatalogNormalizePublic(const xmlChar *pubID)
01034 {
01035     int ok = 1;
01036     int white;
01037     const xmlChar *p;
01038     xmlChar *ret;
01039     xmlChar *q;
01040 
01041     if (pubID == NULL)
01042         return(NULL);
01043 
01044     white = 1;
01045     for (p = pubID;*p != 0 && ok;p++) {
01046         if (!xmlIsBlank_ch(*p))
01047             white = 0;
01048         else if (*p == 0x20 && !white)
01049             white = 1;
01050         else
01051             ok = 0;
01052     }
01053     if (ok && !white)   /* is normalized */
01054         return(NULL);
01055 
01056     ret = xmlStrdup(pubID);
01057     q = ret;
01058     white = 0;
01059     for (p = pubID;*p != 0;p++) {
01060         if (xmlIsBlank_ch(*p)) {
01061             if (q != ret)
01062                 white = 1;
01063         } else {
01064             if (white) {
01065                 *(q++) = 0x20;
01066                 white = 0;
01067             }
01068             *(q++) = *p;
01069         }
01070     }
01071     *q = 0;
01072     return(ret);
01073 }
01074 
01075 /************************************************************************
01076  *                                  *
01077  *          The XML Catalog parser              *
01078  *                                  *
01079  ************************************************************************/
01080 
01081 static xmlCatalogEntryPtr
01082 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
01083 static void
01084 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
01085                        xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
01086 static xmlChar *
01087 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
01088                   const xmlChar *sysID);
01089 static xmlChar *
01090 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
01091 
01092 
01093 /**
01094  * xmlGetXMLCatalogEntryType:
01095  * @name:  the name
01096  *
01097  * lookup the internal type associated to an XML catalog entry name
01098  *
01099  * Returns the type associated with that name
01100  */
01101 static xmlCatalogEntryType
01102 xmlGetXMLCatalogEntryType(const xmlChar *name) {
01103     xmlCatalogEntryType type = XML_CATA_NONE;
01104     if (xmlStrEqual(name, (const xmlChar *) "system"))
01105     type = XML_CATA_SYSTEM;
01106     else if (xmlStrEqual(name, (const xmlChar *) "public"))
01107     type = XML_CATA_PUBLIC;
01108     else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
01109     type = XML_CATA_REWRITE_SYSTEM;
01110     else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
01111     type = XML_CATA_DELEGATE_PUBLIC;
01112     else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
01113     type = XML_CATA_DELEGATE_SYSTEM;
01114     else if (xmlStrEqual(name, (const xmlChar *) "uri"))
01115     type = XML_CATA_URI;
01116     else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
01117     type = XML_CATA_REWRITE_URI;
01118     else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
01119     type = XML_CATA_DELEGATE_URI;
01120     else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
01121     type = XML_CATA_NEXT_CATALOG;
01122     else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
01123     type = XML_CATA_CATALOG;
01124     return(type);
01125 }
01126 
01127 /**
01128  * xmlParseXMLCatalogOneNode:
01129  * @cur:  the XML node
01130  * @type:  the type of Catalog entry
01131  * @name:  the name of the node
01132  * @attrName:  the attribute holding the value
01133  * @uriAttrName:  the attribute holding the URI-Reference
01134  * @prefer:  the PUBLIC vs. SYSTEM current preference value
01135  * @cgroup:  the group which includes this node
01136  *
01137  * Finishes the examination of an XML tree node of a catalog and build
01138  * a Catalog entry from it.
01139  *
01140  * Returns the new Catalog entry node or NULL in case of error.
01141  */
01142 static xmlCatalogEntryPtr
01143 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
01144               const xmlChar *name, const xmlChar *attrName,
01145               const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
01146               xmlCatalogEntryPtr cgroup) {
01147     int ok = 1;
01148     xmlChar *uriValue;
01149     xmlChar *nameValue = NULL;
01150     xmlChar *base = NULL;
01151     xmlChar *URL = NULL;
01152     xmlCatalogEntryPtr ret = NULL;
01153 
01154     if (attrName != NULL) {
01155     nameValue = xmlGetProp(cur, attrName);
01156     if (nameValue == NULL) {
01157         xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
01158               "%s entry lacks '%s'\n", name, attrName, NULL);
01159         ok = 0;
01160     }
01161     }
01162     uriValue = xmlGetProp(cur, uriAttrName);
01163     if (uriValue == NULL) {
01164     xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
01165         "%s entry lacks '%s'\n", name, uriAttrName, NULL);
01166     ok = 0;
01167     }
01168     if (!ok) {
01169     if (nameValue != NULL)
01170         xmlFree(nameValue);
01171     if (uriValue != NULL)
01172         xmlFree(uriValue);
01173     return(NULL);
01174     }
01175 
01176     base = xmlNodeGetBase(cur->doc, cur);
01177     URL = xmlBuildURI(uriValue, base);
01178     if (URL != NULL) {
01179     if (xmlDebugCatalogs > 1) {
01180         if (nameValue != NULL)
01181         xmlGenericError(xmlGenericErrorContext,
01182             "Found %s: '%s' '%s'\n", name, nameValue, URL);
01183         else
01184         xmlGenericError(xmlGenericErrorContext,
01185             "Found %s: '%s'\n", name, URL);
01186     }
01187     ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
01188     } else {
01189     xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
01190         "%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
01191     }
01192     if (nameValue != NULL)
01193     xmlFree(nameValue);
01194     if (uriValue != NULL)
01195     xmlFree(uriValue);
01196     if (base != NULL)
01197     xmlFree(base);
01198     if (URL != NULL)
01199     xmlFree(URL);
01200     return(ret);
01201 }
01202 
01203 /**
01204  * xmlParseXMLCatalogNode:
01205  * @cur:  the XML node
01206  * @prefer:  the PUBLIC vs. SYSTEM current preference value
01207  * @parent:  the parent Catalog entry
01208  * @cgroup:  the group which includes this node
01209  *
01210  * Examines an XML tree node of a catalog and build
01211  * a Catalog entry from it adding it to its parent. The examination can
01212  * be recursive.
01213  */
01214 static void
01215 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
01216                    xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
01217 {
01218     xmlChar *base = NULL;
01219     xmlCatalogEntryPtr entry = NULL;
01220 
01221     if (cur == NULL)
01222         return;
01223     if (xmlStrEqual(cur->name, BAD_CAST "group")) {
01224         xmlChar *prop;
01225     xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
01226 
01227         prop = xmlGetProp(cur, BAD_CAST "prefer");
01228         if (prop != NULL) {
01229             if (xmlStrEqual(prop, BAD_CAST "system")) {
01230                 prefer = XML_CATA_PREFER_SYSTEM;
01231             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
01232                 prefer = XML_CATA_PREFER_PUBLIC;
01233             } else {
01234         xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
01235                               "Invalid value for prefer: '%s'\n",
01236                   prop, NULL, NULL);
01237             }
01238             xmlFree(prop);
01239         pref = prefer;
01240         }
01241     prop = xmlGetProp(cur, BAD_CAST "id");
01242     base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
01243     entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
01244     xmlFree(prop);
01245     } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
01246     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
01247         BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
01248     } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
01249     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
01250         BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
01251     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
01252     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
01253         BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
01254         BAD_CAST "rewritePrefix", prefer, cgroup);
01255     } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
01256     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
01257         BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
01258         BAD_CAST "catalog", prefer, cgroup);
01259     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
01260     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
01261         BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
01262         BAD_CAST "catalog", prefer, cgroup);
01263     } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
01264     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
01265         BAD_CAST "uri", BAD_CAST "name",
01266         BAD_CAST "uri", prefer, cgroup);
01267     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
01268     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
01269         BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
01270         BAD_CAST "rewritePrefix", prefer, cgroup);
01271     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
01272     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
01273         BAD_CAST "delegateURI", BAD_CAST "uriStartString",
01274         BAD_CAST "catalog", prefer, cgroup);
01275     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
01276     entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
01277         BAD_CAST "nextCatalog", NULL,
01278         BAD_CAST "catalog", prefer, cgroup);
01279     }
01280     if (entry != NULL) {
01281         if (parent != NULL) {
01282         entry->parent = parent;
01283         if (parent->children == NULL)
01284         parent->children = entry;
01285         else {
01286         xmlCatalogEntryPtr prev;
01287 
01288         prev = parent->children;
01289         while (prev->next != NULL)
01290             prev = prev->next;
01291         prev->next = entry;
01292         }
01293     }
01294     if (entry->type == XML_CATA_GROUP) {
01295         /*
01296          * Recurse to propagate prefer to the subtree
01297          * (xml:base handling is automated)
01298          */
01299             xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
01300     }
01301     }
01302     if (base != NULL)
01303     xmlFree(base);
01304 }
01305 
01306 /**
01307  * xmlParseXMLCatalogNodeList:
01308  * @cur:  the XML node list of siblings
01309  * @prefer:  the PUBLIC vs. SYSTEM current preference value
01310  * @parent:  the parent Catalog entry
01311  * @cgroup:  the group which includes this list
01312  *
01313  * Examines a list of XML sibling nodes of a catalog and build
01314  * a list of Catalog entry from it adding it to the parent.
01315  * The examination will recurse to examine node subtrees.
01316  */
01317 static void
01318 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
01319                        xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
01320     while (cur != NULL) {
01321     if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
01322         (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
01323         xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
01324     }
01325     cur = cur->next;
01326     }
01327     /* TODO: sort the list according to REWRITE lengths and prefer value */
01328 }
01329 
01330 /**
01331  * xmlParseXMLCatalogFile:
01332  * @prefer:  the PUBLIC vs. SYSTEM current preference value
01333  * @filename:  the filename for the catalog
01334  *
01335  * Parses the catalog file to extract the XML tree and then analyze the
01336  * tree to build a list of Catalog entries corresponding to this catalog
01337  *
01338  * Returns the resulting Catalog entries list
01339  */
01340 static xmlCatalogEntryPtr
01341 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
01342     xmlDocPtr doc;
01343     xmlNodePtr cur;
01344     xmlChar *prop;
01345     xmlCatalogEntryPtr parent = NULL;
01346 
01347     if (filename == NULL)
01348         return(NULL);
01349 
01350     doc = xmlParseCatalogFile((const char *) filename);
01351     if (doc == NULL) {
01352     if (xmlDebugCatalogs)
01353         xmlGenericError(xmlGenericErrorContext,
01354             "Failed to parse catalog %s\n", filename);
01355     return(NULL);
01356     }
01357 
01358     if (xmlDebugCatalogs)
01359     xmlGenericError(xmlGenericErrorContext,
01360         "%d Parsing catalog %s\n", xmlGetThreadId(), filename);
01361 
01362     cur = xmlDocGetRootElement(doc);
01363     if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
01364     (cur->ns != NULL) && (cur->ns->href != NULL) &&
01365     (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
01366 
01367     parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
01368                     (const xmlChar *)filename, NULL, prefer, NULL);
01369         if (parent == NULL) {
01370         xmlFreeDoc(doc);
01371         return(NULL);
01372     }
01373 
01374     prop = xmlGetProp(cur, BAD_CAST "prefer");
01375     if (prop != NULL) {
01376         if (xmlStrEqual(prop, BAD_CAST "system")) {
01377         prefer = XML_CATA_PREFER_SYSTEM;
01378         } else if (xmlStrEqual(prop, BAD_CAST "public")) {
01379         prefer = XML_CATA_PREFER_PUBLIC;
01380         } else {
01381         xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
01382                   "Invalid value for prefer: '%s'\n",
01383                   prop, NULL, NULL);
01384         }
01385         xmlFree(prop);
01386     }
01387     cur = cur->children;
01388     xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
01389     } else {
01390     xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
01391               "File %s is not an XML Catalog\n",
01392               filename, NULL, NULL);
01393     xmlFreeDoc(doc);
01394     return(NULL);
01395     }
01396     xmlFreeDoc(doc);
01397     return(parent);
01398 }
01399 
01400 /**
01401  * xmlFetchXMLCatalogFile:
01402  * @catal:  an existing but incomplete catalog entry
01403  *
01404  * Fetch and parse the subcatalog referenced by an entry
01405  *
01406  * Returns 0 in case of success, -1 otherwise
01407  */
01408 static int
01409 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
01410     xmlCatalogEntryPtr doc;
01411 
01412     if (catal == NULL)
01413     return(-1);
01414     if (catal->URL == NULL)
01415     return(-1);
01416 
01417     /*
01418      * lock the whole catalog for modification
01419      */
01420     xmlRMutexLock(xmlCatalogMutex);
01421     if (catal->children != NULL) {
01422     /* Okay someone else did it in the meantime */
01423     xmlRMutexUnlock(xmlCatalogMutex);
01424     return(0);
01425     }
01426 
01427     if (xmlCatalogXMLFiles != NULL) {
01428     doc = (xmlCatalogEntryPtr)
01429         xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
01430     if (doc != NULL) {
01431         if (xmlDebugCatalogs)
01432         xmlGenericError(xmlGenericErrorContext,
01433             "Found %s in file hash\n", catal->URL);
01434 
01435         if (catal->type == XML_CATA_CATALOG)
01436         catal->children = doc->children;
01437         else
01438         catal->children = doc;
01439         catal->dealloc = 0;
01440         xmlRMutexUnlock(xmlCatalogMutex);
01441         return(0);
01442     }
01443     if (xmlDebugCatalogs)
01444         xmlGenericError(xmlGenericErrorContext,
01445         "%s not found in file hash\n", catal->URL);
01446     }
01447 
01448     /*
01449      * Fetch and parse. Note that xmlParseXMLCatalogFile does not
01450      * use the existing catalog, there is no recursion allowed at
01451      * that level.
01452      */
01453     doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
01454     if (doc == NULL) {
01455     catal->type = XML_CATA_BROKEN_CATALOG;
01456     xmlRMutexUnlock(xmlCatalogMutex);
01457     return(-1);
01458     }
01459 
01460     if (catal->type == XML_CATA_CATALOG)
01461     catal->children = doc->children;
01462     else
01463     catal->children = doc;
01464 
01465     doc->dealloc = 1;
01466 
01467     if (xmlCatalogXMLFiles == NULL)
01468     xmlCatalogXMLFiles = xmlHashCreate(10);
01469     if (xmlCatalogXMLFiles != NULL) {
01470     if (xmlDebugCatalogs)
01471         xmlGenericError(xmlGenericErrorContext,
01472         "%s added to file hash\n", catal->URL);
01473     xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
01474     }
01475     xmlRMutexUnlock(xmlCatalogMutex);
01476     return(0);
01477 }
01478 
01479 /************************************************************************
01480  *                                  *
01481  *          XML Catalog handling                *
01482  *                                  *
01483  ************************************************************************/
01484 
01485 /**
01486  * xmlAddXMLCatalog:
01487  * @catal:  top of an XML catalog
01488  * @type:  the type of record to add to the catalog
01489  * @orig:  the system, public or prefix to match (or NULL)
01490  * @replace:  the replacement value for the match
01491  *
01492  * Add an entry in the XML catalog, it may overwrite existing but
01493  * different entries.
01494  *
01495  * Returns 0 if successful, -1 otherwise
01496  */
01497 static int
01498 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
01499           const xmlChar *orig, const xmlChar *replace) {
01500     xmlCatalogEntryPtr cur;
01501     xmlCatalogEntryType typ;
01502     int doregister = 0;
01503 
01504     if ((catal == NULL) ||
01505     ((catal->type != XML_CATA_CATALOG) &&
01506      (catal->type != XML_CATA_BROKEN_CATALOG)))
01507     return(-1);
01508     if (catal->children == NULL) {
01509     xmlFetchXMLCatalogFile(catal);
01510     }
01511     if (catal->children == NULL)
01512     doregister = 1;
01513 
01514     typ = xmlGetXMLCatalogEntryType(type);
01515     if (typ == XML_CATA_NONE) {
01516     if (xmlDebugCatalogs)
01517         xmlGenericError(xmlGenericErrorContext,
01518             "Failed to add unknown element %s to catalog\n", type);
01519     return(-1);
01520     }
01521 
01522     cur = catal->children;
01523     /*
01524      * Might be a simple "update in place"
01525      */
01526     if (cur != NULL) {
01527     while (cur != NULL) {
01528         if ((orig != NULL) && (cur->type == typ) &&
01529         (xmlStrEqual(orig, cur->name))) {
01530         if (xmlDebugCatalogs)
01531             xmlGenericError(xmlGenericErrorContext,
01532                 "Updating element %s to catalog\n", type);
01533         if (cur->value != NULL)
01534             xmlFree(cur->value);
01535         if (cur->URL != NULL)
01536             xmlFree(cur->URL);
01537         cur->value = xmlStrdup(replace);
01538         cur->URL = xmlStrdup(replace);
01539         return(0);
01540         }
01541         if (cur->next == NULL)
01542         break;
01543         cur = cur->next;
01544     }
01545     }
01546     if (xmlDebugCatalogs)
01547     xmlGenericError(xmlGenericErrorContext,
01548         "Adding element %s to catalog\n", type);
01549     if (cur == NULL)
01550     catal->children = xmlNewCatalogEntry(typ, orig, replace,
01551                                      NULL, catal->prefer, NULL);
01552     else
01553     cur->next = xmlNewCatalogEntry(typ, orig, replace,
01554                                NULL, catal->prefer, NULL);
01555     if (doregister) {
01556         catal->type = XML_CATA_CATALOG;
01557     cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
01558     if (cur != NULL)
01559         cur->children = catal->children;
01560     }
01561 
01562     return(0);
01563 }
01564 
01565 /**
01566  * xmlDelXMLCatalog:
01567  * @catal:  top of an XML catalog
01568  * @value:  the value to remove from the catalog
01569  *
01570  * Remove entries in the XML catalog where the value or the URI
01571  * is equal to @value
01572  *
01573  * Returns the number of entries removed if successful, -1 otherwise
01574  */
01575 static int
01576 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
01577     xmlCatalogEntryPtr cur;
01578     int ret = 0;
01579 
01580     if ((catal == NULL) ||
01581     ((catal->type != XML_CATA_CATALOG) &&
01582      (catal->type != XML_CATA_BROKEN_CATALOG)))
01583     return(-1);
01584     if (value == NULL)
01585     return(-1);
01586     if (catal->children == NULL) {
01587     xmlFetchXMLCatalogFile(catal);
01588     }
01589 
01590     /*
01591      * Scan the children
01592      */
01593     cur = catal->children;
01594     while (cur != NULL) {
01595     if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
01596         (xmlStrEqual(value, cur->value))) {
01597         if (xmlDebugCatalogs) {
01598         if (cur->name != NULL)
01599             xmlGenericError(xmlGenericErrorContext,
01600                 "Removing element %s from catalog\n", cur->name);
01601         else
01602             xmlGenericError(xmlGenericErrorContext,
01603                 "Removing element %s from catalog\n", cur->value);
01604         }
01605         cur->type = XML_CATA_REMOVED;
01606     }
01607     cur = cur->next;
01608     }
01609     return(ret);
01610 }
01611 
01612 /**
01613  * xmlCatalogXMLResolve:
01614  * @catal:  a catalog list
01615  * @pubID:  the public ID string
01616  * @sysID:  the system ID string
01617  *
01618  * Do a complete resolution lookup of an External Identifier for a
01619  * list of catalog entries.
01620  *
01621  * Implements (or tries to) 7.1. External Identifier Resolution
01622  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
01623  *
01624  * Returns the URI of the resource or NULL if not found
01625  */
01626 static xmlChar *
01627 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
01628                   const xmlChar *sysID) {
01629     xmlChar *ret = NULL;
01630     xmlCatalogEntryPtr cur;
01631     int haveDelegate = 0;
01632     int haveNext = 0;
01633 
01634     /*
01635      * protection against loops
01636      */
01637     if (catal->depth > MAX_CATAL_DEPTH) {
01638     xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
01639               "Detected recursion in catalog %s\n",
01640               catal->name, NULL, NULL);
01641     return(NULL);
01642     }
01643     catal->depth++;
01644 
01645     /*
01646      * First tries steps 2/ 3/ 4/ if a system ID is provided.
01647      */
01648     if (sysID != NULL) {
01649     xmlCatalogEntryPtr rewrite = NULL;
01650     int lenrewrite = 0, len;
01651     cur = catal;
01652     haveDelegate = 0;
01653     while (cur != NULL) {
01654         switch (cur->type) {
01655         case XML_CATA_SYSTEM:
01656             if (xmlStrEqual(sysID, cur->name)) {
01657             if (xmlDebugCatalogs)
01658                 xmlGenericError(xmlGenericErrorContext,
01659                     "Found system match %s, using %s\n",
01660                             cur->name, cur->URL);
01661             catal->depth--;
01662             return(xmlStrdup(cur->URL));
01663             }
01664             break;
01665         case XML_CATA_REWRITE_SYSTEM:
01666             len = xmlStrlen(cur->name);
01667             if ((len > lenrewrite) &&
01668             (!xmlStrncmp(sysID, cur->name, len))) {
01669             lenrewrite = len;
01670             rewrite = cur;
01671             }
01672             break;
01673         case XML_CATA_DELEGATE_SYSTEM:
01674             if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
01675             haveDelegate++;
01676             break;
01677         case XML_CATA_NEXT_CATALOG:
01678             haveNext++;
01679             break;
01680         default:
01681             break;
01682         }
01683         cur = cur->next;
01684     }
01685     if (rewrite != NULL) {
01686         if (xmlDebugCatalogs)
01687         xmlGenericError(xmlGenericErrorContext,
01688             "Using rewriting rule %s\n", rewrite->name);
01689         ret = xmlStrdup(rewrite->URL);
01690         if (ret != NULL)
01691         ret = xmlStrcat(ret, &sysID[lenrewrite]);
01692         catal->depth--;
01693         return(ret);
01694     }
01695     if (haveDelegate) {
01696         const xmlChar *delegates[MAX_DELEGATE];
01697         int nbList = 0, i;
01698 
01699         /*
01700          * Assume the entries have been sorted by decreasing substring
01701          * matches when the list was produced.
01702          */
01703         cur = catal;
01704         while (cur != NULL) {
01705         if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
01706             (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
01707             for (i = 0;i < nbList;i++)
01708             if (xmlStrEqual(cur->URL, delegates[i]))
01709                 break;
01710             if (i < nbList) {
01711             cur = cur->next;
01712             continue;
01713             }
01714             if (nbList < MAX_DELEGATE)
01715             delegates[nbList++] = cur->URL;
01716 
01717             if (cur->children == NULL) {
01718             xmlFetchXMLCatalogFile(cur);
01719             }
01720             if (cur->children != NULL) {
01721             if (xmlDebugCatalogs)
01722                 xmlGenericError(xmlGenericErrorContext,
01723                     "Trying system delegate %s\n", cur->URL);
01724             ret = xmlCatalogListXMLResolve(
01725                 cur->children, NULL, sysID);
01726             if (ret != NULL) {
01727                 catal->depth--;
01728                 return(ret);
01729             }
01730             }
01731         }
01732         cur = cur->next;
01733         }
01734         /*
01735          * Apply the cut algorithm explained in 4/
01736          */
01737         catal->depth--;
01738         return(XML_CATAL_BREAK);
01739     }
01740     }
01741     /*
01742      * Then tries 5/ 6/ if a public ID is provided
01743      */
01744     if (pubID != NULL) {
01745     cur = catal;
01746     haveDelegate = 0;
01747     while (cur != NULL) {
01748         switch (cur->type) {
01749         case XML_CATA_PUBLIC:
01750             if (xmlStrEqual(pubID, cur->name)) {
01751             if (xmlDebugCatalogs)
01752                 xmlGenericError(xmlGenericErrorContext,
01753                     "Found public match %s\n", cur->name);
01754             catal->depth--;
01755             return(xmlStrdup(cur->URL));
01756             }
01757             break;
01758         case XML_CATA_DELEGATE_PUBLIC:
01759             if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
01760             (cur->prefer == XML_CATA_PREFER_PUBLIC))
01761             haveDelegate++;
01762             break;
01763         case XML_CATA_NEXT_CATALOG:
01764             if (sysID == NULL)
01765             haveNext++;
01766             break;
01767         default:
01768             break;
01769         }
01770         cur = cur->next;
01771     }
01772     if (haveDelegate) {
01773         const xmlChar *delegates[MAX_DELEGATE];
01774         int nbList = 0, i;
01775 
01776         /*
01777          * Assume the entries have been sorted by decreasing substring
01778          * matches when the list was produced.
01779          */
01780         cur = catal;
01781         while (cur != NULL) {
01782         if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
01783             (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
01784             (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
01785 
01786             for (i = 0;i < nbList;i++)
01787             if (xmlStrEqual(cur->URL, delegates[i]))
01788                 break;
01789             if (i < nbList) {
01790             cur = cur->next;
01791             continue;
01792             }
01793             if (nbList < MAX_DELEGATE)
01794             delegates[nbList++] = cur->URL;
01795 
01796             if (cur->children == NULL) {
01797             xmlFetchXMLCatalogFile(cur);
01798             }
01799             if (cur->children != NULL) {
01800             if (xmlDebugCatalogs)
01801                 xmlGenericError(xmlGenericErrorContext,
01802                     "Trying public delegate %s\n", cur->URL);
01803             ret = xmlCatalogListXMLResolve(
01804                 cur->children, pubID, NULL);
01805             if (ret != NULL) {
01806                 catal->depth--;
01807                 return(ret);
01808             }
01809             }
01810         }
01811         cur = cur->next;
01812         }
01813         /*
01814          * Apply the cut algorithm explained in 4/
01815          */
01816         catal->depth--;
01817         return(XML_CATAL_BREAK);
01818     }
01819     }
01820     if (haveNext) {
01821     cur = catal;
01822     while (cur != NULL) {
01823         if (cur->type == XML_CATA_NEXT_CATALOG) {
01824         if (cur->children == NULL) {
01825             xmlFetchXMLCatalogFile(cur);
01826         }
01827         if (cur->children != NULL) {
01828             ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
01829             if (ret != NULL) {
01830             catal->depth--;
01831             return(ret);
01832             } else if (catal->depth > MAX_CATAL_DEPTH) {
01833                 return(NULL);
01834             }
01835         }
01836         }
01837         cur = cur->next;
01838     }
01839     }
01840 
01841     catal->depth--;
01842     return(NULL);
01843 }
01844 
01845 /**
01846  * xmlCatalogXMLResolveURI:
01847  * @catal:  a catalog list
01848  * @URI:  the URI
01849  * @sysID:  the system ID string
01850  *
01851  * Do a complete resolution lookup of an External Identifier for a
01852  * list of catalog entries.
01853  *
01854  * Implements (or tries to) 7.2.2. URI Resolution
01855  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
01856  *
01857  * Returns the URI of the resource or NULL if not found
01858  */
01859 static xmlChar *
01860 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
01861     xmlChar *ret = NULL;
01862     xmlCatalogEntryPtr cur;
01863     int haveDelegate = 0;
01864     int haveNext = 0;
01865     xmlCatalogEntryPtr rewrite = NULL;
01866     int lenrewrite = 0, len;
01867 
01868     if (catal == NULL)
01869     return(NULL);
01870 
01871     if (URI == NULL)
01872     return(NULL);
01873 
01874     if (catal->depth > MAX_CATAL_DEPTH) {
01875     xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
01876               "Detected recursion in catalog %s\n",
01877               catal->name, NULL, NULL);
01878     return(NULL);
01879     }
01880 
01881     /*
01882      * First tries steps 2/ 3/ 4/ if a system ID is provided.
01883      */
01884     cur = catal;
01885     haveDelegate = 0;
01886     while (cur != NULL) {
01887     switch (cur->type) {
01888         case XML_CATA_URI:
01889         if (xmlStrEqual(URI, cur->name)) {
01890             if (xmlDebugCatalogs)
01891             xmlGenericError(xmlGenericErrorContext,
01892                 "Found URI match %s\n", cur->name);
01893             return(xmlStrdup(cur->URL));
01894         }
01895         break;
01896         case XML_CATA_REWRITE_URI:
01897         len = xmlStrlen(cur->name);
01898         if ((len > lenrewrite) &&
01899             (!xmlStrncmp(URI, cur->name, len))) {
01900             lenrewrite = len;
01901             rewrite = cur;
01902         }
01903         break;
01904         case XML_CATA_DELEGATE_URI:
01905         if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
01906             haveDelegate++;
01907         break;
01908         case XML_CATA_NEXT_CATALOG:
01909         haveNext++;
01910         break;
01911         default:
01912         break;
01913     }
01914     cur = cur->next;
01915     }
01916     if (rewrite != NULL) {
01917     if (xmlDebugCatalogs)
01918         xmlGenericError(xmlGenericErrorContext,
01919             "Using rewriting rule %s\n", rewrite->name);
01920     ret = xmlStrdup(rewrite->URL);
01921     if (ret != NULL)
01922         ret = xmlStrcat(ret, &URI[lenrewrite]);
01923     return(ret);
01924     }
01925     if (haveDelegate) {
01926     const xmlChar *delegates[MAX_DELEGATE];
01927     int nbList = 0, i;
01928 
01929     /*
01930      * Assume the entries have been sorted by decreasing substring
01931      * matches when the list was produced.
01932      */
01933     cur = catal;
01934     while (cur != NULL) {
01935         if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
01936              (cur->type == XML_CATA_DELEGATE_URI)) &&
01937         (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
01938         for (i = 0;i < nbList;i++)
01939             if (xmlStrEqual(cur->URL, delegates[i]))
01940             break;
01941         if (i < nbList) {
01942             cur = cur->next;
01943             continue;
01944         }
01945         if (nbList < MAX_DELEGATE)
01946             delegates[nbList++] = cur->URL;
01947 
01948         if (cur->children == NULL) {
01949             xmlFetchXMLCatalogFile(cur);
01950         }
01951         if (cur->children != NULL) {
01952             if (xmlDebugCatalogs)
01953             xmlGenericError(xmlGenericErrorContext,
01954                 "Trying URI delegate %s\n", cur->URL);
01955             ret = xmlCatalogListXMLResolveURI(
01956                 cur->children, URI);
01957             if (ret != NULL)
01958             return(ret);
01959         }
01960         }
01961         cur = cur->next;
01962     }
01963     /*
01964      * Apply the cut algorithm explained in 4/
01965      */
01966     return(XML_CATAL_BREAK);
01967     }
01968     if (haveNext) {
01969     cur = catal;
01970     while (cur != NULL) {
01971         if (cur->type == XML_CATA_NEXT_CATALOG) {
01972         if (cur->children == NULL) {
01973             xmlFetchXMLCatalogFile(cur);
01974         }
01975         if (cur->children != NULL) {
01976             ret = xmlCatalogListXMLResolveURI(cur->children, URI);
01977             if (ret != NULL)
01978             return(ret);
01979         }
01980         }
01981         cur = cur->next;
01982     }
01983     }
01984 
01985     return(NULL);
01986 }
01987 
01988 /**
01989  * xmlCatalogListXMLResolve:
01990  * @catal:  a catalog list
01991  * @pubID:  the public ID string
01992  * @sysID:  the system ID string
01993  *
01994  * Do a complete resolution lookup of an External Identifier for a
01995  * list of catalogs
01996  *
01997  * Implements (or tries to) 7.1. External Identifier Resolution
01998  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
01999  *
02000  * Returns the URI of the resource or NULL if not found
02001  */
02002 static xmlChar *
02003 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
02004                   const xmlChar *sysID) {
02005     xmlChar *ret = NULL;
02006     xmlChar *urnID = NULL;
02007     xmlChar *normid;
02008 
02009     if (catal == NULL)
02010         return(NULL);
02011     if ((pubID == NULL) && (sysID == NULL))
02012     return(NULL);
02013 
02014     normid = xmlCatalogNormalizePublic(pubID);
02015     if (normid != NULL)
02016         pubID = (*normid != 0 ? normid : NULL);
02017 
02018     if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
02019     urnID = xmlCatalogUnWrapURN(pubID);
02020     if (xmlDebugCatalogs) {
02021         if (urnID == NULL)
02022         xmlGenericError(xmlGenericErrorContext,
02023             "Public URN ID %s expanded to NULL\n", pubID);
02024         else
02025         xmlGenericError(xmlGenericErrorContext,
02026             "Public URN ID expanded to %s\n", urnID);
02027     }
02028     ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
02029     if (urnID != NULL)
02030         xmlFree(urnID);
02031     if (normid != NULL)
02032         xmlFree(normid);
02033     return(ret);
02034     }
02035     if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
02036     urnID = xmlCatalogUnWrapURN(sysID);
02037     if (xmlDebugCatalogs) {
02038         if (urnID == NULL)
02039         xmlGenericError(xmlGenericErrorContext,
02040             "System URN ID %s expanded to NULL\n", sysID);
02041         else
02042         xmlGenericError(xmlGenericErrorContext,
02043             "System URN ID expanded to %s\n", urnID);
02044     }
02045     if (pubID == NULL)
02046         ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
02047     else if (xmlStrEqual(pubID, urnID))
02048         ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
02049     else {
02050         ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
02051     }
02052     if (urnID != NULL)
02053         xmlFree(urnID);
02054     if (normid != NULL)
02055         xmlFree(normid);
02056     return(ret);
02057     }
02058     while (catal != NULL) {
02059     if (catal->type == XML_CATA_CATALOG) {
02060         if (catal->children == NULL) {
02061         xmlFetchXMLCatalogFile(catal);
02062         }
02063         if (catal->children != NULL) {
02064         ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
02065         if (ret != NULL) {
02066             break;
02067                 } else if ((catal->children != NULL) &&
02068                    (catal->children->depth > MAX_CATAL_DEPTH)) {
02069                 ret = NULL;
02070             break;
02071             }
02072         }
02073     }
02074     catal = catal->next;
02075     }
02076     if (normid != NULL)
02077     xmlFree(normid);
02078     return(ret);
02079 }
02080 
02081 /**
02082  * xmlCatalogListXMLResolveURI:
02083  * @catal:  a catalog list
02084  * @URI:  the URI
02085  *
02086  * Do a complete resolution lookup of an URI for a list of catalogs
02087  *
02088  * Implements (or tries to) 7.2. URI Resolution
02089  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
02090  *
02091  * Returns the URI of the resource or NULL if not found
02092  */
02093 static xmlChar *
02094 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
02095     xmlChar *ret = NULL;
02096     xmlChar *urnID = NULL;
02097 
02098     if (catal == NULL)
02099         return(NULL);
02100     if (URI == NULL)
02101     return(NULL);
02102 
02103     if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
02104     urnID = xmlCatalogUnWrapURN(URI);
02105     if (xmlDebugCatalogs) {
02106         if (urnID == NULL)
02107         xmlGenericError(xmlGenericErrorContext,
02108             "URN ID %s expanded to NULL\n", URI);
02109         else
02110         xmlGenericError(xmlGenericErrorContext,
02111             "URN ID expanded to %s\n", urnID);
02112     }
02113     ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
02114     if (urnID != NULL)
02115         xmlFree(urnID);
02116     return(ret);
02117     }
02118     while (catal != NULL) {
02119     if (catal->type == XML_CATA_CATALOG) {
02120         if (catal->children == NULL) {
02121         xmlFetchXMLCatalogFile(catal);
02122         }
02123         if (catal->children != NULL) {
02124         ret = xmlCatalogXMLResolveURI(catal->children, URI);
02125         if (ret != NULL)
02126             return(ret);
02127         }
02128     }
02129     catal = catal->next;
02130     }
02131     return(ret);
02132 }
02133 
02134 /************************************************************************
02135  *                                  *
02136  *          The SGML Catalog parser             *
02137  *                                  *
02138  ************************************************************************/
02139 
02140 
02141 #define RAW *cur
02142 #define NEXT cur++;
02143 #define SKIP(x) cur += x;
02144 
02145 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
02146 
02147 /**
02148  * xmlParseSGMLCatalogComment:
02149  * @cur:  the current character
02150  *
02151  * Skip a comment in an SGML catalog
02152  *
02153  * Returns new current character
02154  */
02155 static const xmlChar *
02156 xmlParseSGMLCatalogComment(const xmlChar *cur) {
02157     if ((cur[0] != '-') || (cur[1] != '-'))
02158     return(cur);
02159     SKIP(2);
02160     while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
02161     NEXT;
02162     if (cur[0] == 0) {
02163     return(NULL);
02164     }
02165     return(cur + 2);
02166 }
02167 
02168 /**
02169  * xmlParseSGMLCatalogPubid:
02170  * @cur:  the current character
02171  * @id:  the return location
02172  *
02173  * Parse an SGML catalog ID
02174  *
02175  * Returns new current character and store the value in @id
02176  */
02177 static const xmlChar *
02178 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
02179     xmlChar *buf = NULL, *tmp;
02180     int len = 0;
02181     int size = 50;
02182     xmlChar stop;
02183     int count = 0;
02184 
02185     *id = NULL;
02186 
02187     if (RAW == '"') {
02188         NEXT;
02189     stop = '"';
02190     } else if (RAW == '\'') {
02191         NEXT;
02192     stop = '\'';
02193     } else {
02194     stop = ' ';
02195     }
02196     buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
02197     if (buf == NULL) {
02198         xmlCatalogErrMemory("allocating public ID");
02199     return(NULL);
02200     }
02201     while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
02202     if ((*cur == stop) && (stop != ' '))
02203         break;
02204     if ((stop == ' ') && (IS_BLANK_CH(*cur)))
02205         break;
02206     if (len + 1 >= size) {
02207         size *= 2;
02208         tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
02209         if (tmp == NULL) {
02210         xmlCatalogErrMemory("allocating public ID");
02211         xmlFree(buf);
02212         return(NULL);
02213         }
02214         buf = tmp;
02215     }
02216     buf[len++] = *cur;
02217     count++;
02218     NEXT;
02219     }
02220     buf[len] = 0;
02221     if (stop == ' ') {
02222     if (!IS_BLANK_CH(*cur)) {
02223         xmlFree(buf);
02224         return(NULL);
02225     }
02226     } else {
02227     if (*cur != stop) {
02228         xmlFree(buf);
02229         return(NULL);
02230     }
02231     NEXT;
02232     }
02233     *id = buf;
02234     return(cur);
02235 }
02236 
02237 /**
02238  * xmlParseSGMLCatalogName:
02239  * @cur:  the current character
02240  * @name:  the return location
02241  *
02242  * Parse an SGML catalog name
02243  *
02244  * Returns new current character and store the value in @name
02245  */
02246 static const xmlChar *
02247 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
02248     xmlChar buf[XML_MAX_NAMELEN + 5];
02249     int len = 0;
02250     int c;
02251 
02252     *name = NULL;
02253 
02254     /*
02255      * Handler for more complex cases
02256      */
02257     c = *cur;
02258     if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
02259     return(NULL);
02260     }
02261 
02262     while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
02263             (c == '.') || (c == '-') ||
02264         (c == '_') || (c == ':'))) {
02265     buf[len++] = c;
02266     cur++;
02267     c = *cur;
02268     if (len >= XML_MAX_NAMELEN)
02269         return(NULL);
02270     }
02271     *name = xmlStrndup(buf, len);
02272     return(cur);
02273 }
02274 
02275 /**
02276  * xmlGetSGMLCatalogEntryType:
02277  * @name:  the entry name
02278  *
02279  * Get the Catalog entry type for a given SGML Catalog name
02280  *
02281  * Returns Catalog entry type
02282  */
02283 static xmlCatalogEntryType
02284 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
02285     xmlCatalogEntryType type = XML_CATA_NONE;
02286     if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
02287     type = SGML_CATA_SYSTEM;
02288     else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
02289     type = SGML_CATA_PUBLIC;
02290     else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
02291     type = SGML_CATA_DELEGATE;
02292     else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
02293     type = SGML_CATA_ENTITY;
02294     else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
02295     type = SGML_CATA_DOCTYPE;
02296     else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
02297     type = SGML_CATA_LINKTYPE;
02298     else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
02299     type = SGML_CATA_NOTATION;
02300     else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
02301     type = SGML_CATA_SGMLDECL;
02302     else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
02303     type = SGML_CATA_DOCUMENT;
02304     else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
02305     type = SGML_CATA_CATALOG;
02306     else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
02307     type = SGML_CATA_BASE;
02308     return(type);
02309 }
02310 
02311 /**
02312  * xmlParseSGMLCatalog:
02313  * @catal:  the SGML Catalog
02314  * @value:  the content of the SGML Catalog serialization
02315  * @file:  the filepath for the catalog
02316  * @super:  should this be handled as a Super Catalog in which case
02317  *          parsing is not recursive
02318  *
02319  * Parse an SGML catalog content and fill up the @catal hash table with
02320  * the new entries found.
02321  *
02322  * Returns 0 in case of success, -1 in case of error.
02323  */
02324 static int
02325 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
02326                 const char *file, int super) {
02327     const xmlChar *cur = value;
02328     xmlChar *base = NULL;
02329     int res;
02330 
02331     if ((cur == NULL) || (file == NULL))
02332         return(-1);
02333     base = xmlStrdup((const xmlChar *) file);
02334 
02335     while ((cur != NULL) && (cur[0] != 0)) {
02336     SKIP_BLANKS;
02337     if (cur[0] == 0)
02338         break;
02339     if ((cur[0] == '-') && (cur[1] == '-')) {
02340         cur = xmlParseSGMLCatalogComment(cur);
02341         if (cur == NULL) {
02342         /* error */
02343         break;
02344         }
02345     } else {
02346         xmlChar *sysid = NULL;
02347         xmlChar *name = NULL;
02348         xmlCatalogEntryType type = XML_CATA_NONE;
02349 
02350         cur = xmlParseSGMLCatalogName(cur, &name);
02351         if (name == NULL) {
02352         /* error */
02353         break;
02354         }
02355         if (!IS_BLANK_CH(*cur)) {
02356         /* error */
02357         break;
02358         }
02359         SKIP_BLANKS;
02360         if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
02361                 type = SGML_CATA_SYSTEM;
02362         else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
02363                 type = SGML_CATA_PUBLIC;
02364         else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
02365                 type = SGML_CATA_DELEGATE;
02366         else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
02367                 type = SGML_CATA_ENTITY;
02368         else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
02369                 type = SGML_CATA_DOCTYPE;
02370         else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
02371                 type = SGML_CATA_LINKTYPE;
02372         else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
02373                 type = SGML_CATA_NOTATION;
02374         else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
02375                 type = SGML_CATA_SGMLDECL;
02376         else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
02377                 type = SGML_CATA_DOCUMENT;
02378         else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
02379                 type = SGML_CATA_CATALOG;
02380         else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
02381                 type = SGML_CATA_BASE;
02382         else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
02383         xmlFree(name);
02384         cur = xmlParseSGMLCatalogName(cur, &name);
02385         if (name == NULL) {
02386             /* error */
02387             break;
02388         }
02389         xmlFree(name);
02390         continue;
02391         }
02392         xmlFree(name);
02393         name = NULL;
02394 
02395         switch(type) {
02396         case SGML_CATA_ENTITY:
02397             if (*cur == '%')
02398             type = SGML_CATA_PENTITY;
02399         case SGML_CATA_PENTITY:
02400         case SGML_CATA_DOCTYPE:
02401         case SGML_CATA_LINKTYPE:
02402         case SGML_CATA_NOTATION:
02403             cur = xmlParseSGMLCatalogName(cur, &name);
02404             if (cur == NULL) {
02405             /* error */
02406             break;
02407             }
02408             if (!IS_BLANK_CH(*cur)) {
02409             /* error */
02410             break;
02411             }
02412             SKIP_BLANKS;
02413             cur = xmlParseSGMLCatalogPubid(cur, &sysid);
02414             if (cur == NULL) {
02415             /* error */
02416             break;
02417             }
02418             break;
02419         case SGML_CATA_PUBLIC:
02420         case SGML_CATA_SYSTEM:
02421         case SGML_CATA_DELEGATE:
02422             cur = xmlParseSGMLCatalogPubid(cur, &name);
02423             if (cur == NULL) {
02424             /* error */
02425             break;
02426             }
02427             if (type != SGML_CATA_SYSTEM) {
02428                 xmlChar *normid;
02429 
02430                 normid = xmlCatalogNormalizePublic(name);
02431                 if (normid != NULL) {
02432                     if (name != NULL)
02433                         xmlFree(name);
02434                     if (*normid != 0)
02435                         name = normid;
02436                     else {
02437                         xmlFree(normid);
02438                         name = NULL;
02439                     }
02440                 }
02441             }
02442             if (!IS_BLANK_CH(*cur)) {
02443             /* error */
02444             break;
02445             }
02446             SKIP_BLANKS;
02447             cur = xmlParseSGMLCatalogPubid(cur, &sysid);
02448             if (cur == NULL) {
02449             /* error */
02450             break;
02451             }
02452             break;
02453         case SGML_CATA_BASE:
02454         case SGML_CATA_CATALOG:
02455         case SGML_CATA_DOCUMENT:
02456         case SGML_CATA_SGMLDECL:
02457             cur = xmlParseSGMLCatalogPubid(cur, &sysid);
02458             if (cur == NULL) {
02459             /* error */
02460             break;
02461             }
02462             break;
02463         default:
02464             break;
02465         }
02466         if (cur == NULL) {
02467         if (name != NULL)
02468             xmlFree(name);
02469         if (sysid != NULL)
02470             xmlFree(sysid);
02471         break;
02472         } else if (type == SGML_CATA_BASE) {
02473         if (base != NULL)
02474             xmlFree(base);
02475         base = xmlStrdup(sysid);
02476         } else if ((type == SGML_CATA_PUBLIC) ||
02477                (type == SGML_CATA_SYSTEM)) {
02478         xmlChar *filename;
02479 
02480         filename = xmlBuildURI(sysid, base);
02481         if (filename != NULL) {
02482             xmlCatalogEntryPtr entry;
02483 
02484             entry = xmlNewCatalogEntry(type, name, filename,
02485                                    NULL, XML_CATA_PREFER_NONE, NULL);
02486             res = xmlHashAddEntry(catal->sgml, name, entry);
02487             if (res < 0) {
02488             xmlFreeCatalogEntry(entry);
02489             }
02490             xmlFree(filename);
02491         }
02492 
02493         } else if (type == SGML_CATA_CATALOG) {
02494         if (super) {
02495             xmlCatalogEntryPtr entry;
02496 
02497             entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
02498                                    XML_CATA_PREFER_NONE, NULL);
02499             res = xmlHashAddEntry(catal->sgml, sysid, entry);
02500             if (res < 0) {
02501             xmlFreeCatalogEntry(entry);
02502             }
02503         } else {
02504             xmlChar *filename;
02505 
02506             filename = xmlBuildURI(sysid, base);
02507             if (filename != NULL) {
02508             xmlExpandCatalog(catal, (const char *)filename);
02509             xmlFree(filename);
02510             }
02511         }
02512         }
02513         /*
02514          * drop anything else we won't handle it
02515          */
02516         if (name != NULL)
02517         xmlFree(name);
02518         if (sysid != NULL)
02519         xmlFree(sysid);
02520     }
02521     }
02522     if (base != NULL)
02523     xmlFree(base);
02524     if (cur == NULL)
02525     return(-1);
02526     return(0);
02527 }
02528 
02529 /************************************************************************
02530  *                                  *
02531  *          SGML Catalog handling               *
02532  *                                  *
02533  ************************************************************************/
02534 
02535 /**
02536  * xmlCatalogGetSGMLPublic:
02537  * @catal:  an SGML catalog hash
02538  * @pubID:  the public ID string
02539  *
02540  * Try to lookup the catalog local reference associated to a public ID
02541  *
02542  * Returns the local resource if found or NULL otherwise.
02543  */
02544 static const xmlChar *
02545 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
02546     xmlCatalogEntryPtr entry;
02547     xmlChar *normid;
02548 
02549     if (catal == NULL)
02550     return(NULL);
02551 
02552     normid = xmlCatalogNormalizePublic(pubID);
02553     if (normid != NULL)
02554         pubID = (*normid != 0 ? normid : NULL);
02555 
02556     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
02557     if (entry == NULL) {
02558     if (normid != NULL)
02559         xmlFree(normid);
02560     return(NULL);
02561     }
02562     if (entry->type == SGML_CATA_PUBLIC) {
02563     if (normid != NULL)
02564         xmlFree(normid);
02565     return(entry->URL);
02566     }
02567     if (normid != NULL)
02568         xmlFree(normid);
02569     return(NULL);
02570 }
02571 
02572 /**
02573  * xmlCatalogGetSGMLSystem:
02574  * @catal:  an SGML catalog hash
02575  * @sysID:  the system ID string
02576  *
02577  * Try to lookup the catalog local reference for a system ID
02578  *
02579  * Returns the local resource if found or NULL otherwise.
02580  */
02581 static const xmlChar *
02582 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
02583     xmlCatalogEntryPtr entry;
02584 
02585     if (catal == NULL)
02586     return(NULL);
02587 
02588     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
02589     if (entry == NULL)
02590     return(NULL);
02591     if (entry->type == SGML_CATA_SYSTEM)
02592     return(entry->URL);
02593     return(NULL);
02594 }
02595 
02596 /**
02597  * xmlCatalogSGMLResolve:
02598  * @catal:  the SGML catalog
02599  * @pubID:  the public ID string
02600  * @sysID:  the system ID string
02601  *
02602  * Do a complete resolution lookup of an External Identifier
02603  *
02604  * Returns the URI of the resource or NULL if not found
02605  */
02606 static const xmlChar *
02607 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
02608                   const xmlChar *sysID) {
02609     const xmlChar *ret = NULL;
02610 
02611     if (catal->sgml == NULL)
02612     return(NULL);
02613 
02614     if (pubID != NULL)
02615     ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
02616     if (ret != NULL)
02617     return(ret);
02618     if (sysID != NULL)
02619     ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
02620     if (ret != NULL)
02621     return(ret);
02622     return(NULL);
02623 }
02624 
02625 /************************************************************************
02626  *                                  *
02627  *          Specific Public interfaces          *
02628  *                                  *
02629  ************************************************************************/
02630 
02631 /**
02632  * xmlLoadSGMLSuperCatalog:
02633  * @filename:  a file path
02634  *
02635  * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
02636  * references. This is only needed for manipulating SGML Super Catalogs
02637  * like adding and removing CATALOG or DELEGATE entries.
02638  *
02639  * Returns the catalog parsed or NULL in case of error
02640  */
02641 xmlCatalogPtr
02642 xmlLoadSGMLSuperCatalog(const char *filename)
02643 {
02644     xmlChar *content;
02645     xmlCatalogPtr catal;
02646     int ret;
02647 
02648     content = xmlLoadFileContent(filename);
02649     if (content == NULL)
02650         return(NULL);
02651 
02652     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
02653     if (catal == NULL) {
02654     xmlFree(content);
02655     return(NULL);
02656     }
02657 
02658     ret = xmlParseSGMLCatalog(catal, content, filename, 1);
02659     xmlFree(content);
02660     if (ret < 0) {
02661     xmlFreeCatalog(catal);
02662     return(NULL);
02663     }
02664     return (catal);
02665 }
02666 
02667 /**
02668  * xmlLoadACatalog:
02669  * @filename:  a file path
02670  *
02671  * Load the catalog and build the associated data structures.
02672  * This can be either an XML Catalog or an SGML Catalog
02673  * It will recurse in SGML CATALOG entries. On the other hand XML
02674  * Catalogs are not handled recursively.
02675  *
02676  * Returns the catalog parsed or NULL in case of error
02677  */
02678 xmlCatalogPtr
02679 xmlLoadACatalog(const char *filename)
02680 {
02681     xmlChar *content;
02682     xmlChar *first;
02683     xmlCatalogPtr catal;
02684     int ret;
02685 
02686     content = xmlLoadFileContent(filename);
02687     if (content == NULL)
02688         return(NULL);
02689 
02690 
02691     first = content;
02692 
02693     while ((*first != 0) && (*first != '-') && (*first != '<') &&
02694        (!(((*first >= 'A') && (*first <= 'Z')) ||
02695           ((*first >= 'a') && (*first <= 'z')))))
02696     first++;
02697 
02698     if (*first != '<') {
02699     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
02700     if (catal == NULL) {
02701         xmlFree(content);
02702         return(NULL);
02703     }
02704         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
02705     if (ret < 0) {
02706         xmlFreeCatalog(catal);
02707         xmlFree(content);
02708         return(NULL);
02709     }
02710     } else {
02711     catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
02712     if (catal == NULL) {
02713         xmlFree(content);
02714         return(NULL);
02715     }
02716         catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
02717                NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
02718     }
02719     xmlFree(content);
02720     return (catal);
02721 }
02722 
02723 /**
02724  * xmlExpandCatalog:
02725  * @catal:  a catalog
02726  * @filename:  a file path
02727  *
02728  * Load the catalog and expand the existing catal structure.
02729  * This can be either an XML Catalog or an SGML Catalog
02730  *
02731  * Returns 0 in case of success, -1 in case of error
02732  */
02733 static int
02734 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
02735 {
02736     int ret;
02737 
02738     if ((catal == NULL) || (filename == NULL))
02739     return(-1);
02740 
02741 
02742     if (catal->type == XML_SGML_CATALOG_TYPE) {
02743     xmlChar *content;
02744 
02745     content = xmlLoadFileContent(filename);
02746     if (content == NULL)
02747         return(-1);
02748 
02749         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
02750     if (ret < 0) {
02751         xmlFree(content);
02752         return(-1);
02753     }
02754     xmlFree(content);
02755     } else {
02756     xmlCatalogEntryPtr tmp, cur;
02757     tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
02758                NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
02759 
02760     cur = catal->xml;
02761     if (cur == NULL) {
02762         catal->xml = tmp;
02763     } else {
02764         while (cur->next != NULL) cur = cur->next;
02765         cur->next = tmp;
02766     }
02767     }
02768     return (0);
02769 }
02770 
02771 /**
02772  * xmlACatalogResolveSystem:
02773  * @catal:  a Catalog
02774  * @sysID:  the system ID string
02775  *
02776  * Try to lookup the catalog resource for a system ID
02777  *
02778  * Returns the resource if found or NULL otherwise, the value returned
02779  *      must be freed by the caller.
02780  */
02781 xmlChar *
02782 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
02783     xmlChar *ret = NULL;
02784 
02785     if ((sysID == NULL) || (catal == NULL))
02786     return(NULL);
02787 
02788     if (xmlDebugCatalogs)
02789     xmlGenericError(xmlGenericErrorContext,
02790         "Resolve sysID %s\n", sysID);
02791 
02792     if (catal->type == XML_XML_CATALOG_TYPE) {
02793     ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
02794     if (ret == XML_CATAL_BREAK)
02795         ret = NULL;
02796     } else {
02797     const xmlChar *sgml;
02798 
02799     sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
02800     if (sgml != NULL)
02801         ret = xmlStrdup(sgml);
02802     }
02803     return(ret);
02804 }
02805 
02806 /**
02807  * xmlACatalogResolvePublic:
02808  * @catal:  a Catalog
02809  * @pubID:  the public ID string
02810  *
02811  * Try to lookup the catalog local reference associated to a public ID in that catalog
02812  *
02813  * Returns the local resource if found or NULL otherwise, the value returned
02814  *      must be freed by the caller.
02815  */
02816 xmlChar *
02817 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
02818     xmlChar *ret = NULL;
02819 
02820     if ((pubID == NULL) || (catal == NULL))
02821     return(NULL);
02822 
02823     if (xmlDebugCatalogs)
02824     xmlGenericError(xmlGenericErrorContext,
02825         "Resolve pubID %s\n", pubID);
02826 
02827     if (catal->type == XML_XML_CATALOG_TYPE) {
02828     ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
02829     if (ret == XML_CATAL_BREAK)
02830         ret = NULL;
02831     } else {
02832     const xmlChar *sgml;
02833 
02834     sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
02835     if (sgml != NULL)
02836         ret = xmlStrdup(sgml);
02837     }
02838     return(ret);
02839 }
02840 
02841 /**
02842  * xmlACatalogResolve:
02843  * @catal:  a Catalog
02844  * @pubID:  the public ID string
02845  * @sysID:  the system ID string
02846  *
02847  * Do a complete resolution lookup of an External Identifier
02848  *
02849  * Returns the URI of the resource or NULL if not found, it must be freed
02850  *      by the caller.
02851  */
02852 xmlChar *
02853 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
02854                    const xmlChar * sysID)
02855 {
02856     xmlChar *ret = NULL;
02857 
02858     if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
02859         return (NULL);
02860 
02861     if (xmlDebugCatalogs) {
02862          if ((pubID != NULL) && (sysID != NULL)) {
02863              xmlGenericError(xmlGenericErrorContext,
02864                              "Resolve: pubID %s sysID %s\n", pubID, sysID);
02865          } else if (pubID != NULL) {
02866              xmlGenericError(xmlGenericErrorContext,
02867                              "Resolve: pubID %s\n", pubID);
02868          } else {
02869              xmlGenericError(xmlGenericErrorContext,
02870                              "Resolve: sysID %s\n", sysID);
02871          }
02872     }
02873 
02874     if (catal->type == XML_XML_CATALOG_TYPE) {
02875         ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
02876     if (ret == XML_CATAL_BREAK)
02877         ret = NULL;
02878     } else {
02879         const xmlChar *sgml;
02880 
02881         sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
02882         if (sgml != NULL)
02883             ret = xmlStrdup(sgml);
02884     }
02885     return (ret);
02886 }
02887 
02888 /**
02889  * xmlACatalogResolveURI:
02890  * @catal:  a Catalog
02891  * @URI:  the URI
02892  *
02893  * Do a complete resolution lookup of an URI
02894  *
02895  * Returns the URI of the resource or NULL if not found, it must be freed
02896  *      by the caller.
02897  */
02898 xmlChar *
02899 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
02900     xmlChar *ret = NULL;
02901 
02902     if ((URI == NULL) || (catal == NULL))
02903     return(NULL);
02904 
02905     if (xmlDebugCatalogs)
02906     xmlGenericError(xmlGenericErrorContext,
02907         "Resolve URI %s\n", URI);
02908 
02909     if (catal->type == XML_XML_CATALOG_TYPE) {
02910     ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
02911     if (ret == XML_CATAL_BREAK)
02912         ret = NULL;
02913     } else {
02914     const xmlChar *sgml;
02915 
02916     sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
02917     if (sgml != NULL)
02918             ret = xmlStrdup(sgml);
02919     }
02920     return(ret);
02921 }
02922 
02923 #ifdef LIBXML_OUTPUT_ENABLED
02924 /**
02925  * xmlACatalogDump:
02926  * @catal:  a Catalog
02927  * @out:  the file.
02928  *
02929  * Dump the given catalog to the given file.
02930  */
02931 void
02932 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
02933     if ((out == NULL) || (catal == NULL))
02934     return;
02935 
02936     if (catal->type == XML_XML_CATALOG_TYPE) {
02937     xmlDumpXMLCatalog(out, catal->xml);
02938     } else {
02939     xmlHashScan(catal->sgml,
02940             (xmlHashScanner) xmlCatalogDumpEntry, out);
02941     }
02942 }
02943 #endif /* LIBXML_OUTPUT_ENABLED */
02944 
02945 /**
02946  * xmlACatalogAdd:
02947  * @catal:  a Catalog
02948  * @type:  the type of record to add to the catalog
02949  * @orig:  the system, public or prefix to match
02950  * @replace:  the replacement value for the match
02951  *
02952  * Add an entry in the catalog, it may overwrite existing but
02953  * different entries.
02954  *
02955  * Returns 0 if successful, -1 otherwise
02956  */
02957 int
02958 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
02959               const xmlChar * orig, const xmlChar * replace)
02960 {
02961     int res = -1;
02962 
02963     if (catal == NULL)
02964     return(-1);
02965 
02966     if (catal->type == XML_XML_CATALOG_TYPE) {
02967         res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
02968     } else {
02969         xmlCatalogEntryType cattype;
02970 
02971         cattype = xmlGetSGMLCatalogEntryType(type);
02972         if (cattype != XML_CATA_NONE) {
02973             xmlCatalogEntryPtr entry;
02974 
02975             entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
02976                                        XML_CATA_PREFER_NONE, NULL);
02977         if (catal->sgml == NULL)
02978         catal->sgml = xmlHashCreate(10);
02979             res = xmlHashAddEntry(catal->sgml, orig, entry);
02980         }
02981     }
02982     return (res);
02983 }
02984 
02985 /**
02986  * xmlACatalogRemove:
02987  * @catal:  a Catalog
02988  * @value:  the value to remove
02989  *
02990  * Remove an entry from the catalog
02991  *
02992  * Returns the number of entries removed if successful, -1 otherwise
02993  */
02994 int
02995 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
02996     int res = -1;
02997 
02998     if ((catal == NULL) || (value == NULL))
02999     return(-1);
03000 
03001     if (catal->type == XML_XML_CATALOG_TYPE) {
03002     res = xmlDelXMLCatalog(catal->xml, value);
03003     } else {
03004     res = xmlHashRemoveEntry(catal->sgml, value,
03005         (xmlHashDeallocator) xmlFreeCatalogEntry);
03006     if (res == 0)
03007         res = 1;
03008     }
03009     return(res);
03010 }
03011 
03012 /**
03013  * xmlNewCatalog:
03014  * @sgml:  should this create an SGML catalog
03015  *
03016  * create a new Catalog.
03017  *
03018  * Returns the xmlCatalogPtr or NULL in case of error
03019  */
03020 xmlCatalogPtr
03021 xmlNewCatalog(int sgml) {
03022     xmlCatalogPtr catal = NULL;
03023 
03024     if (sgml) {
03025     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
03026                             xmlCatalogDefaultPrefer);
03027         if ((catal != NULL) && (catal->sgml == NULL))
03028         catal->sgml = xmlHashCreate(10);
03029     } else
03030     catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
03031                             xmlCatalogDefaultPrefer);
03032     return(catal);
03033 }
03034 
03035 /**
03036  * xmlCatalogIsEmpty:
03037  * @catal:  should this create an SGML catalog
03038  *
03039  * Check is a catalog is empty
03040  *
03041  * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
03042  */
03043 int
03044 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
03045     if (catal == NULL)
03046     return(-1);
03047 
03048     if (catal->type == XML_XML_CATALOG_TYPE) {
03049     if (catal->xml == NULL)
03050         return(1);
03051     if ((catal->xml->type != XML_CATA_CATALOG) &&
03052         (catal->xml->type != XML_CATA_BROKEN_CATALOG))
03053         return(-1);
03054     if (catal->xml->children == NULL)
03055         return(1);
03056         return(0);
03057     } else {
03058     int res;
03059 
03060     if (catal->sgml == NULL)
03061         return(1);
03062     res = xmlHashSize(catal->sgml);
03063     if (res == 0)
03064         return(1);
03065     if (res < 0)
03066         return(-1);
03067     }
03068     return(0);
03069 }
03070 
03071 /************************************************************************
03072  *                                  *
03073  *   Public interfaces manipulating the global shared default catalog   *
03074  *                                  *
03075  ************************************************************************/
03076 
03077 /**
03078  * xmlInitializeCatalogData:
03079  *
03080  * Do the catalog initialization only of global data, doesn't try to load
03081  * any catalog actually.
03082  * this function is not thread safe, catalog initialization should
03083  * preferably be done once at startup
03084  */
03085 static void
03086 xmlInitializeCatalogData(void) {
03087     if (xmlCatalogInitialized != 0)
03088     return;
03089 
03090     if (getenv("XML_DEBUG_CATALOG"))
03091     xmlDebugCatalogs = 1;
03092     xmlCatalogMutex = xmlNewRMutex();
03093 
03094     xmlCatalogInitialized = 1;
03095 }
03096 /**
03097  * xmlInitializeCatalog:
03098  *
03099  * Do the catalog initialization.
03100  * this function is not thread safe, catalog initialization should
03101  * preferably be done once at startup
03102  */
03103 void
03104 xmlInitializeCatalog(void) {
03105     if (xmlCatalogInitialized != 0)
03106     return;
03107 
03108     xmlInitializeCatalogData();
03109     xmlRMutexLock(xmlCatalogMutex);
03110 
03111     if (getenv("XML_DEBUG_CATALOG"))
03112     xmlDebugCatalogs = 1;
03113 
03114     if (xmlDefaultCatalog == NULL) {
03115     const char *catalogs;
03116     char *path;
03117     const char *cur, *paths;
03118     xmlCatalogPtr catal;
03119     xmlCatalogEntryPtr *nextent;
03120 
03121     catalogs = (const char *) getenv("XML_CATALOG_FILES");
03122     if (catalogs == NULL)
03123 #if defined(_WIN32) && defined(_MSC_VER)
03124     {
03125         void* hmodule;
03126         hmodule = GetModuleHandleA("libxml2.dll");
03127         if (hmodule == NULL)
03128             hmodule = GetModuleHandleA(NULL);
03129         if (hmodule != NULL) {
03130             char buf[256];
03131             unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
03132             if (len != 0) {
03133                 char* p = &(buf[len]);
03134                 while (*p != '\\' && p > buf)
03135                     p--;
03136                 if (p != buf) {
03137                     xmlChar* uri;
03138                     strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
03139                     uri = xmlCanonicPath((const xmlChar*)buf);
03140                     if (uri != NULL) {
03141                         strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
03142                         xmlFree(uri);
03143                     }
03144                 }
03145             }
03146         }
03147         catalogs = XML_XML_DEFAULT_CATALOG;
03148     }
03149 #else
03150         catalogs = XML_XML_DEFAULT_CATALOG;
03151 #endif
03152 
03153     catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
03154         xmlCatalogDefaultPrefer);
03155     if (catal != NULL) {
03156         /* the XML_CATALOG_FILES envvar is allowed to contain a
03157            space-separated list of entries. */
03158         cur = catalogs;
03159         nextent = &catal->xml;
03160         while (*cur != '\0') {
03161         while (xmlIsBlank_ch(*cur))
03162             cur++;
03163         if (*cur != 0) {
03164             paths = cur;
03165             while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
03166             cur++;
03167             path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
03168             if (path != NULL) {
03169             *nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
03170                 NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
03171             if (*nextent != NULL)
03172                 nextent = &((*nextent)->next);
03173             xmlFree(path);
03174             }
03175         }
03176         }
03177         xmlDefaultCatalog = catal;
03178     }
03179     }
03180 
03181     xmlRMutexUnlock(xmlCatalogMutex);
03182 }
03183 
03184 
03185 /**
03186  * xmlLoadCatalog:
03187  * @filename:  a file path
03188  *
03189  * Load the catalog and makes its definitions effective for the default
03190  * external entity loader. It will recurse in SGML CATALOG entries.
03191  * this function is not thread safe, catalog initialization should
03192  * preferably be done once at startup
03193  *
03194  * Returns 0 in case of success -1 in case of error
03195  */
03196 int
03197 xmlLoadCatalog(const char *filename)
03198 {
03199     int ret;
03200     xmlCatalogPtr catal;
03201 
03202     if (!xmlCatalogInitialized)
03203     xmlInitializeCatalogData();
03204 
03205     xmlRMutexLock(xmlCatalogMutex);
03206 
03207     if (xmlDefaultCatalog == NULL) {
03208     catal = xmlLoadACatalog(filename);
03209     if (catal == NULL) {
03210         xmlRMutexUnlock(xmlCatalogMutex);
03211         return(-1);
03212     }
03213 
03214     xmlDefaultCatalog = catal;
03215     xmlRMutexUnlock(xmlCatalogMutex);
03216     return(0);
03217     }
03218 
03219     ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
03220     xmlRMutexUnlock(xmlCatalogMutex);
03221     return(ret);
03222 }
03223 
03224 /**
03225  * xmlLoadCatalogs:
03226  * @pathss:  a list of directories separated by a colon or a space.
03227  *
03228  * Load the catalogs and makes their definitions effective for the default
03229  * external entity loader.
03230  * this function is not thread safe, catalog initialization should
03231  * preferably be done once at startup
03232  */
03233 void
03234 xmlLoadCatalogs(const char *pathss) {
03235     const char *cur;
03236     const char *paths;
03237     xmlChar *path;
03238 #ifdef _WIN32
03239     int i, iLen;
03240 #endif
03241 
03242     if (pathss == NULL)
03243     return;
03244 
03245     cur = pathss;
03246     while (*cur != 0) {
03247     while (xmlIsBlank_ch(*cur)) cur++;
03248     if (*cur != 0) {
03249         paths = cur;
03250         while ((*cur != 0) && (*cur != PATH_SEAPARATOR) && (!xmlIsBlank_ch(*cur)))
03251         cur++;
03252         path = xmlStrndup((const xmlChar *)paths, cur - paths);
03253 #ifdef _WIN32
03254         iLen = strlen((const char*)path);
03255         for(i = 0; i < iLen; i++) {
03256             if(path[i] == '\\') {
03257                 path[i] = '/';
03258             }
03259         }
03260 #endif
03261         if (path != NULL) {
03262         xmlLoadCatalog((const char *) path);
03263         xmlFree(path);
03264         }
03265     }
03266     while (*cur == PATH_SEAPARATOR)
03267         cur++;
03268     }
03269 }
03270 
03271 /**
03272  * xmlCatalogCleanup:
03273  *
03274  * Free up all the memory associated with catalogs
03275  */
03276 void
03277 xmlCatalogCleanup(void) {
03278     if (xmlCatalogInitialized == 0)
03279         return;
03280 
03281     xmlRMutexLock(xmlCatalogMutex);
03282     if (xmlDebugCatalogs)
03283     xmlGenericError(xmlGenericErrorContext,
03284         "Catalogs cleanup\n");
03285     if (xmlCatalogXMLFiles != NULL)
03286     xmlHashFree(xmlCatalogXMLFiles,
03287             (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
03288     xmlCatalogXMLFiles = NULL;
03289     if (xmlDefaultCatalog != NULL)
03290     xmlFreeCatalog(xmlDefaultCatalog);
03291     xmlDefaultCatalog = NULL;
03292     xmlDebugCatalogs = 0;
03293     xmlCatalogInitialized = 0;
03294     xmlRMutexUnlock(xmlCatalogMutex);
03295     xmlFreeRMutex(xmlCatalogMutex);
03296 }
03297 
03298 /**
03299  * xmlCatalogResolveSystem:
03300  * @sysID:  the system ID string
03301  *
03302  * Try to lookup the catalog resource for a system ID
03303  *
03304  * Returns the resource if found or NULL otherwise, the value returned
03305  *      must be freed by the caller.
03306  */
03307 xmlChar *
03308 xmlCatalogResolveSystem(const xmlChar *sysID) {
03309     xmlChar *ret;
03310 
03311     if (!xmlCatalogInitialized)
03312     xmlInitializeCatalog();
03313 
03314     ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
03315     return(ret);
03316 }
03317 
03318 /**
03319  * xmlCatalogResolvePublic:
03320  * @pubID:  the public ID string
03321  *
03322  * Try to lookup the catalog reference associated to a public ID
03323  *
03324  * Returns the resource if found or NULL otherwise, the value returned
03325  *      must be freed by the caller.
03326  */
03327 xmlChar *
03328 xmlCatalogResolvePublic(const xmlChar *pubID) {
03329     xmlChar *ret;
03330 
03331     if (!xmlCatalogInitialized)
03332     xmlInitializeCatalog();
03333 
03334     ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
03335     return(ret);
03336 }
03337 
03338 /**
03339  * xmlCatalogResolve:
03340  * @pubID:  the public ID string
03341  * @sysID:  the system ID string
03342  *
03343  * Do a complete resolution lookup of an External Identifier
03344  *
03345  * Returns the URI of the resource or NULL if not found, it must be freed
03346  *      by the caller.
03347  */
03348 xmlChar *
03349 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
03350     xmlChar *ret;
03351 
03352     if (!xmlCatalogInitialized)
03353     xmlInitializeCatalog();
03354 
03355     ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
03356     return(ret);
03357 }
03358 
03359 /**
03360  * xmlCatalogResolveURI:
03361  * @URI:  the URI
03362  *
03363  * Do a complete resolution lookup of an URI
03364  *
03365  * Returns the URI of the resource or NULL if not found, it must be freed
03366  *      by the caller.
03367  */
03368 xmlChar *
03369 xmlCatalogResolveURI(const xmlChar *URI) {
03370     xmlChar *ret;
03371 
03372     if (!xmlCatalogInitialized)
03373     xmlInitializeCatalog();
03374 
03375     ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
03376     return(ret);
03377 }
03378 
03379 #ifdef LIBXML_OUTPUT_ENABLED
03380 /**
03381  * xmlCatalogDump:
03382  * @out:  the file.
03383  *
03384  * Dump all the global catalog content to the given file.
03385  */
03386 void
03387 xmlCatalogDump(FILE *out) {
03388     if (out == NULL)
03389     return;
03390 
03391     if (!xmlCatalogInitialized)
03392     xmlInitializeCatalog();
03393 
03394     xmlACatalogDump(xmlDefaultCatalog, out);
03395 }
03396 #endif /* LIBXML_OUTPUT_ENABLED */
03397 
03398 /**
03399  * xmlCatalogAdd:
03400  * @type:  the type of record to add to the catalog
03401  * @orig:  the system, public or prefix to match
03402  * @replace:  the replacement value for the match
03403  *
03404  * Add an entry in the catalog, it may overwrite existing but
03405  * different entries.
03406  * If called before any other catalog routine, allows to override the
03407  * default shared catalog put in place by xmlInitializeCatalog();
03408  *
03409  * Returns 0 if successful, -1 otherwise
03410  */
03411 int
03412 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
03413     int res = -1;
03414 
03415     if (!xmlCatalogInitialized)
03416     xmlInitializeCatalogData();
03417 
03418     xmlRMutexLock(xmlCatalogMutex);
03419     /*
03420      * Specific case where one want to override the default catalog
03421      * put in place by xmlInitializeCatalog();
03422      */
03423     if ((xmlDefaultCatalog == NULL) &&
03424     (xmlStrEqual(type, BAD_CAST "catalog"))) {
03425     xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
03426                                   xmlCatalogDefaultPrefer);
03427     xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
03428                     orig, NULL,  xmlCatalogDefaultPrefer, NULL);
03429 
03430     xmlRMutexUnlock(xmlCatalogMutex);
03431     return(0);
03432     }
03433 
03434     res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
03435     xmlRMutexUnlock(xmlCatalogMutex);
03436     return(res);
03437 }
03438 
03439 /**
03440  * xmlCatalogRemove:
03441  * @value:  the value to remove
03442  *
03443  * Remove an entry from the catalog
03444  *
03445  * Returns the number of entries removed if successful, -1 otherwise
03446  */
03447 int
03448 xmlCatalogRemove(const xmlChar *value) {
03449     int res;
03450 
03451     if (!xmlCatalogInitialized)
03452     xmlInitializeCatalog();
03453 
03454     xmlRMutexLock(xmlCatalogMutex);
03455     res = xmlACatalogRemove(xmlDefaultCatalog, value);
03456     xmlRMutexUnlock(xmlCatalogMutex);
03457     return(res);
03458 }
03459 
03460 /**
03461  * xmlCatalogConvert:
03462  *
03463  * Convert all the SGML catalog entries as XML ones
03464  *
03465  * Returns the number of entries converted if successful, -1 otherwise
03466  */
03467 int
03468 xmlCatalogConvert(void) {
03469     int res = -1;
03470 
03471     if (!xmlCatalogInitialized)
03472     xmlInitializeCatalog();
03473 
03474     xmlRMutexLock(xmlCatalogMutex);
03475     res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
03476     xmlRMutexUnlock(xmlCatalogMutex);
03477     return(res);
03478 }
03479 
03480 /************************************************************************
03481  *                                  *
03482  *  Public interface manipulating the common preferences        *
03483  *                                  *
03484  ************************************************************************/
03485 
03486 /**
03487  * xmlCatalogGetDefaults:
03488  *
03489  * Used to get the user preference w.r.t. to what catalogs should
03490  * be accepted
03491  *
03492  * Returns the current xmlCatalogAllow value
03493  */
03494 xmlCatalogAllow
03495 xmlCatalogGetDefaults(void) {
03496     return(xmlCatalogDefaultAllow);
03497 }
03498 
03499 /**
03500  * xmlCatalogSetDefaults:
03501  * @allow:  what catalogs should be accepted
03502  *
03503  * Used to set the user preference w.r.t. to what catalogs should
03504  * be accepted
03505  */
03506 void
03507 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
03508     if (xmlDebugCatalogs) {
03509     switch (allow) {
03510         case XML_CATA_ALLOW_NONE:
03511         xmlGenericError(xmlGenericErrorContext,
03512             "Disabling catalog usage\n");
03513         break;
03514         case XML_CATA_ALLOW_GLOBAL:
03515         xmlGenericError(xmlGenericErrorContext,
03516             "Allowing only global catalogs\n");
03517         break;
03518         case XML_CATA_ALLOW_DOCUMENT:
03519         xmlGenericError(xmlGenericErrorContext,
03520             "Allowing only catalogs from the document\n");
03521         break;
03522         case XML_CATA_ALLOW_ALL:
03523         xmlGenericError(xmlGenericErrorContext,
03524             "Allowing all catalogs\n");
03525         break;
03526     }
03527     }
03528     xmlCatalogDefaultAllow = allow;
03529 }
03530 
03531 /**
03532  * xmlCatalogSetDefaultPrefer:
03533  * @prefer:  the default preference for delegation
03534  *
03535  * Allows to set the preference between public and system for deletion
03536  * in XML Catalog resolution. C.f. section 4.1.1 of the spec
03537  * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
03538  *
03539  * Returns the previous value of the default preference for delegation
03540  */
03541 xmlCatalogPrefer
03542 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
03543     xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
03544 
03545     if (prefer == XML_CATA_PREFER_NONE)
03546     return(ret);
03547 
03548     if (xmlDebugCatalogs) {
03549     switch (prefer) {
03550         case XML_CATA_PREFER_PUBLIC:
03551         xmlGenericError(xmlGenericErrorContext,
03552             "Setting catalog preference to PUBLIC\n");
03553         break;
03554         case XML_CATA_PREFER_SYSTEM:
03555         xmlGenericError(xmlGenericErrorContext,
03556             "Setting catalog preference to SYSTEM\n");
03557         break;
03558         default:
03559         return(ret);
03560     }
03561     }
03562     xmlCatalogDefaultPrefer = prefer;
03563     return(ret);
03564 }
03565 
03566 /**
03567  * xmlCatalogSetDebug:
03568  * @level:  the debug level of catalogs required
03569  *
03570  * Used to set the debug level for catalog operation, 0 disable
03571  * debugging, 1 enable it
03572  *
03573  * Returns the previous value of the catalog debugging level
03574  */
03575 int
03576 xmlCatalogSetDebug(int level) {
03577     int ret = xmlDebugCatalogs;
03578 
03579     if (level <= 0)
03580         xmlDebugCatalogs = 0;
03581     else
03582     xmlDebugCatalogs = level;
03583     return(ret);
03584 }
03585 
03586 /************************************************************************
03587  *                                  *
03588  *   Minimal interfaces used for per-document catalogs by the parser    *
03589  *                                  *
03590  ************************************************************************/
03591 
03592 /**
03593  * xmlCatalogFreeLocal:
03594  * @catalogs:  a document's list of catalogs
03595  *
03596  * Free up the memory associated to the catalog list
03597  */
03598 void
03599 xmlCatalogFreeLocal(void *catalogs) {
03600     xmlCatalogEntryPtr catal;
03601 
03602     if (!xmlCatalogInitialized)
03603     xmlInitializeCatalog();
03604 
03605     catal = (xmlCatalogEntryPtr) catalogs;
03606     if (catal != NULL)
03607     xmlFreeCatalogEntryList(catal);
03608 }
03609 
03610 
03611 /**
03612  * xmlCatalogAddLocal:
03613  * @catalogs:  a document's list of catalogs
03614  * @URL:  the URL to a new local catalog
03615  *
03616  * Add the new entry to the catalog list
03617  *
03618  * Returns the updated list
03619  */
03620 void *
03621 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
03622     xmlCatalogEntryPtr catal, add;
03623 
03624     if (!xmlCatalogInitialized)
03625     xmlInitializeCatalog();
03626 
03627     if (URL == NULL)
03628     return(catalogs);
03629 
03630     if (xmlDebugCatalogs)
03631     xmlGenericError(xmlGenericErrorContext,
03632         "Adding document catalog %s\n", URL);
03633 
03634     add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
03635                          xmlCatalogDefaultPrefer, NULL);
03636     if (add == NULL)
03637     return(catalogs);
03638 
03639     catal = (xmlCatalogEntryPtr) catalogs;
03640     if (catal == NULL)
03641     return((void *) add);
03642 
03643     while (catal->next != NULL)
03644     catal = catal->next;
03645     catal->next = add;
03646     return(catalogs);
03647 }
03648 
03649 /**
03650  * xmlCatalogLocalResolve:
03651  * @catalogs:  a document's list of catalogs
03652  * @pubID:  the public ID string
03653  * @sysID:  the system ID string
03654  *
03655  * Do a complete resolution lookup of an External Identifier using a
03656  * document's private catalog list
03657  *
03658  * Returns the URI of the resource or NULL if not found, it must be freed
03659  *      by the caller.
03660  */
03661 xmlChar *
03662 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
03663                    const xmlChar *sysID) {
03664     xmlCatalogEntryPtr catal;
03665     xmlChar *ret;
03666 
03667     if (!xmlCatalogInitialized)
03668     xmlInitializeCatalog();
03669 
03670     if ((pubID == NULL) && (sysID == NULL))
03671     return(NULL);
03672 
03673     if (xmlDebugCatalogs) {
03674         if ((pubID != NULL) && (sysID != NULL)) {
03675             xmlGenericError(xmlGenericErrorContext,
03676                             "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
03677         } else if (pubID != NULL) {
03678             xmlGenericError(xmlGenericErrorContext,
03679                             "Local Resolve: pubID %s\n", pubID);
03680         } else {
03681             xmlGenericError(xmlGenericErrorContext,
03682                             "Local Resolve: sysID %s\n", sysID);
03683         }
03684     }
03685 
03686     catal = (xmlCatalogEntryPtr) catalogs;
03687     if (catal == NULL)
03688     return(NULL);
03689     ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
03690     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
03691     return(ret);
03692     return(NULL);
03693 }
03694 
03695 /**
03696  * xmlCatalogLocalResolveURI:
03697  * @catalogs:  a document's list of catalogs
03698  * @URI:  the URI
03699  *
03700  * Do a complete resolution lookup of an URI using a
03701  * document's private catalog list
03702  *
03703  * Returns the URI of the resource or NULL if not found, it must be freed
03704  *      by the caller.
03705  */
03706 xmlChar *
03707 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
03708     xmlCatalogEntryPtr catal;
03709     xmlChar *ret;
03710 
03711     if (!xmlCatalogInitialized)
03712     xmlInitializeCatalog();
03713 
03714     if (URI == NULL)
03715     return(NULL);
03716 
03717     if (xmlDebugCatalogs)
03718     xmlGenericError(xmlGenericErrorContext,
03719         "Resolve URI %s\n", URI);
03720 
03721     catal = (xmlCatalogEntryPtr) catalogs;
03722     if (catal == NULL)
03723     return(NULL);
03724     ret = xmlCatalogListXMLResolveURI(catal, URI);
03725     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
03726     return(ret);
03727     return(NULL);
03728 }
03729 
03730 /************************************************************************
03731  *                                  *
03732  *          Deprecated interfaces               *
03733  *                                  *
03734  ************************************************************************/
03735 /**
03736  * xmlCatalogGetSystem:
03737  * @sysID:  the system ID string
03738  *
03739  * Try to lookup the catalog reference associated to a system ID
03740  * DEPRECATED, use xmlCatalogResolveSystem()
03741  *
03742  * Returns the resource if found or NULL otherwise.
03743  */
03744 const xmlChar *
03745 xmlCatalogGetSystem(const xmlChar *sysID) {
03746     xmlChar *ret;
03747     static xmlChar result[1000];
03748     static int msg = 0;
03749 
03750     if (!xmlCatalogInitialized)
03751     xmlInitializeCatalog();
03752 
03753     if (msg == 0) {
03754     xmlGenericError(xmlGenericErrorContext,
03755         "Use of deprecated xmlCatalogGetSystem() call\n");
03756     msg++;
03757     }
03758 
03759     if (sysID == NULL)
03760     return(NULL);
03761 
03762     /*
03763      * Check first the XML catalogs
03764      */
03765     if (xmlDefaultCatalog != NULL) {
03766     ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
03767     if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
03768         snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
03769         result[sizeof(result) - 1] = 0;
03770         return(result);
03771     }
03772     }
03773 
03774     if (xmlDefaultCatalog != NULL)
03775     return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
03776     return(NULL);
03777 }
03778 
03779 /**
03780  * xmlCatalogGetPublic:
03781  * @pubID:  the public ID string
03782  *
03783  * Try to lookup the catalog reference associated to a public ID
03784  * DEPRECATED, use xmlCatalogResolvePublic()
03785  *
03786  * Returns the resource if found or NULL otherwise.
03787  */
03788 const xmlChar *
03789 xmlCatalogGetPublic(const xmlChar *pubID) {
03790     xmlChar *ret;
03791     static xmlChar result[1000];
03792     static int msg = 0;
03793 
03794     if (!xmlCatalogInitialized)
03795     xmlInitializeCatalog();
03796 
03797     if (msg == 0) {
03798     xmlGenericError(xmlGenericErrorContext,
03799         "Use of deprecated xmlCatalogGetPublic() call\n");
03800     msg++;
03801     }
03802 
03803     if (pubID == NULL)
03804     return(NULL);
03805 
03806     /*
03807      * Check first the XML catalogs
03808      */
03809     if (xmlDefaultCatalog != NULL) {
03810     ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
03811     if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
03812         snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
03813         result[sizeof(result) - 1] = 0;
03814         return(result);
03815     }
03816     }
03817 
03818     if (xmlDefaultCatalog != NULL)
03819     return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
03820     return(NULL);
03821 }
03822 
03823 #define bottom_catalog
03824 #include "elfgcchack.h"
03825 #endif /* LIBXML_CATALOG_ENABLED */
03826