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