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