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