Home | History | Annotate | Download | only in src
      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 #else
   1001     len = fread(content, 1, size, fd);
   1002 #endif
   1003     if (len < 0) {
   1004         xmlFree(content);
   1005         return (NULL);
   1006     }
   1007 #ifdef HAVE_STAT
   1008     close(fd);
   1009 #else
   1010     fclose(fd);
   1011 #endif
   1012     content[len] = 0;
   1013 
   1014     return(content);
   1015 }
   1016 
   1017 /**
   1018  * xmlCatalogNormalizePublic:
   1019  * @pubID:  the public ID string
   1020  *
   1021  *  Normalizes the Public Identifier
   1022  *
   1023  * Implements 6.2. Public Identifier Normalization
   1024  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
   1025  *
   1026  * Returns the new string or NULL, the string must be deallocated
   1027  *         by the caller.
   1028  */
   1029 static xmlChar *
   1030 xmlCatalogNormalizePublic(const xmlChar *pubID)
   1031 {
   1032     int ok = 1;
   1033     int white;
   1034     const xmlChar *p;
   1035     xmlChar *ret;
   1036     xmlChar *q;
   1037 
   1038     if (pubID == NULL)
   1039         return(NULL);
   1040 
   1041     white = 1;
   1042     for (p = pubID;*p != 0 && ok;p++) {
   1043         if (!xmlIsBlank_ch(*p))
   1044             white = 0;
   1045         else if (*p == 0x20 && !white)
   1046             white = 1;
   1047         else
   1048             ok = 0;
   1049     }
   1050     if (ok && !white)	/* is normalized */
   1051         return(NULL);
   1052 
   1053     ret = xmlStrdup(pubID);
   1054     q = ret;
   1055     white = 0;
   1056     for (p = pubID;*p != 0;p++) {
   1057         if (xmlIsBlank_ch(*p)) {
   1058             if (q != ret)
   1059                 white = 1;
   1060         } else {
   1061             if (white) {
   1062                 *(q++) = 0x20;
   1063                 white = 0;
   1064             }
   1065             *(q++) = *p;
   1066         }
   1067     }
   1068     *q = 0;
   1069     return(ret);
   1070 }
   1071 
   1072 /************************************************************************
   1073  *									*
   1074  *			The XML Catalog parser				*
   1075  *									*
   1076  ************************************************************************/
   1077 
   1078 static xmlCatalogEntryPtr
   1079 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
   1080 static void
   1081 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
   1082 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
   1083 static xmlChar *
   1084 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
   1085 	              const xmlChar *sysID);
   1086 static xmlChar *
   1087 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
   1088 
   1089 
   1090 /**
   1091  * xmlGetXMLCatalogEntryType:
   1092  * @name:  the name
   1093  *
   1094  * lookup the internal type associated to an XML catalog entry name
   1095  *
   1096  * Returns the type associated with that name
   1097  */
   1098 static xmlCatalogEntryType
   1099 xmlGetXMLCatalogEntryType(const xmlChar *name) {
   1100     xmlCatalogEntryType type = XML_CATA_NONE;
   1101     if (xmlStrEqual(name, (const xmlChar *) "system"))
   1102 	type = XML_CATA_SYSTEM;
   1103     else if (xmlStrEqual(name, (const xmlChar *) "public"))
   1104 	type = XML_CATA_PUBLIC;
   1105     else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
   1106 	type = XML_CATA_REWRITE_SYSTEM;
   1107     else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
   1108 	type = XML_CATA_DELEGATE_PUBLIC;
   1109     else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
   1110 	type = XML_CATA_DELEGATE_SYSTEM;
   1111     else if (xmlStrEqual(name, (const xmlChar *) "uri"))
   1112 	type = XML_CATA_URI;
   1113     else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
   1114 	type = XML_CATA_REWRITE_URI;
   1115     else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
   1116 	type = XML_CATA_DELEGATE_URI;
   1117     else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
   1118 	type = XML_CATA_NEXT_CATALOG;
   1119     else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
   1120 	type = XML_CATA_CATALOG;
   1121     return(type);
   1122 }
   1123 
   1124 /**
   1125  * xmlParseXMLCatalogOneNode:
   1126  * @cur:  the XML node
   1127  * @type:  the type of Catalog entry
   1128  * @name:  the name of the node
   1129  * @attrName:  the attribute holding the value
   1130  * @uriAttrName:  the attribute holding the URI-Reference
   1131  * @prefer:  the PUBLIC vs. SYSTEM current preference value
   1132  * @cgroup:  the group which includes this node
   1133  *
   1134  * Finishes the examination of an XML tree node of a catalog and build
   1135  * a Catalog entry from it.
   1136  *
   1137  * Returns the new Catalog entry node or NULL in case of error.
   1138  */
   1139 static xmlCatalogEntryPtr
   1140 xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
   1141 			  const xmlChar *name, const xmlChar *attrName,
   1142 			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
   1143 			  xmlCatalogEntryPtr cgroup) {
   1144     int ok = 1;
   1145     xmlChar *uriValue;
   1146     xmlChar *nameValue = NULL;
   1147     xmlChar *base = NULL;
   1148     xmlChar *URL = NULL;
   1149     xmlCatalogEntryPtr ret = NULL;
   1150 
   1151     if (attrName != NULL) {
   1152 	nameValue = xmlGetProp(cur, attrName);
   1153 	if (nameValue == NULL) {
   1154 	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
   1155 			  "%s entry lacks '%s'\n", name, attrName, NULL);
   1156 	    ok = 0;
   1157 	}
   1158     }
   1159     uriValue = xmlGetProp(cur, uriAttrName);
   1160     if (uriValue == NULL) {
   1161 	xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
   1162 		"%s entry lacks '%s'\n", name, uriAttrName, NULL);
   1163 	ok = 0;
   1164     }
   1165     if (!ok) {
   1166 	if (nameValue != NULL)
   1167 	    xmlFree(nameValue);
   1168 	if (uriValue != NULL)
   1169 	    xmlFree(uriValue);
   1170 	return(NULL);
   1171     }
   1172 
   1173     base = xmlNodeGetBase(cur->doc, cur);
   1174     URL = xmlBuildURI(uriValue, base);
   1175     if (URL != NULL) {
   1176 	if (xmlDebugCatalogs > 1) {
   1177 	    if (nameValue != NULL)
   1178 		xmlGenericError(xmlGenericErrorContext,
   1179 			"Found %s: '%s' '%s'\n", name, nameValue, URL);
   1180 	    else
   1181 		xmlGenericError(xmlGenericErrorContext,
   1182 			"Found %s: '%s'\n", name, URL);
   1183 	}
   1184 	ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
   1185     } else {
   1186 	xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
   1187 		"%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
   1188     }
   1189     if (nameValue != NULL)
   1190 	xmlFree(nameValue);
   1191     if (uriValue != NULL)
   1192 	xmlFree(uriValue);
   1193     if (base != NULL)
   1194 	xmlFree(base);
   1195     if (URL != NULL)
   1196 	xmlFree(URL);
   1197     return(ret);
   1198 }
   1199 
   1200 /**
   1201  * xmlParseXMLCatalogNode:
   1202  * @cur:  the XML node
   1203  * @prefer:  the PUBLIC vs. SYSTEM current preference value
   1204  * @parent:  the parent Catalog entry
   1205  * @cgroup:  the group which includes this node
   1206  *
   1207  * Examines an XML tree node of a catalog and build
   1208  * a Catalog entry from it adding it to its parent. The examination can
   1209  * be recursive.
   1210  */
   1211 static void
   1212 xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
   1213 	               xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
   1214 {
   1215     xmlChar *base = NULL;
   1216     xmlCatalogEntryPtr entry = NULL;
   1217 
   1218     if (cur == NULL)
   1219         return;
   1220     if (xmlStrEqual(cur->name, BAD_CAST "group")) {
   1221         xmlChar *prop;
   1222 	xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
   1223 
   1224         prop = xmlGetProp(cur, BAD_CAST "prefer");
   1225         if (prop != NULL) {
   1226             if (xmlStrEqual(prop, BAD_CAST "system")) {
   1227                 prefer = XML_CATA_PREFER_SYSTEM;
   1228             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
   1229                 prefer = XML_CATA_PREFER_PUBLIC;
   1230             } else {
   1231 		xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
   1232                               "Invalid value for prefer: '%s'\n",
   1233 			      prop, NULL, NULL);
   1234             }
   1235             xmlFree(prop);
   1236 	    pref = prefer;
   1237         }
   1238 	prop = xmlGetProp(cur, BAD_CAST "id");
   1239 	base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
   1240 	entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
   1241 	xmlFree(prop);
   1242     } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
   1243 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
   1244 		BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
   1245     } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
   1246 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
   1247 		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
   1248     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
   1249 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
   1250 		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
   1251 		BAD_CAST "rewritePrefix", prefer, cgroup);
   1252     } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
   1253 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
   1254 		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
   1255 		BAD_CAST "catalog", prefer, cgroup);
   1256     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
   1257 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
   1258 		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
   1259 		BAD_CAST "catalog", prefer, cgroup);
   1260     } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
   1261 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
   1262 		BAD_CAST "uri", BAD_CAST "name",
   1263 		BAD_CAST "uri", prefer, cgroup);
   1264     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
   1265 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
   1266 		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
   1267 		BAD_CAST "rewritePrefix", prefer, cgroup);
   1268     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
   1269 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
   1270 		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
   1271 		BAD_CAST "catalog", prefer, cgroup);
   1272     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
   1273 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
   1274 		BAD_CAST "nextCatalog", NULL,
   1275 		BAD_CAST "catalog", prefer, cgroup);
   1276     }
   1277     if (entry != NULL) {
   1278         if (parent != NULL) {
   1279 	    entry->parent = parent;
   1280 	    if (parent->children == NULL)
   1281 		parent->children = entry;
   1282 	    else {
   1283 		xmlCatalogEntryPtr prev;
   1284 
   1285 		prev = parent->children;
   1286 		while (prev->next != NULL)
   1287 		    prev = prev->next;
   1288 		prev->next = entry;
   1289 	    }
   1290 	}
   1291 	if (entry->type == XML_CATA_GROUP) {
   1292 	    /*
   1293 	     * Recurse to propagate prefer to the subtree
   1294 	     * (xml:base handling is automated)
   1295 	     */
   1296             xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
   1297 	}
   1298     }
   1299     if (base != NULL)
   1300 	xmlFree(base);
   1301 }
   1302 
   1303 /**
   1304  * xmlParseXMLCatalogNodeList:
   1305  * @cur:  the XML node list of siblings
   1306  * @prefer:  the PUBLIC vs. SYSTEM current preference value
   1307  * @parent:  the parent Catalog entry
   1308  * @cgroup:  the group which includes this list
   1309  *
   1310  * Examines a list of XML sibling nodes of a catalog and build
   1311  * a list of Catalog entry from it adding it to the parent.
   1312  * The examination will recurse to examine node subtrees.
   1313  */
   1314 static void
   1315 xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
   1316 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
   1317     while (cur != NULL) {
   1318 	if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
   1319 	    (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
   1320 	    xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
   1321 	}
   1322 	cur = cur->next;
   1323     }
   1324     /* TODO: sort the list according to REWRITE lengths and prefer value */
   1325 }
   1326 
   1327 /**
   1328  * xmlParseXMLCatalogFile:
   1329  * @prefer:  the PUBLIC vs. SYSTEM current preference value
   1330  * @filename:  the filename for the catalog
   1331  *
   1332  * Parses the catalog file to extract the XML tree and then analyze the
   1333  * tree to build a list of Catalog entries corresponding to this catalog
   1334  *
   1335  * Returns the resulting Catalog entries list
   1336  */
   1337 static xmlCatalogEntryPtr
   1338 xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
   1339     xmlDocPtr doc;
   1340     xmlNodePtr cur;
   1341     xmlChar *prop;
   1342     xmlCatalogEntryPtr parent = NULL;
   1343 
   1344     if (filename == NULL)
   1345         return(NULL);
   1346 
   1347     doc = xmlParseCatalogFile((const char *) filename);
   1348     if (doc == NULL) {
   1349 	if (xmlDebugCatalogs)
   1350 	    xmlGenericError(xmlGenericErrorContext,
   1351 		    "Failed to parse catalog %s\n", filename);
   1352 	return(NULL);
   1353     }
   1354 
   1355     if (xmlDebugCatalogs)
   1356 	xmlGenericError(xmlGenericErrorContext,
   1357 		"%d Parsing catalog %s\n", xmlGetThreadId(), filename);
   1358 
   1359     cur = xmlDocGetRootElement(doc);
   1360     if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
   1361 	(cur->ns != NULL) && (cur->ns->href != NULL) &&
   1362 	(xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
   1363 
   1364 	parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
   1365 				    (const xmlChar *)filename, NULL, prefer, NULL);
   1366         if (parent == NULL) {
   1367 	    xmlFreeDoc(doc);
   1368 	    return(NULL);
   1369 	}
   1370 
   1371 	prop = xmlGetProp(cur, BAD_CAST "prefer");
   1372 	if (prop != NULL) {
   1373 	    if (xmlStrEqual(prop, BAD_CAST "system")) {
   1374 		prefer = XML_CATA_PREFER_SYSTEM;
   1375 	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
   1376 		prefer = XML_CATA_PREFER_PUBLIC;
   1377 	    } else {
   1378 		xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
   1379 			      "Invalid value for prefer: '%s'\n",
   1380 			      prop, NULL, NULL);
   1381 	    }
   1382 	    xmlFree(prop);
   1383 	}
   1384 	cur = cur->children;
   1385 	xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
   1386     } else {
   1387 	xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
   1388 		      "File %s is not an XML Catalog\n",
   1389 		      filename, NULL, NULL);
   1390 	xmlFreeDoc(doc);
   1391 	return(NULL);
   1392     }
   1393     xmlFreeDoc(doc);
   1394     return(parent);
   1395 }
   1396 
   1397 /**
   1398  * xmlFetchXMLCatalogFile:
   1399  * @catal:  an existing but incomplete catalog entry
   1400  *
   1401  * Fetch and parse the subcatalog referenced by an entry
   1402  *
   1403  * Returns 0 in case of success, -1 otherwise
   1404  */
   1405 static int
   1406 xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
   1407     xmlCatalogEntryPtr doc;
   1408 
   1409     if (catal == NULL)
   1410 	return(-1);
   1411     if (catal->URL == NULL)
   1412 	return(-1);
   1413     if (catal->children != NULL)
   1414 	return(-1);
   1415 
   1416     /*
   1417      * lock the whole catalog for modification
   1418      */
   1419     xmlRMutexLock(xmlCatalogMutex);
   1420     if (catal->children != NULL) {
   1421 	/* Okay someone else did it in the meantime */
   1422 	xmlRMutexUnlock(xmlCatalogMutex);
   1423 	return(0);
   1424     }
   1425 
   1426     if (xmlCatalogXMLFiles != NULL) {
   1427 	doc = (xmlCatalogEntryPtr)
   1428 	    xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
   1429 	if (doc != NULL) {
   1430 	    if (xmlDebugCatalogs)
   1431 		xmlGenericError(xmlGenericErrorContext,
   1432 		    "Found %s in file hash\n", catal->URL);
   1433 
   1434 	    if (catal->type == XML_CATA_CATALOG)
   1435 		catal->children = doc->children;
   1436 	    else
   1437 		catal->children = doc;
   1438 	    catal->dealloc = 0;
   1439 	    xmlRMutexUnlock(xmlCatalogMutex);
   1440 	    return(0);
   1441 	}
   1442 	if (xmlDebugCatalogs)
   1443 	    xmlGenericError(xmlGenericErrorContext,
   1444 		"%s not found in file hash\n", catal->URL);
   1445     }
   1446 
   1447     /*
   1448      * Fetch and parse. Note that xmlParseXMLCatalogFile does not
   1449      * use the existing catalog, there is no recursion allowed at
   1450      * that level.
   1451      */
   1452     doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
   1453     if (doc == NULL) {
   1454 	catal->type = XML_CATA_BROKEN_CATALOG;
   1455 	xmlRMutexUnlock(xmlCatalogMutex);
   1456 	return(-1);
   1457     }
   1458 
   1459     if (catal->type == XML_CATA_CATALOG)
   1460 	catal->children = doc->children;
   1461     else
   1462 	catal->children = doc;
   1463 
   1464     doc->dealloc = 1;
   1465 
   1466     if (xmlCatalogXMLFiles == NULL)
   1467 	xmlCatalogXMLFiles = xmlHashCreate(10);
   1468     if (xmlCatalogXMLFiles != NULL) {
   1469 	if (xmlDebugCatalogs)
   1470 	    xmlGenericError(xmlGenericErrorContext,
   1471 		"%s added to file hash\n", catal->URL);
   1472 	xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
   1473     }
   1474     xmlRMutexUnlock(xmlCatalogMutex);
   1475     return(0);
   1476 }
   1477 
   1478 /************************************************************************
   1479  *									*
   1480  *			XML Catalog handling				*
   1481  *									*
   1482  ************************************************************************/
   1483 
   1484 /**
   1485  * xmlAddXMLCatalog:
   1486  * @catal:  top of an XML catalog
   1487  * @type:  the type of record to add to the catalog
   1488  * @orig:  the system, public or prefix to match (or NULL)
   1489  * @replace:  the replacement value for the match
   1490  *
   1491  * Add an entry in the XML catalog, it may overwrite existing but
   1492  * different entries.
   1493  *
   1494  * Returns 0 if successful, -1 otherwise
   1495  */
   1496 static int
   1497 xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
   1498 	      const xmlChar *orig, const xmlChar *replace) {
   1499     xmlCatalogEntryPtr cur;
   1500     xmlCatalogEntryType typ;
   1501     int doregister = 0;
   1502 
   1503     if ((catal == NULL) ||
   1504 	((catal->type != XML_CATA_CATALOG) &&
   1505 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
   1506 	return(-1);
   1507     if (catal->children == NULL) {
   1508 	xmlFetchXMLCatalogFile(catal);
   1509     }
   1510     if (catal->children == NULL)
   1511 	doregister = 1;
   1512 
   1513     typ = xmlGetXMLCatalogEntryType(type);
   1514     if (typ == XML_CATA_NONE) {
   1515 	if (xmlDebugCatalogs)
   1516 	    xmlGenericError(xmlGenericErrorContext,
   1517 		    "Failed to add unknown element %s to catalog\n", type);
   1518 	return(-1);
   1519     }
   1520 
   1521     cur = catal->children;
   1522     /*
   1523      * Might be a simple "update in place"
   1524      */
   1525     if (cur != NULL) {
   1526 	while (cur != NULL) {
   1527 	    if ((orig != NULL) && (cur->type == typ) &&
   1528 		(xmlStrEqual(orig, cur->name))) {
   1529 		if (xmlDebugCatalogs)
   1530 		    xmlGenericError(xmlGenericErrorContext,
   1531 			    "Updating element %s to catalog\n", type);
   1532 		if (cur->value != NULL)
   1533 		    xmlFree(cur->value);
   1534 		if (cur->URL != NULL)
   1535 		    xmlFree(cur->URL);
   1536 		cur->value = xmlStrdup(replace);
   1537 		cur->URL = xmlStrdup(replace);
   1538 		return(0);
   1539 	    }
   1540 	    if (cur->next == NULL)
   1541 		break;
   1542 	    cur = cur->next;
   1543 	}
   1544     }
   1545     if (xmlDebugCatalogs)
   1546 	xmlGenericError(xmlGenericErrorContext,
   1547 		"Adding element %s to catalog\n", type);
   1548     if (cur == NULL)
   1549 	catal->children = xmlNewCatalogEntry(typ, orig, replace,
   1550 		                             NULL, catal->prefer, NULL);
   1551     else
   1552 	cur->next = xmlNewCatalogEntry(typ, orig, replace,
   1553 		                       NULL, catal->prefer, NULL);
   1554     if (doregister) {
   1555         catal->type = XML_CATA_CATALOG;
   1556 	cur = xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
   1557 	if (cur != NULL)
   1558 	    cur->children = catal->children;
   1559     }
   1560 
   1561     return(0);
   1562 }
   1563 
   1564 /**
   1565  * xmlDelXMLCatalog:
   1566  * @catal:  top of an XML catalog
   1567  * @value:  the value to remove from the catalog
   1568  *
   1569  * Remove entries in the XML catalog where the value or the URI
   1570  * is equal to @value
   1571  *
   1572  * Returns the number of entries removed if successful, -1 otherwise
   1573  */
   1574 static int
   1575 xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
   1576     xmlCatalogEntryPtr cur;
   1577     int ret = 0;
   1578 
   1579     if ((catal == NULL) ||
   1580 	((catal->type != XML_CATA_CATALOG) &&
   1581 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
   1582 	return(-1);
   1583     if (value == NULL)
   1584 	return(-1);
   1585     if (catal->children == NULL) {
   1586 	xmlFetchXMLCatalogFile(catal);
   1587     }
   1588 
   1589     /*
   1590      * Scan the children
   1591      */
   1592     cur = catal->children;
   1593     while (cur != NULL) {
   1594 	if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
   1595 	    (xmlStrEqual(value, cur->value))) {
   1596 	    if (xmlDebugCatalogs) {
   1597 		if (cur->name != NULL)
   1598 		    xmlGenericError(xmlGenericErrorContext,
   1599 			    "Removing element %s from catalog\n", cur->name);
   1600 		else
   1601 		    xmlGenericError(xmlGenericErrorContext,
   1602 			    "Removing element %s from catalog\n", cur->value);
   1603 	    }
   1604 	    cur->type = XML_CATA_REMOVED;
   1605 	}
   1606 	cur = cur->next;
   1607     }
   1608     return(ret);
   1609 }
   1610 
   1611 /**
   1612  * xmlCatalogXMLResolve:
   1613  * @catal:  a catalog list
   1614  * @pubID:  the public ID string
   1615  * @sysID:  the system ID string
   1616  *
   1617  * Do a complete resolution lookup of an External Identifier for a
   1618  * list of catalog entries.
   1619  *
   1620  * Implements (or tries to) 7.1. External Identifier Resolution
   1621  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
   1622  *
   1623  * Returns the URI of the resource or NULL if not found
   1624  */
   1625 static xmlChar *
   1626 xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
   1627 	              const xmlChar *sysID) {
   1628     xmlChar *ret = NULL;
   1629     xmlCatalogEntryPtr cur;
   1630     int haveDelegate = 0;
   1631     int haveNext = 0;
   1632 
   1633     /*
   1634      * protection against loops
   1635      */
   1636     if (catal->depth > MAX_CATAL_DEPTH) {
   1637 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
   1638 		      "Detected recursion in catalog %s\n",
   1639 		      catal->name, NULL, NULL);
   1640 	return(NULL);
   1641     }
   1642     catal->depth++;
   1643 
   1644     /*
   1645      * First tries steps 2/ 3/ 4/ if a system ID is provided.
   1646      */
   1647     if (sysID != NULL) {
   1648 	xmlCatalogEntryPtr rewrite = NULL;
   1649 	int lenrewrite = 0, len;
   1650 	cur = catal;
   1651 	haveDelegate = 0;
   1652 	while (cur != NULL) {
   1653 	    switch (cur->type) {
   1654 		case XML_CATA_SYSTEM:
   1655 		    if (xmlStrEqual(sysID, cur->name)) {
   1656 			if (xmlDebugCatalogs)
   1657 			    xmlGenericError(xmlGenericErrorContext,
   1658 				    "Found system match %s, using %s\n",
   1659 				            cur->name, cur->URL);
   1660 			catal->depth--;
   1661 			return(xmlStrdup(cur->URL));
   1662 		    }
   1663 		    break;
   1664 		case XML_CATA_REWRITE_SYSTEM:
   1665 		    len = xmlStrlen(cur->name);
   1666 		    if ((len > lenrewrite) &&
   1667 			(!xmlStrncmp(sysID, cur->name, len))) {
   1668 			lenrewrite = len;
   1669 			rewrite = cur;
   1670 		    }
   1671 		    break;
   1672 		case XML_CATA_DELEGATE_SYSTEM:
   1673 		    if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
   1674 			haveDelegate++;
   1675 		    break;
   1676 		case XML_CATA_NEXT_CATALOG:
   1677 		    haveNext++;
   1678 		    break;
   1679 		default:
   1680 		    break;
   1681 	    }
   1682 	    cur = cur->next;
   1683 	}
   1684 	if (rewrite != NULL) {
   1685 	    if (xmlDebugCatalogs)
   1686 		xmlGenericError(xmlGenericErrorContext,
   1687 			"Using rewriting rule %s\n", rewrite->name);
   1688 	    ret = xmlStrdup(rewrite->URL);
   1689 	    if (ret != NULL)
   1690 		ret = xmlStrcat(ret, &sysID[lenrewrite]);
   1691 	    catal->depth--;
   1692 	    return(ret);
   1693 	}
   1694 	if (haveDelegate) {
   1695 	    const xmlChar *delegates[MAX_DELEGATE];
   1696 	    int nbList = 0, i;
   1697 
   1698 	    /*
   1699 	     * Assume the entries have been sorted by decreasing substring
   1700 	     * matches when the list was produced.
   1701 	     */
   1702 	    cur = catal;
   1703 	    while (cur != NULL) {
   1704 		if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
   1705 		    (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
   1706 		    for (i = 0;i < nbList;i++)
   1707 			if (xmlStrEqual(cur->URL, delegates[i]))
   1708 			    break;
   1709 		    if (i < nbList) {
   1710 			cur = cur->next;
   1711 			continue;
   1712 		    }
   1713 		    if (nbList < MAX_DELEGATE)
   1714 			delegates[nbList++] = cur->URL;
   1715 
   1716 		    if (cur->children == NULL) {
   1717 			xmlFetchXMLCatalogFile(cur);
   1718 		    }
   1719 		    if (cur->children != NULL) {
   1720 			if (xmlDebugCatalogs)
   1721 			    xmlGenericError(xmlGenericErrorContext,
   1722 				    "Trying system delegate %s\n", cur->URL);
   1723 			ret = xmlCatalogListXMLResolve(
   1724 				cur->children, NULL, sysID);
   1725 			if (ret != NULL) {
   1726 			    catal->depth--;
   1727 			    return(ret);
   1728 			}
   1729 		    }
   1730 		}
   1731 		cur = cur->next;
   1732 	    }
   1733 	    /*
   1734 	     * Apply the cut algorithm explained in 4/
   1735 	     */
   1736 	    catal->depth--;
   1737 	    return(XML_CATAL_BREAK);
   1738 	}
   1739     }
   1740     /*
   1741      * Then tries 5/ 6/ if a public ID is provided
   1742      */
   1743     if (pubID != NULL) {
   1744 	cur = catal;
   1745 	haveDelegate = 0;
   1746 	while (cur != NULL) {
   1747 	    switch (cur->type) {
   1748 		case XML_CATA_PUBLIC:
   1749 		    if (xmlStrEqual(pubID, cur->name)) {
   1750 			if (xmlDebugCatalogs)
   1751 			    xmlGenericError(xmlGenericErrorContext,
   1752 				    "Found public match %s\n", cur->name);
   1753 			catal->depth--;
   1754 			return(xmlStrdup(cur->URL));
   1755 		    }
   1756 		    break;
   1757 		case XML_CATA_DELEGATE_PUBLIC:
   1758 		    if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
   1759 			(cur->prefer == XML_CATA_PREFER_PUBLIC))
   1760 			haveDelegate++;
   1761 		    break;
   1762 		case XML_CATA_NEXT_CATALOG:
   1763 		    if (sysID == NULL)
   1764 			haveNext++;
   1765 		    break;
   1766 		default:
   1767 		    break;
   1768 	    }
   1769 	    cur = cur->next;
   1770 	}
   1771 	if (haveDelegate) {
   1772 	    const xmlChar *delegates[MAX_DELEGATE];
   1773 	    int nbList = 0, i;
   1774 
   1775 	    /*
   1776 	     * Assume the entries have been sorted by decreasing substring
   1777 	     * matches when the list was produced.
   1778 	     */
   1779 	    cur = catal;
   1780 	    while (cur != NULL) {
   1781 		if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
   1782 		    (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
   1783 		    (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
   1784 
   1785 		    for (i = 0;i < nbList;i++)
   1786 			if (xmlStrEqual(cur->URL, delegates[i]))
   1787 			    break;
   1788 		    if (i < nbList) {
   1789 			cur = cur->next;
   1790 			continue;
   1791 		    }
   1792 		    if (nbList < MAX_DELEGATE)
   1793 			delegates[nbList++] = cur->URL;
   1794 
   1795 		    if (cur->children == NULL) {
   1796 			xmlFetchXMLCatalogFile(cur);
   1797 		    }
   1798 		    if (cur->children != NULL) {
   1799 			if (xmlDebugCatalogs)
   1800 			    xmlGenericError(xmlGenericErrorContext,
   1801 				    "Trying public delegate %s\n", cur->URL);
   1802 			ret = xmlCatalogListXMLResolve(
   1803 				cur->children, pubID, NULL);
   1804 			if (ret != NULL) {
   1805 			    catal->depth--;
   1806 			    return(ret);
   1807 			}
   1808 		    }
   1809 		}
   1810 		cur = cur->next;
   1811 	    }
   1812 	    /*
   1813 	     * Apply the cut algorithm explained in 4/
   1814 	     */
   1815 	    catal->depth--;
   1816 	    return(XML_CATAL_BREAK);
   1817 	}
   1818     }
   1819     if (haveNext) {
   1820 	cur = catal;
   1821 	while (cur != NULL) {
   1822 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
   1823 		if (cur->children == NULL) {
   1824 		    xmlFetchXMLCatalogFile(cur);
   1825 		}
   1826 		if (cur->children != NULL) {
   1827 		    ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
   1828 		    if (ret != NULL) {
   1829 			catal->depth--;
   1830 			return(ret);
   1831 		    } else if (catal->depth > MAX_CATAL_DEPTH) {
   1832 		        return(NULL);
   1833 		    }
   1834 		}
   1835 	    }
   1836 	    cur = cur->next;
   1837 	}
   1838     }
   1839 
   1840     catal->depth--;
   1841     return(NULL);
   1842 }
   1843 
   1844 /**
   1845  * xmlCatalogXMLResolveURI:
   1846  * @catal:  a catalog list
   1847  * @URI:  the URI
   1848  * @sysID:  the system ID string
   1849  *
   1850  * Do a complete resolution lookup of an External Identifier for a
   1851  * list of catalog entries.
   1852  *
   1853  * Implements (or tries to) 7.2.2. URI Resolution
   1854  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
   1855  *
   1856  * Returns the URI of the resource or NULL if not found
   1857  */
   1858 static xmlChar *
   1859 xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
   1860     xmlChar *ret = NULL;
   1861     xmlCatalogEntryPtr cur;
   1862     int haveDelegate = 0;
   1863     int haveNext = 0;
   1864     xmlCatalogEntryPtr rewrite = NULL;
   1865     int lenrewrite = 0, len;
   1866 
   1867     if (catal == NULL)
   1868 	return(NULL);
   1869 
   1870     if (URI == NULL)
   1871 	return(NULL);
   1872 
   1873     if (catal->depth > MAX_CATAL_DEPTH) {
   1874 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
   1875 		      "Detected recursion in catalog %s\n",
   1876 		      catal->name, NULL, NULL);
   1877 	return(NULL);
   1878     }
   1879 
   1880     /*
   1881      * First tries steps 2/ 3/ 4/ if a system ID is provided.
   1882      */
   1883     cur = catal;
   1884     haveDelegate = 0;
   1885     while (cur != NULL) {
   1886 	switch (cur->type) {
   1887 	    case XML_CATA_URI:
   1888 		if (xmlStrEqual(URI, cur->name)) {
   1889 		    if (xmlDebugCatalogs)
   1890 			xmlGenericError(xmlGenericErrorContext,
   1891 				"Found URI match %s\n", cur->name);
   1892 		    return(xmlStrdup(cur->URL));
   1893 		}
   1894 		break;
   1895 	    case XML_CATA_REWRITE_URI:
   1896 		len = xmlStrlen(cur->name);
   1897 		if ((len > lenrewrite) &&
   1898 		    (!xmlStrncmp(URI, cur->name, len))) {
   1899 		    lenrewrite = len;
   1900 		    rewrite = cur;
   1901 		}
   1902 		break;
   1903 	    case XML_CATA_DELEGATE_URI:
   1904 		if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
   1905 		    haveDelegate++;
   1906 		break;
   1907 	    case XML_CATA_NEXT_CATALOG:
   1908 		haveNext++;
   1909 		break;
   1910 	    default:
   1911 		break;
   1912 	}
   1913 	cur = cur->next;
   1914     }
   1915     if (rewrite != NULL) {
   1916 	if (xmlDebugCatalogs)
   1917 	    xmlGenericError(xmlGenericErrorContext,
   1918 		    "Using rewriting rule %s\n", rewrite->name);
   1919 	ret = xmlStrdup(rewrite->URL);
   1920 	if (ret != NULL)
   1921 	    ret = xmlStrcat(ret, &URI[lenrewrite]);
   1922 	return(ret);
   1923     }
   1924     if (haveDelegate) {
   1925 	const xmlChar *delegates[MAX_DELEGATE];
   1926 	int nbList = 0, i;
   1927 
   1928 	/*
   1929 	 * Assume the entries have been sorted by decreasing substring
   1930 	 * matches when the list was produced.
   1931 	 */
   1932 	cur = catal;
   1933 	while (cur != NULL) {
   1934 	    if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
   1935 	         (cur->type == XML_CATA_DELEGATE_URI)) &&
   1936 		(!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
   1937 		for (i = 0;i < nbList;i++)
   1938 		    if (xmlStrEqual(cur->URL, delegates[i]))
   1939 			break;
   1940 		if (i < nbList) {
   1941 		    cur = cur->next;
   1942 		    continue;
   1943 		}
   1944 		if (nbList < MAX_DELEGATE)
   1945 		    delegates[nbList++] = cur->URL;
   1946 
   1947 		if (cur->children == NULL) {
   1948 		    xmlFetchXMLCatalogFile(cur);
   1949 		}
   1950 		if (cur->children != NULL) {
   1951 		    if (xmlDebugCatalogs)
   1952 			xmlGenericError(xmlGenericErrorContext,
   1953 				"Trying URI delegate %s\n", cur->URL);
   1954 		    ret = xmlCatalogListXMLResolveURI(
   1955 			    cur->children, URI);
   1956 		    if (ret != NULL)
   1957 			return(ret);
   1958 		}
   1959 	    }
   1960 	    cur = cur->next;
   1961 	}
   1962 	/*
   1963 	 * Apply the cut algorithm explained in 4/
   1964 	 */
   1965 	return(XML_CATAL_BREAK);
   1966     }
   1967     if (haveNext) {
   1968 	cur = catal;
   1969 	while (cur != NULL) {
   1970 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
   1971 		if (cur->children == NULL) {
   1972 		    xmlFetchXMLCatalogFile(cur);
   1973 		}
   1974 		if (cur->children != NULL) {
   1975 		    ret = xmlCatalogListXMLResolveURI(cur->children, URI);
   1976 		    if (ret != NULL)
   1977 			return(ret);
   1978 		}
   1979 	    }
   1980 	    cur = cur->next;
   1981 	}
   1982     }
   1983 
   1984     return(NULL);
   1985 }
   1986 
   1987 /**
   1988  * xmlCatalogListXMLResolve:
   1989  * @catal:  a catalog list
   1990  * @pubID:  the public ID string
   1991  * @sysID:  the system ID string
   1992  *
   1993  * Do a complete resolution lookup of an External Identifier for a
   1994  * list of catalogs
   1995  *
   1996  * Implements (or tries to) 7.1. External Identifier Resolution
   1997  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
   1998  *
   1999  * Returns the URI of the resource or NULL if not found
   2000  */
   2001 static xmlChar *
   2002 xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
   2003 	              const xmlChar *sysID) {
   2004     xmlChar *ret = NULL;
   2005     xmlChar *urnID = NULL;
   2006     xmlChar *normid;
   2007 
   2008     if (catal == NULL)
   2009         return(NULL);
   2010     if ((pubID == NULL) && (sysID == NULL))
   2011 	return(NULL);
   2012 
   2013     normid = xmlCatalogNormalizePublic(pubID);
   2014     if (normid != NULL)
   2015         pubID = (*normid != 0 ? normid : NULL);
   2016 
   2017     if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
   2018 	urnID = xmlCatalogUnWrapURN(pubID);
   2019 	if (xmlDebugCatalogs) {
   2020 	    if (urnID == NULL)
   2021 		xmlGenericError(xmlGenericErrorContext,
   2022 			"Public URN ID %s expanded to NULL\n", pubID);
   2023 	    else
   2024 		xmlGenericError(xmlGenericErrorContext,
   2025 			"Public URN ID expanded to %s\n", urnID);
   2026 	}
   2027 	ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
   2028 	if (urnID != NULL)
   2029 	    xmlFree(urnID);
   2030 	if (normid != NULL)
   2031 	    xmlFree(normid);
   2032 	return(ret);
   2033     }
   2034     if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
   2035 	urnID = xmlCatalogUnWrapURN(sysID);
   2036 	if (xmlDebugCatalogs) {
   2037 	    if (urnID == NULL)
   2038 		xmlGenericError(xmlGenericErrorContext,
   2039 			"System URN ID %s expanded to NULL\n", sysID);
   2040 	    else
   2041 		xmlGenericError(xmlGenericErrorContext,
   2042 			"System URN ID expanded to %s\n", urnID);
   2043 	}
   2044 	if (pubID == NULL)
   2045 	    ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
   2046 	else if (xmlStrEqual(pubID, urnID))
   2047 	    ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
   2048 	else {
   2049 	    ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
   2050 	}
   2051 	if (urnID != NULL)
   2052 	    xmlFree(urnID);
   2053 	if (normid != NULL)
   2054 	    xmlFree(normid);
   2055 	return(ret);
   2056     }
   2057     while (catal != NULL) {
   2058 	if (catal->type == XML_CATA_CATALOG) {
   2059 	    if (catal->children == NULL) {
   2060 		xmlFetchXMLCatalogFile(catal);
   2061 	    }
   2062 	    if (catal->children != NULL) {
   2063 		ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
   2064 		if (ret != NULL) {
   2065 		    break;
   2066                 } else if ((catal->children != NULL) &&
   2067 		           (catal->children->depth > MAX_CATAL_DEPTH)) {
   2068 	            ret = NULL;
   2069 		    break;
   2070 	        }
   2071 	    }
   2072 	}
   2073 	catal = catal->next;
   2074     }
   2075     if (normid != NULL)
   2076 	xmlFree(normid);
   2077     return(ret);
   2078 }
   2079 
   2080 /**
   2081  * xmlCatalogListXMLResolveURI:
   2082  * @catal:  a catalog list
   2083  * @URI:  the URI
   2084  *
   2085  * Do a complete resolution lookup of an URI for a list of catalogs
   2086  *
   2087  * Implements (or tries to) 7.2. URI Resolution
   2088  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
   2089  *
   2090  * Returns the URI of the resource or NULL if not found
   2091  */
   2092 static xmlChar *
   2093 xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
   2094     xmlChar *ret = NULL;
   2095     xmlChar *urnID = NULL;
   2096 
   2097     if (catal == NULL)
   2098         return(NULL);
   2099     if (URI == NULL)
   2100 	return(NULL);
   2101 
   2102     if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
   2103 	urnID = xmlCatalogUnWrapURN(URI);
   2104 	if (xmlDebugCatalogs) {
   2105 	    if (urnID == NULL)
   2106 		xmlGenericError(xmlGenericErrorContext,
   2107 			"URN ID %s expanded to NULL\n", URI);
   2108 	    else
   2109 		xmlGenericError(xmlGenericErrorContext,
   2110 			"URN ID expanded to %s\n", urnID);
   2111 	}
   2112 	ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
   2113 	if (urnID != NULL)
   2114 	    xmlFree(urnID);
   2115 	return(ret);
   2116     }
   2117     while (catal != NULL) {
   2118 	if (catal->type == XML_CATA_CATALOG) {
   2119 	    if (catal->children == NULL) {
   2120 		xmlFetchXMLCatalogFile(catal);
   2121 	    }
   2122 	    if (catal->children != NULL) {
   2123 		ret = xmlCatalogXMLResolveURI(catal->children, URI);
   2124 		if (ret != NULL)
   2125 		    return(ret);
   2126 	    }
   2127 	}
   2128 	catal = catal->next;
   2129     }
   2130     return(ret);
   2131 }
   2132 
   2133 /************************************************************************
   2134  *									*
   2135  *			The SGML Catalog parser				*
   2136  *									*
   2137  ************************************************************************/
   2138 
   2139 
   2140 #define RAW *cur
   2141 #define NEXT cur++;
   2142 #define SKIP(x) cur += x;
   2143 
   2144 #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
   2145 
   2146 /**
   2147  * xmlParseSGMLCatalogComment:
   2148  * @cur:  the current character
   2149  *
   2150  * Skip a comment in an SGML catalog
   2151  *
   2152  * Returns new current character
   2153  */
   2154 static const xmlChar *
   2155 xmlParseSGMLCatalogComment(const xmlChar *cur) {
   2156     if ((cur[0] != '-') || (cur[1] != '-'))
   2157 	return(cur);
   2158     SKIP(2);
   2159     while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
   2160 	NEXT;
   2161     if (cur[0] == 0) {
   2162 	return(NULL);
   2163     }
   2164     return(cur + 2);
   2165 }
   2166 
   2167 /**
   2168  * xmlParseSGMLCatalogPubid:
   2169  * @cur:  the current character
   2170  * @id:  the return location
   2171  *
   2172  * Parse an SGML catalog ID
   2173  *
   2174  * Returns new current character and store the value in @id
   2175  */
   2176 static const xmlChar *
   2177 xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
   2178     xmlChar *buf = NULL, *tmp;
   2179     int len = 0;
   2180     int size = 50;
   2181     xmlChar stop;
   2182     int count = 0;
   2183 
   2184     *id = NULL;
   2185 
   2186     if (RAW == '"') {
   2187         NEXT;
   2188 	stop = '"';
   2189     } else if (RAW == '\'') {
   2190         NEXT;
   2191 	stop = '\'';
   2192     } else {
   2193 	stop = ' ';
   2194     }
   2195     buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
   2196     if (buf == NULL) {
   2197         xmlCatalogErrMemory("allocating public ID");
   2198 	return(NULL);
   2199     }
   2200     while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
   2201 	if ((*cur == stop) && (stop != ' '))
   2202 	    break;
   2203 	if ((stop == ' ') && (IS_BLANK_CH(*cur)))
   2204 	    break;
   2205 	if (len + 1 >= size) {
   2206 	    size *= 2;
   2207 	    tmp = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
   2208 	    if (tmp == NULL) {
   2209 		xmlCatalogErrMemory("allocating public ID");
   2210 		xmlFree(buf);
   2211 		return(NULL);
   2212 	    }
   2213 	    buf = tmp;
   2214 	}
   2215 	buf[len++] = *cur;
   2216 	count++;
   2217 	NEXT;
   2218     }
   2219     buf[len] = 0;
   2220     if (stop == ' ') {
   2221 	if (!IS_BLANK_CH(*cur)) {
   2222 	    xmlFree(buf);
   2223 	    return(NULL);
   2224 	}
   2225     } else {
   2226 	if (*cur != stop) {
   2227 	    xmlFree(buf);
   2228 	    return(NULL);
   2229 	}
   2230 	NEXT;
   2231     }
   2232     *id = buf;
   2233     return(cur);
   2234 }
   2235 
   2236 /**
   2237  * xmlParseSGMLCatalogName:
   2238  * @cur:  the current character
   2239  * @name:  the return location
   2240  *
   2241  * Parse an SGML catalog name
   2242  *
   2243  * Returns new current character and store the value in @name
   2244  */
   2245 static const xmlChar *
   2246 xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
   2247     xmlChar buf[XML_MAX_NAMELEN + 5];
   2248     int len = 0;
   2249     int c;
   2250 
   2251     *name = NULL;
   2252 
   2253     /*
   2254      * Handler for more complex cases
   2255      */
   2256     c = *cur;
   2257     if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
   2258 	return(NULL);
   2259     }
   2260 
   2261     while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
   2262             (c == '.') || (c == '-') ||
   2263 	    (c == '_') || (c == ':'))) {
   2264 	buf[len++] = c;
   2265 	cur++;
   2266 	c = *cur;
   2267 	if (len >= XML_MAX_NAMELEN)
   2268 	    return(NULL);
   2269     }
   2270     *name = xmlStrndup(buf, len);
   2271     return(cur);
   2272 }
   2273 
   2274 /**
   2275  * xmlGetSGMLCatalogEntryType:
   2276  * @name:  the entry name
   2277  *
   2278  * Get the Catalog entry type for a given SGML Catalog name
   2279  *
   2280  * Returns Catalog entry type
   2281  */
   2282 static xmlCatalogEntryType
   2283 xmlGetSGMLCatalogEntryType(const xmlChar *name) {
   2284     xmlCatalogEntryType type = XML_CATA_NONE;
   2285     if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
   2286 	type = SGML_CATA_SYSTEM;
   2287     else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
   2288 	type = SGML_CATA_PUBLIC;
   2289     else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
   2290 	type = SGML_CATA_DELEGATE;
   2291     else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
   2292 	type = SGML_CATA_ENTITY;
   2293     else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
   2294 	type = SGML_CATA_DOCTYPE;
   2295     else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
   2296 	type = SGML_CATA_LINKTYPE;
   2297     else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
   2298 	type = SGML_CATA_NOTATION;
   2299     else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
   2300 	type = SGML_CATA_SGMLDECL;
   2301     else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
   2302 	type = SGML_CATA_DOCUMENT;
   2303     else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
   2304 	type = SGML_CATA_CATALOG;
   2305     else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
   2306 	type = SGML_CATA_BASE;
   2307     return(type);
   2308 }
   2309 
   2310 /**
   2311  * xmlParseSGMLCatalog:
   2312  * @catal:  the SGML Catalog
   2313  * @value:  the content of the SGML Catalog serialization
   2314  * @file:  the filepath for the catalog
   2315  * @super:  should this be handled as a Super Catalog in which case
   2316  *          parsing is not recursive
   2317  *
   2318  * Parse an SGML catalog content and fill up the @catal hash table with
   2319  * the new entries found.
   2320  *
   2321  * Returns 0 in case of success, -1 in case of error.
   2322  */
   2323 static int
   2324 xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
   2325 	            const char *file, int super) {
   2326     const xmlChar *cur = value;
   2327     xmlChar *base = NULL;
   2328     int res;
   2329 
   2330     if ((cur == NULL) || (file == NULL))
   2331         return(-1);
   2332     base = xmlStrdup((const xmlChar *) file);
   2333 
   2334     while ((cur != NULL) && (cur[0] != 0)) {
   2335 	SKIP_BLANKS;
   2336 	if (cur[0] == 0)
   2337 	    break;
   2338 	if ((cur[0] == '-') && (cur[1] == '-')) {
   2339 	    cur = xmlParseSGMLCatalogComment(cur);
   2340 	    if (cur == NULL) {
   2341 		/* error */
   2342 		break;
   2343 	    }
   2344 	} else {
   2345 	    xmlChar *sysid = NULL;
   2346 	    xmlChar *name = NULL;
   2347 	    xmlCatalogEntryType type = XML_CATA_NONE;
   2348 
   2349 	    cur = xmlParseSGMLCatalogName(cur, &name);
   2350 	    if (name == NULL) {
   2351 		/* error */
   2352 		break;
   2353 	    }
   2354 	    if (!IS_BLANK_CH(*cur)) {
   2355 		/* error */
   2356 		break;
   2357 	    }
   2358 	    SKIP_BLANKS;
   2359 	    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
   2360                 type = SGML_CATA_SYSTEM;
   2361 	    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
   2362                 type = SGML_CATA_PUBLIC;
   2363 	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
   2364                 type = SGML_CATA_DELEGATE;
   2365 	    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
   2366                 type = SGML_CATA_ENTITY;
   2367 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
   2368                 type = SGML_CATA_DOCTYPE;
   2369 	    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
   2370                 type = SGML_CATA_LINKTYPE;
   2371 	    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
   2372                 type = SGML_CATA_NOTATION;
   2373 	    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
   2374                 type = SGML_CATA_SGMLDECL;
   2375 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
   2376                 type = SGML_CATA_DOCUMENT;
   2377 	    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
   2378                 type = SGML_CATA_CATALOG;
   2379 	    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
   2380                 type = SGML_CATA_BASE;
   2381 	    else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
   2382 		xmlFree(name);
   2383 		cur = xmlParseSGMLCatalogName(cur, &name);
   2384 		if (name == NULL) {
   2385 		    /* error */
   2386 		    break;
   2387 		}
   2388 		xmlFree(name);
   2389 		continue;
   2390 	    }
   2391 	    xmlFree(name);
   2392 	    name = NULL;
   2393 
   2394 	    switch(type) {
   2395 		case SGML_CATA_ENTITY:
   2396 		    if (*cur == '%')
   2397 			type = SGML_CATA_PENTITY;
   2398 		case SGML_CATA_PENTITY:
   2399 		case SGML_CATA_DOCTYPE:
   2400 		case SGML_CATA_LINKTYPE:
   2401 		case SGML_CATA_NOTATION:
   2402 		    cur = xmlParseSGMLCatalogName(cur, &name);
   2403 		    if (cur == NULL) {
   2404 			/* error */
   2405 			break;
   2406 		    }
   2407 		    if (!IS_BLANK_CH(*cur)) {
   2408 			/* error */
   2409 			break;
   2410 		    }
   2411 		    SKIP_BLANKS;
   2412 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
   2413 		    if (cur == NULL) {
   2414 			/* error */
   2415 			break;
   2416 		    }
   2417 		    break;
   2418 		case SGML_CATA_PUBLIC:
   2419 		case SGML_CATA_SYSTEM:
   2420 		case SGML_CATA_DELEGATE:
   2421 		    cur = xmlParseSGMLCatalogPubid(cur, &name);
   2422 		    if (cur == NULL) {
   2423 			/* error */
   2424 			break;
   2425 		    }
   2426 		    if (type != SGML_CATA_SYSTEM) {
   2427 		        xmlChar *normid;
   2428 
   2429 		        normid = xmlCatalogNormalizePublic(name);
   2430 		        if (normid != NULL) {
   2431 		            if (name != NULL)
   2432 		                xmlFree(name);
   2433 		            if (*normid != 0)
   2434 		                name = normid;
   2435 		            else {
   2436 		                xmlFree(normid);
   2437 		                name = NULL;
   2438 		            }
   2439 		        }
   2440 		    }
   2441 		    if (!IS_BLANK_CH(*cur)) {
   2442 			/* error */
   2443 			break;
   2444 		    }
   2445 		    SKIP_BLANKS;
   2446 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
   2447 		    if (cur == NULL) {
   2448 			/* error */
   2449 			break;
   2450 		    }
   2451 		    break;
   2452 		case SGML_CATA_BASE:
   2453 		case SGML_CATA_CATALOG:
   2454 		case SGML_CATA_DOCUMENT:
   2455 		case SGML_CATA_SGMLDECL:
   2456 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
   2457 		    if (cur == NULL) {
   2458 			/* error */
   2459 			break;
   2460 		    }
   2461 		    break;
   2462 		default:
   2463 		    break;
   2464 	    }
   2465 	    if (cur == NULL) {
   2466 		if (name != NULL)
   2467 		    xmlFree(name);
   2468 		if (sysid != NULL)
   2469 		    xmlFree(sysid);
   2470 		break;
   2471 	    } else if (type == SGML_CATA_BASE) {
   2472 		if (base != NULL)
   2473 		    xmlFree(base);
   2474 		base = xmlStrdup(sysid);
   2475 	    } else if ((type == SGML_CATA_PUBLIC) ||
   2476 		       (type == SGML_CATA_SYSTEM)) {
   2477 		xmlChar *filename;
   2478 
   2479 		filename = xmlBuildURI(sysid, base);
   2480 		if (filename != NULL) {
   2481 		    xmlCatalogEntryPtr entry;
   2482 
   2483 		    entry = xmlNewCatalogEntry(type, name, filename,
   2484 			                       NULL, XML_CATA_PREFER_NONE, NULL);
   2485 		    res = xmlHashAddEntry(catal->sgml, name, entry);
   2486 		    if (res < 0) {
   2487 			xmlFreeCatalogEntry(entry);
   2488 		    }
   2489 		    xmlFree(filename);
   2490 		}
   2491 
   2492 	    } else if (type == SGML_CATA_CATALOG) {
   2493 		if (super) {
   2494 		    xmlCatalogEntryPtr entry;
   2495 
   2496 		    entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
   2497 			                       XML_CATA_PREFER_NONE, NULL);
   2498 		    res = xmlHashAddEntry(catal->sgml, sysid, entry);
   2499 		    if (res < 0) {
   2500 			xmlFreeCatalogEntry(entry);
   2501 		    }
   2502 		} else {
   2503 		    xmlChar *filename;
   2504 
   2505 		    filename = xmlBuildURI(sysid, base);
   2506 		    if (filename != NULL) {
   2507 			xmlExpandCatalog(catal, (const char *)filename);
   2508 			xmlFree(filename);
   2509 		    }
   2510 		}
   2511 	    }
   2512 	    /*
   2513 	     * drop anything else we won't handle it
   2514 	     */
   2515 	    if (name != NULL)
   2516 		xmlFree(name);
   2517 	    if (sysid != NULL)
   2518 		xmlFree(sysid);
   2519 	}
   2520     }
   2521     if (base != NULL)
   2522 	xmlFree(base);
   2523     if (cur == NULL)
   2524 	return(-1);
   2525     return(0);
   2526 }
   2527 
   2528 /************************************************************************
   2529  *									*
   2530  *			SGML Catalog handling				*
   2531  *									*
   2532  ************************************************************************/
   2533 
   2534 /**
   2535  * xmlCatalogGetSGMLPublic:
   2536  * @catal:  an SGML catalog hash
   2537  * @pubID:  the public ID string
   2538  *
   2539  * Try to lookup the catalog local reference associated to a public ID
   2540  *
   2541  * Returns the local resource if found or NULL otherwise.
   2542  */
   2543 static const xmlChar *
   2544 xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
   2545     xmlCatalogEntryPtr entry;
   2546     xmlChar *normid;
   2547 
   2548     if (catal == NULL)
   2549 	return(NULL);
   2550 
   2551     normid = xmlCatalogNormalizePublic(pubID);
   2552     if (normid != NULL)
   2553         pubID = (*normid != 0 ? normid : NULL);
   2554 
   2555     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
   2556     if (entry == NULL) {
   2557 	if (normid != NULL)
   2558 	    xmlFree(normid);
   2559 	return(NULL);
   2560     }
   2561     if (entry->type == SGML_CATA_PUBLIC) {
   2562 	if (normid != NULL)
   2563 	    xmlFree(normid);
   2564 	return(entry->URL);
   2565     }
   2566     if (normid != NULL)
   2567         xmlFree(normid);
   2568     return(NULL);
   2569 }
   2570 
   2571 /**
   2572  * xmlCatalogGetSGMLSystem:
   2573  * @catal:  an SGML catalog hash
   2574  * @sysID:  the system ID string
   2575  *
   2576  * Try to lookup the catalog local reference for a system ID
   2577  *
   2578  * Returns the local resource if found or NULL otherwise.
   2579  */
   2580 static const xmlChar *
   2581 xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
   2582     xmlCatalogEntryPtr entry;
   2583 
   2584     if (catal == NULL)
   2585 	return(NULL);
   2586 
   2587     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
   2588     if (entry == NULL)
   2589 	return(NULL);
   2590     if (entry->type == SGML_CATA_SYSTEM)
   2591 	return(entry->URL);
   2592     return(NULL);
   2593 }
   2594 
   2595 /**
   2596  * xmlCatalogSGMLResolve:
   2597  * @catal:  the SGML catalog
   2598  * @pubID:  the public ID string
   2599  * @sysID:  the system ID string
   2600  *
   2601  * Do a complete resolution lookup of an External Identifier
   2602  *
   2603  * Returns the URI of the resource or NULL if not found
   2604  */
   2605 static const xmlChar *
   2606 xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
   2607 	              const xmlChar *sysID) {
   2608     const xmlChar *ret = NULL;
   2609 
   2610     if (catal->sgml == NULL)
   2611 	return(NULL);
   2612 
   2613     if (pubID != NULL)
   2614 	ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
   2615     if (ret != NULL)
   2616 	return(ret);
   2617     if (sysID != NULL)
   2618 	ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
   2619     if (ret != NULL)
   2620 	return(ret);
   2621     return(NULL);
   2622 }
   2623 
   2624 /************************************************************************
   2625  *									*
   2626  *			Specific Public interfaces			*
   2627  *									*
   2628  ************************************************************************/
   2629 
   2630 /**
   2631  * xmlLoadSGMLSuperCatalog:
   2632  * @filename:  a file path
   2633  *
   2634  * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
   2635  * references. This is only needed for manipulating SGML Super Catalogs
   2636  * like adding and removing CATALOG or DELEGATE entries.
   2637  *
   2638  * Returns the catalog parsed or NULL in case of error
   2639  */
   2640 xmlCatalogPtr
   2641 xmlLoadSGMLSuperCatalog(const char *filename)
   2642 {
   2643     xmlChar *content;
   2644     xmlCatalogPtr catal;
   2645     int ret;
   2646 
   2647     content = xmlLoadFileContent(filename);
   2648     if (content == NULL)
   2649         return(NULL);
   2650 
   2651     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
   2652     if (catal == NULL) {
   2653 	xmlFree(content);
   2654 	return(NULL);
   2655     }
   2656 
   2657     ret = xmlParseSGMLCatalog(catal, content, filename, 1);
   2658     xmlFree(content);
   2659     if (ret < 0) {
   2660 	xmlFreeCatalog(catal);
   2661 	return(NULL);
   2662     }
   2663     return (catal);
   2664 }
   2665 
   2666 /**
   2667  * xmlLoadACatalog:
   2668  * @filename:  a file path
   2669  *
   2670  * Load the catalog and build the associated data structures.
   2671  * This can be either an XML Catalog or an SGML Catalog
   2672  * It will recurse in SGML CATALOG entries. On the other hand XML
   2673  * Catalogs are not handled recursively.
   2674  *
   2675  * Returns the catalog parsed or NULL in case of error
   2676  */
   2677 xmlCatalogPtr
   2678 xmlLoadACatalog(const char *filename)
   2679 {
   2680     xmlChar *content;
   2681     xmlChar *first;
   2682     xmlCatalogPtr catal;
   2683     int ret;
   2684 
   2685     content = xmlLoadFileContent(filename);
   2686     if (content == NULL)
   2687         return(NULL);
   2688 
   2689 
   2690     first = content;
   2691 
   2692     while ((*first != 0) && (*first != '-') && (*first != '<') &&
   2693 	   (!(((*first >= 'A') && (*first <= 'Z')) ||
   2694 	      ((*first >= 'a') && (*first <= 'z')))))
   2695 	first++;
   2696 
   2697     if (*first != '<') {
   2698 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
   2699 	if (catal == NULL) {
   2700 	    xmlFree(content);
   2701 	    return(NULL);
   2702 	}
   2703         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
   2704 	if (ret < 0) {
   2705 	    xmlFreeCatalog(catal);
   2706 	    xmlFree(content);
   2707 	    return(NULL);
   2708 	}
   2709     } else {
   2710 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
   2711 	if (catal == NULL) {
   2712 	    xmlFree(content);
   2713 	    return(NULL);
   2714 	}
   2715         catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
   2716 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
   2717     }
   2718     xmlFree(content);
   2719     return (catal);
   2720 }
   2721 
   2722 /**
   2723  * xmlExpandCatalog:
   2724  * @catal:  a catalog
   2725  * @filename:  a file path
   2726  *
   2727  * Load the catalog and expand the existing catal structure.
   2728  * This can be either an XML Catalog or an SGML Catalog
   2729  *
   2730  * Returns 0 in case of success, -1 in case of error
   2731  */
   2732 static int
   2733 xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
   2734 {
   2735     int ret;
   2736 
   2737     if ((catal == NULL) || (filename == NULL))
   2738 	return(-1);
   2739 
   2740 
   2741     if (catal->type == XML_SGML_CATALOG_TYPE) {
   2742 	xmlChar *content;
   2743 
   2744 	content = xmlLoadFileContent(filename);
   2745 	if (content == NULL)
   2746 	    return(-1);
   2747 
   2748         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
   2749 	if (ret < 0) {
   2750 	    xmlFree(content);
   2751 	    return(-1);
   2752 	}
   2753 	xmlFree(content);
   2754     } else {
   2755 	xmlCatalogEntryPtr tmp, cur;
   2756 	tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
   2757 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
   2758 
   2759 	cur = catal->xml;
   2760 	if (cur == NULL) {
   2761 	    catal->xml = tmp;
   2762 	} else {
   2763 	    while (cur->next != NULL) cur = cur->next;
   2764 	    cur->next = tmp;
   2765 	}
   2766     }
   2767     return (0);
   2768 }
   2769 
   2770 /**
   2771  * xmlACatalogResolveSystem:
   2772  * @catal:  a Catalog
   2773  * @sysID:  the system ID string
   2774  *
   2775  * Try to lookup the catalog resource for a system ID
   2776  *
   2777  * Returns the resource if found or NULL otherwise, the value returned
   2778  *      must be freed by the caller.
   2779  */
   2780 xmlChar *
   2781 xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
   2782     xmlChar *ret = NULL;
   2783 
   2784     if ((sysID == NULL) || (catal == NULL))
   2785 	return(NULL);
   2786 
   2787     if (xmlDebugCatalogs)
   2788 	xmlGenericError(xmlGenericErrorContext,
   2789 		"Resolve sysID %s\n", sysID);
   2790 
   2791     if (catal->type == XML_XML_CATALOG_TYPE) {
   2792 	ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
   2793 	if (ret == XML_CATAL_BREAK)
   2794 	    ret = NULL;
   2795     } else {
   2796 	const xmlChar *sgml;
   2797 
   2798 	sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
   2799 	if (sgml != NULL)
   2800 	    ret = xmlStrdup(sgml);
   2801     }
   2802     return(ret);
   2803 }
   2804 
   2805 /**
   2806  * xmlACatalogResolvePublic:
   2807  * @catal:  a Catalog
   2808  * @pubID:  the public ID string
   2809  *
   2810  * Try to lookup the catalog local reference associated to a public ID in that catalog
   2811  *
   2812  * Returns the local resource if found or NULL otherwise, the value returned
   2813  *      must be freed by the caller.
   2814  */
   2815 xmlChar *
   2816 xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
   2817     xmlChar *ret = NULL;
   2818 
   2819     if ((pubID == NULL) || (catal == NULL))
   2820 	return(NULL);
   2821 
   2822     if (xmlDebugCatalogs)
   2823 	xmlGenericError(xmlGenericErrorContext,
   2824 		"Resolve pubID %s\n", pubID);
   2825 
   2826     if (catal->type == XML_XML_CATALOG_TYPE) {
   2827 	ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
   2828 	if (ret == XML_CATAL_BREAK)
   2829 	    ret = NULL;
   2830     } else {
   2831 	const xmlChar *sgml;
   2832 
   2833 	sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
   2834 	if (sgml != NULL)
   2835 	    ret = xmlStrdup(sgml);
   2836     }
   2837     return(ret);
   2838 }
   2839 
   2840 /**
   2841  * xmlACatalogResolve:
   2842  * @catal:  a Catalog
   2843  * @pubID:  the public ID string
   2844  * @sysID:  the system ID string
   2845  *
   2846  * Do a complete resolution lookup of an External Identifier
   2847  *
   2848  * Returns the URI of the resource or NULL if not found, it must be freed
   2849  *      by the caller.
   2850  */
   2851 xmlChar *
   2852 xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
   2853                    const xmlChar * sysID)
   2854 {
   2855     xmlChar *ret = NULL;
   2856 
   2857     if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
   2858         return (NULL);
   2859 
   2860     if (xmlDebugCatalogs) {
   2861          if ((pubID != NULL) && (sysID != NULL)) {
   2862              xmlGenericError(xmlGenericErrorContext,
   2863                              "Resolve: pubID %s sysID %s\n", pubID, sysID);
   2864          } else if (pubID != NULL) {
   2865              xmlGenericError(xmlGenericErrorContext,
   2866                              "Resolve: pubID %s\n", pubID);
   2867          } else {
   2868              xmlGenericError(xmlGenericErrorContext,
   2869                              "Resolve: sysID %s\n", sysID);
   2870          }
   2871     }
   2872 
   2873     if (catal->type == XML_XML_CATALOG_TYPE) {
   2874         ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
   2875 	if (ret == XML_CATAL_BREAK)
   2876 	    ret = NULL;
   2877     } else {
   2878         const xmlChar *sgml;
   2879 
   2880         sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
   2881         if (sgml != NULL)
   2882             ret = xmlStrdup(sgml);
   2883     }
   2884     return (ret);
   2885 }
   2886 
   2887 /**
   2888  * xmlACatalogResolveURI:
   2889  * @catal:  a Catalog
   2890  * @URI:  the URI
   2891  *
   2892  * Do a complete resolution lookup of an URI
   2893  *
   2894  * Returns the URI of the resource or NULL if not found, it must be freed
   2895  *      by the caller.
   2896  */
   2897 xmlChar *
   2898 xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
   2899     xmlChar *ret = NULL;
   2900 
   2901     if ((URI == NULL) || (catal == NULL))
   2902 	return(NULL);
   2903 
   2904     if (xmlDebugCatalogs)
   2905 	xmlGenericError(xmlGenericErrorContext,
   2906 		"Resolve URI %s\n", URI);
   2907 
   2908     if (catal->type == XML_XML_CATALOG_TYPE) {
   2909 	ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
   2910 	if (ret == XML_CATAL_BREAK)
   2911 	    ret = NULL;
   2912     } else {
   2913 	const xmlChar *sgml;
   2914 
   2915 	sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
   2916 	if (sgml != NULL)
   2917             ret = xmlStrdup(sgml);
   2918     }
   2919     return(ret);
   2920 }
   2921 
   2922 #ifdef LIBXML_OUTPUT_ENABLED
   2923 /**
   2924  * xmlACatalogDump:
   2925  * @catal:  a Catalog
   2926  * @out:  the file.
   2927  *
   2928  * Dump the given catalog to the given file.
   2929  */
   2930 void
   2931 xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
   2932     if ((out == NULL) || (catal == NULL))
   2933 	return;
   2934 
   2935     if (catal->type == XML_XML_CATALOG_TYPE) {
   2936 	xmlDumpXMLCatalog(out, catal->xml);
   2937     } else {
   2938 	xmlHashScan(catal->sgml,
   2939 		    (xmlHashScanner) xmlCatalogDumpEntry, out);
   2940     }
   2941 }
   2942 #endif /* LIBXML_OUTPUT_ENABLED */
   2943 
   2944 /**
   2945  * xmlACatalogAdd:
   2946  * @catal:  a Catalog
   2947  * @type:  the type of record to add to the catalog
   2948  * @orig:  the system, public or prefix to match
   2949  * @replace:  the replacement value for the match
   2950  *
   2951  * Add an entry in the catalog, it may overwrite existing but
   2952  * different entries.
   2953  *
   2954  * Returns 0 if successful, -1 otherwise
   2955  */
   2956 int
   2957 xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
   2958               const xmlChar * orig, const xmlChar * replace)
   2959 {
   2960     int res = -1;
   2961 
   2962     if (catal == NULL)
   2963 	return(-1);
   2964 
   2965     if (catal->type == XML_XML_CATALOG_TYPE) {
   2966         res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
   2967     } else {
   2968         xmlCatalogEntryType cattype;
   2969 
   2970         cattype = xmlGetSGMLCatalogEntryType(type);
   2971         if (cattype != XML_CATA_NONE) {
   2972             xmlCatalogEntryPtr entry;
   2973 
   2974             entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
   2975                                        XML_CATA_PREFER_NONE, NULL);
   2976 	    if (catal->sgml == NULL)
   2977 		catal->sgml = xmlHashCreate(10);
   2978             res = xmlHashAddEntry(catal->sgml, orig, entry);
   2979         }
   2980     }
   2981     return (res);
   2982 }
   2983 
   2984 /**
   2985  * xmlACatalogRemove:
   2986  * @catal:  a Catalog
   2987  * @value:  the value to remove
   2988  *
   2989  * Remove an entry from the catalog
   2990  *
   2991  * Returns the number of entries removed if successful, -1 otherwise
   2992  */
   2993 int
   2994 xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
   2995     int res = -1;
   2996 
   2997     if ((catal == NULL) || (value == NULL))
   2998 	return(-1);
   2999 
   3000     if (catal->type == XML_XML_CATALOG_TYPE) {
   3001 	res = xmlDelXMLCatalog(catal->xml, value);
   3002     } else {
   3003 	res = xmlHashRemoveEntry(catal->sgml, value,
   3004 		(xmlHashDeallocator) xmlFreeCatalogEntry);
   3005 	if (res == 0)
   3006 	    res = 1;
   3007     }
   3008     return(res);
   3009 }
   3010 
   3011 /**
   3012  * xmlNewCatalog:
   3013  * @sgml:  should this create an SGML catalog
   3014  *
   3015  * create a new Catalog.
   3016  *
   3017  * Returns the xmlCatalogPtr or NULL in case of error
   3018  */
   3019 xmlCatalogPtr
   3020 xmlNewCatalog(int sgml) {
   3021     xmlCatalogPtr catal = NULL;
   3022 
   3023     if (sgml) {
   3024 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
   3025 		                    xmlCatalogDefaultPrefer);
   3026         if ((catal != NULL) && (catal->sgml == NULL))
   3027 	    catal->sgml = xmlHashCreate(10);
   3028     } else
   3029 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
   3030 		                    xmlCatalogDefaultPrefer);
   3031     return(catal);
   3032 }
   3033 
   3034 /**
   3035  * xmlCatalogIsEmpty:
   3036  * @catal:  should this create an SGML catalog
   3037  *
   3038  * Check is a catalog is empty
   3039  *
   3040  * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
   3041  */
   3042 int
   3043 xmlCatalogIsEmpty(xmlCatalogPtr catal) {
   3044     if (catal == NULL)
   3045 	return(-1);
   3046 
   3047     if (catal->type == XML_XML_CATALOG_TYPE) {
   3048 	if (catal->xml == NULL)
   3049 	    return(1);
   3050 	if ((catal->xml->type != XML_CATA_CATALOG) &&
   3051 	    (catal->xml->type != XML_CATA_BROKEN_CATALOG))
   3052 	    return(-1);
   3053 	if (catal->xml->children == NULL)
   3054 	    return(1);
   3055         return(0);
   3056     } else {
   3057 	int res;
   3058 
   3059 	if (catal->sgml == NULL)
   3060 	    return(1);
   3061 	res = xmlHashSize(catal->sgml);
   3062 	if (res == 0)
   3063 	    return(1);
   3064 	if (res < 0)
   3065 	    return(-1);
   3066     }
   3067     return(0);
   3068 }
   3069 
   3070 /************************************************************************
   3071  *									*
   3072  *   Public interfaces manipulating the global shared default catalog	*
   3073  *									*
   3074  ************************************************************************/
   3075 
   3076 /**
   3077  * xmlInitializeCatalogData:
   3078  *
   3079  * Do the catalog initialization only of global data, doesn't try to load
   3080  * any catalog actually.
   3081  * this function is not thread safe, catalog initialization should
   3082  * preferably be done once at startup
   3083  */
   3084 static void
   3085 xmlInitializeCatalogData(void) {
   3086     if (xmlCatalogInitialized != 0)
   3087 	return;
   3088 
   3089     if (getenv("XML_DEBUG_CATALOG"))
   3090 	xmlDebugCatalogs = 1;
   3091     xmlCatalogMutex = xmlNewRMutex();
   3092 
   3093     xmlCatalogInitialized = 1;
   3094 }
   3095 /**
   3096  * xmlInitializeCatalog:
   3097  *
   3098  * Do the catalog initialization.
   3099  * this function is not thread safe, catalog initialization should
   3100  * preferably be done once at startup
   3101  */
   3102 void
   3103 xmlInitializeCatalog(void) {
   3104     if (xmlCatalogInitialized != 0)
   3105 	return;
   3106 
   3107     xmlInitializeCatalogData();
   3108     xmlRMutexLock(xmlCatalogMutex);
   3109 
   3110     if (getenv("XML_DEBUG_CATALOG"))
   3111 	xmlDebugCatalogs = 1;
   3112 
   3113     if (xmlDefaultCatalog == NULL) {
   3114 	const char *catalogs;
   3115 	char *path;
   3116 	const char *cur, *paths;
   3117 	xmlCatalogPtr catal;
   3118 	xmlCatalogEntryPtr *nextent;
   3119 
   3120 	catalogs = (const char *) getenv("XML_CATALOG_FILES");
   3121 	if (catalogs == NULL)
   3122 #if defined(_WIN32) && defined(_MSC_VER)
   3123     {
   3124 		void* hmodule;
   3125 		hmodule = GetModuleHandleA("libxml2.dll");
   3126 		if (hmodule == NULL)
   3127 			hmodule = GetModuleHandleA(NULL);
   3128 		if (hmodule != NULL) {
   3129 			char buf[256];
   3130 			unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
   3131 			if (len != 0) {
   3132 				char* p = &(buf[len]);
   3133 				while (*p != '\\' && p > buf)
   3134 					p--;
   3135 				if (p != buf) {
   3136 					xmlChar* uri;
   3137 					strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
   3138 					uri = xmlCanonicPath(buf);
   3139 					if (uri != NULL) {
   3140 						strncpy(XML_XML_DEFAULT_CATALOG, uri, 255);
   3141 						xmlFree(uri);
   3142 					}
   3143 				}
   3144 			}
   3145 		}
   3146 		catalogs = XML_XML_DEFAULT_CATALOG;
   3147     }
   3148 #else
   3149 	    catalogs = XML_XML_DEFAULT_CATALOG;
   3150 #endif
   3151 
   3152 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
   3153 		xmlCatalogDefaultPrefer);
   3154 	if (catal != NULL) {
   3155 	    /* the XML_CATALOG_FILES envvar is allowed to contain a
   3156 	       space-separated list of entries. */
   3157 	    cur = catalogs;
   3158 	    nextent = &catal->xml;
   3159 	    while (*cur != '\0') {
   3160 		while (xmlIsBlank_ch(*cur))
   3161 		    cur++;
   3162 		if (*cur != 0) {
   3163 		    paths = cur;
   3164 		    while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
   3165 			cur++;
   3166 		    path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
   3167 		    if (path != NULL) {
   3168 			*nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
   3169 				NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
   3170 			if (*nextent != NULL)
   3171 			    nextent = &((*nextent)->next);
   3172 			xmlFree(path);
   3173 		    }
   3174 		}
   3175 	    }
   3176 	    xmlDefaultCatalog = catal;
   3177 	}
   3178     }
   3179 
   3180     xmlRMutexUnlock(xmlCatalogMutex);
   3181 }
   3182 
   3183 
   3184 /**
   3185  * xmlLoadCatalog:
   3186  * @filename:  a file path
   3187  *
   3188  * Load the catalog and makes its definitions effective for the default
   3189  * external entity loader. It will recurse in SGML CATALOG entries.
   3190  * this function is not thread safe, catalog initialization should
   3191  * preferably be done once at startup
   3192  *
   3193  * Returns 0 in case of success -1 in case of error
   3194  */
   3195 int
   3196 xmlLoadCatalog(const char *filename)
   3197 {
   3198     int ret;
   3199     xmlCatalogPtr catal;
   3200 
   3201     if (!xmlCatalogInitialized)
   3202 	xmlInitializeCatalogData();
   3203 
   3204     xmlRMutexLock(xmlCatalogMutex);
   3205 
   3206     if (xmlDefaultCatalog == NULL) {
   3207 	catal = xmlLoadACatalog(filename);
   3208 	if (catal == NULL) {
   3209 	    xmlRMutexUnlock(xmlCatalogMutex);
   3210 	    return(-1);
   3211 	}
   3212 
   3213 	xmlDefaultCatalog = catal;
   3214 	xmlRMutexUnlock(xmlCatalogMutex);
   3215 	return(0);
   3216     }
   3217 
   3218     ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
   3219     xmlRMutexUnlock(xmlCatalogMutex);
   3220     return(ret);
   3221 }
   3222 
   3223 /**
   3224  * xmlLoadCatalogs:
   3225  * @pathss:  a list of directories separated by a colon or a space.
   3226  *
   3227  * Load the catalogs and makes their definitions effective for the default
   3228  * external entity loader.
   3229  * this function is not thread safe, catalog initialization should
   3230  * preferably be done once at startup
   3231  */
   3232 void
   3233 xmlLoadCatalogs(const char *pathss) {
   3234     const char *cur;
   3235     const char *paths;
   3236     xmlChar *path;
   3237 #ifdef _WIN32
   3238     int i, iLen;
   3239 #endif
   3240 
   3241     if (pathss == NULL)
   3242 	return;
   3243 
   3244     cur = pathss;
   3245     while (*cur != 0) {
   3246 	while (xmlIsBlank_ch(*cur)) cur++;
   3247 	if (*cur != 0) {
   3248 	    paths = cur;
   3249 	    while ((*cur != 0) && (*cur != PATH_SEAPARATOR) && (!xmlIsBlank_ch(*cur)))
   3250 		cur++;
   3251 	    path = xmlStrndup((const xmlChar *)paths, cur - paths);
   3252 #ifdef _WIN32
   3253         iLen = strlen(path);
   3254         for(i = 0; i < iLen; i++) {
   3255             if(path[i] == '\\') {
   3256                 path[i] = '/';
   3257             }
   3258         }
   3259 #endif
   3260 	    if (path != NULL) {
   3261 		xmlLoadCatalog((const char *) path);
   3262 		xmlFree(path);
   3263 	    }
   3264 	}
   3265 	while (*cur == PATH_SEAPARATOR)
   3266 	    cur++;
   3267     }
   3268 }
   3269 
   3270 /**
   3271  * xmlCatalogCleanup:
   3272  *
   3273  * Free up all the memory associated with catalogs
   3274  */
   3275 void
   3276 xmlCatalogCleanup(void) {
   3277     if (xmlCatalogInitialized == 0)
   3278         return;
   3279 
   3280     xmlRMutexLock(xmlCatalogMutex);
   3281     if (xmlDebugCatalogs)
   3282 	xmlGenericError(xmlGenericErrorContext,
   3283 		"Catalogs cleanup\n");
   3284     if (xmlCatalogXMLFiles != NULL)
   3285 	xmlHashFree(xmlCatalogXMLFiles,
   3286 		    (xmlHashDeallocator)xmlFreeCatalogHashEntryList);
   3287     xmlCatalogXMLFiles = NULL;
   3288     if (xmlDefaultCatalog != NULL)
   3289 	xmlFreeCatalog(xmlDefaultCatalog);
   3290     xmlDefaultCatalog = NULL;
   3291     xmlDebugCatalogs = 0;
   3292     xmlCatalogInitialized = 0;
   3293     xmlRMutexUnlock(xmlCatalogMutex);
   3294     xmlFreeRMutex(xmlCatalogMutex);
   3295 }
   3296 
   3297 /**
   3298  * xmlCatalogResolveSystem:
   3299  * @sysID:  the system ID string
   3300  *
   3301  * Try to lookup the catalog resource for a system ID
   3302  *
   3303  * Returns the resource if found or NULL otherwise, the value returned
   3304  *      must be freed by the caller.
   3305  */
   3306 xmlChar *
   3307 xmlCatalogResolveSystem(const xmlChar *sysID) {
   3308     xmlChar *ret;
   3309 
   3310     if (!xmlCatalogInitialized)
   3311 	xmlInitializeCatalog();
   3312 
   3313     ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
   3314     return(ret);
   3315 }
   3316 
   3317 /**
   3318  * xmlCatalogResolvePublic:
   3319  * @pubID:  the public ID string
   3320  *
   3321  * Try to lookup the catalog reference associated to a public ID
   3322  *
   3323  * Returns the resource if found or NULL otherwise, the value returned
   3324  *      must be freed by the caller.
   3325  */
   3326 xmlChar *
   3327 xmlCatalogResolvePublic(const xmlChar *pubID) {
   3328     xmlChar *ret;
   3329 
   3330     if (!xmlCatalogInitialized)
   3331 	xmlInitializeCatalog();
   3332 
   3333     ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
   3334     return(ret);
   3335 }
   3336 
   3337 /**
   3338  * xmlCatalogResolve:
   3339  * @pubID:  the public ID string
   3340  * @sysID:  the system ID string
   3341  *
   3342  * Do a complete resolution lookup of an External Identifier
   3343  *
   3344  * Returns the URI of the resource or NULL if not found, it must be freed
   3345  *      by the caller.
   3346  */
   3347 xmlChar *
   3348 xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
   3349     xmlChar *ret;
   3350 
   3351     if (!xmlCatalogInitialized)
   3352 	xmlInitializeCatalog();
   3353 
   3354     ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
   3355     return(ret);
   3356 }
   3357 
   3358 /**
   3359  * xmlCatalogResolveURI:
   3360  * @URI:  the URI
   3361  *
   3362  * Do a complete resolution lookup of an URI
   3363  *
   3364  * Returns the URI of the resource or NULL if not found, it must be freed
   3365  *      by the caller.
   3366  */
   3367 xmlChar *
   3368 xmlCatalogResolveURI(const xmlChar *URI) {
   3369     xmlChar *ret;
   3370 
   3371     if (!xmlCatalogInitialized)
   3372 	xmlInitializeCatalog();
   3373 
   3374     ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
   3375     return(ret);
   3376 }
   3377 
   3378 #ifdef LIBXML_OUTPUT_ENABLED
   3379 /**
   3380  * xmlCatalogDump:
   3381  * @out:  the file.
   3382  *
   3383  * Dump all the global catalog content to the given file.
   3384  */
   3385 void
   3386 xmlCatalogDump(FILE *out) {
   3387     if (out == NULL)
   3388 	return;
   3389 
   3390     if (!xmlCatalogInitialized)
   3391 	xmlInitializeCatalog();
   3392 
   3393     xmlACatalogDump(xmlDefaultCatalog, out);
   3394 }
   3395 #endif /* LIBXML_OUTPUT_ENABLED */
   3396 
   3397 /**
   3398  * xmlCatalogAdd:
   3399  * @type:  the type of record to add to the catalog
   3400  * @orig:  the system, public or prefix to match
   3401  * @replace:  the replacement value for the match
   3402  *
   3403  * Add an entry in the catalog, it may overwrite existing but
   3404  * different entries.
   3405  * If called before any other catalog routine, allows to override the
   3406  * default shared catalog put in place by xmlInitializeCatalog();
   3407  *
   3408  * Returns 0 if successful, -1 otherwise
   3409  */
   3410 int
   3411 xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
   3412     int res = -1;
   3413 
   3414     if (!xmlCatalogInitialized)
   3415 	xmlInitializeCatalogData();
   3416 
   3417     xmlRMutexLock(xmlCatalogMutex);
   3418     /*
   3419      * Specific case where one want to override the default catalog
   3420      * put in place by xmlInitializeCatalog();
   3421      */
   3422     if ((xmlDefaultCatalog == NULL) &&
   3423 	(xmlStrEqual(type, BAD_CAST "catalog"))) {
   3424 	xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
   3425 		                          xmlCatalogDefaultPrefer);
   3426 	xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
   3427 				    orig, NULL,  xmlCatalogDefaultPrefer, NULL);
   3428 
   3429 	xmlRMutexUnlock(xmlCatalogMutex);
   3430 	return(0);
   3431     }
   3432 
   3433     res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
   3434     xmlRMutexUnlock(xmlCatalogMutex);
   3435     return(res);
   3436 }
   3437 
   3438 /**
   3439  * xmlCatalogRemove:
   3440  * @value:  the value to remove
   3441  *
   3442  * Remove an entry from the catalog
   3443  *
   3444  * Returns the number of entries removed if successful, -1 otherwise
   3445  */
   3446 int
   3447 xmlCatalogRemove(const xmlChar *value) {
   3448     int res;
   3449 
   3450     if (!xmlCatalogInitialized)
   3451 	xmlInitializeCatalog();
   3452 
   3453     xmlRMutexLock(xmlCatalogMutex);
   3454     res = xmlACatalogRemove(xmlDefaultCatalog, value);
   3455     xmlRMutexUnlock(xmlCatalogMutex);
   3456     return(res);
   3457 }
   3458 
   3459 /**
   3460  * xmlCatalogConvert:
   3461  *
   3462  * Convert all the SGML catalog entries as XML ones
   3463  *
   3464  * Returns the number of entries converted if successful, -1 otherwise
   3465  */
   3466 int
   3467 xmlCatalogConvert(void) {
   3468     int res = -1;
   3469 
   3470     if (!xmlCatalogInitialized)
   3471 	xmlInitializeCatalog();
   3472 
   3473     xmlRMutexLock(xmlCatalogMutex);
   3474     res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
   3475     xmlRMutexUnlock(xmlCatalogMutex);
   3476     return(res);
   3477 }
   3478 
   3479 /************************************************************************
   3480  *									*
   3481  *	Public interface manipulating the common preferences		*
   3482  *									*
   3483  ************************************************************************/
   3484 
   3485 /**
   3486  * xmlCatalogGetDefaults:
   3487  *
   3488  * Used to get the user preference w.r.t. to what catalogs should
   3489  * be accepted
   3490  *
   3491  * Returns the current xmlCatalogAllow value
   3492  */
   3493 xmlCatalogAllow
   3494 xmlCatalogGetDefaults(void) {
   3495     return(xmlCatalogDefaultAllow);
   3496 }
   3497 
   3498 /**
   3499  * xmlCatalogSetDefaults:
   3500  * @allow:  what catalogs should be accepted
   3501  *
   3502  * Used to set the user preference w.r.t. to what catalogs should
   3503  * be accepted
   3504  */
   3505 void
   3506 xmlCatalogSetDefaults(xmlCatalogAllow allow) {
   3507     if (xmlDebugCatalogs) {
   3508 	switch (allow) {
   3509 	    case XML_CATA_ALLOW_NONE:
   3510 		xmlGenericError(xmlGenericErrorContext,
   3511 			"Disabling catalog usage\n");
   3512 		break;
   3513 	    case XML_CATA_ALLOW_GLOBAL:
   3514 		xmlGenericError(xmlGenericErrorContext,
   3515 			"Allowing only global catalogs\n");
   3516 		break;
   3517 	    case XML_CATA_ALLOW_DOCUMENT:
   3518 		xmlGenericError(xmlGenericErrorContext,
   3519 			"Allowing only catalogs from the document\n");
   3520 		break;
   3521 	    case XML_CATA_ALLOW_ALL:
   3522 		xmlGenericError(xmlGenericErrorContext,
   3523 			"Allowing all catalogs\n");
   3524 		break;
   3525 	}
   3526     }
   3527     xmlCatalogDefaultAllow = allow;
   3528 }
   3529 
   3530 /**
   3531  * xmlCatalogSetDefaultPrefer:
   3532  * @prefer:  the default preference for delegation
   3533  *
   3534  * Allows to set the preference between public and system for deletion
   3535  * in XML Catalog resolution. C.f. section 4.1.1 of the spec
   3536  * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
   3537  *
   3538  * Returns the previous value of the default preference for delegation
   3539  */
   3540 xmlCatalogPrefer
   3541 xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
   3542     xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
   3543 
   3544     if (prefer == XML_CATA_PREFER_NONE)
   3545 	return(ret);
   3546 
   3547     if (xmlDebugCatalogs) {
   3548 	switch (prefer) {
   3549 	    case XML_CATA_PREFER_PUBLIC:
   3550 		xmlGenericError(xmlGenericErrorContext,
   3551 			"Setting catalog preference to PUBLIC\n");
   3552 		break;
   3553 	    case XML_CATA_PREFER_SYSTEM:
   3554 		xmlGenericError(xmlGenericErrorContext,
   3555 			"Setting catalog preference to SYSTEM\n");
   3556 		break;
   3557 	    case XML_CATA_PREFER_NONE:
   3558 		break;
   3559 	}
   3560     }
   3561     xmlCatalogDefaultPrefer = prefer;
   3562     return(ret);
   3563 }
   3564 
   3565 /**
   3566  * xmlCatalogSetDebug:
   3567  * @level:  the debug level of catalogs required
   3568  *
   3569  * Used to set the debug level for catalog operation, 0 disable
   3570  * debugging, 1 enable it
   3571  *
   3572  * Returns the previous value of the catalog debugging level
   3573  */
   3574 int
   3575 xmlCatalogSetDebug(int level) {
   3576     int ret = xmlDebugCatalogs;
   3577 
   3578     if (level <= 0)
   3579         xmlDebugCatalogs = 0;
   3580     else
   3581 	xmlDebugCatalogs = level;
   3582     return(ret);
   3583 }
   3584 
   3585 /************************************************************************
   3586  *									*
   3587  *   Minimal interfaces used for per-document catalogs by the parser	*
   3588  *									*
   3589  ************************************************************************/
   3590 
   3591 /**
   3592  * xmlCatalogFreeLocal:
   3593  * @catalogs:  a document's list of catalogs
   3594  *
   3595  * Free up the memory associated to the catalog list
   3596  */
   3597 void
   3598 xmlCatalogFreeLocal(void *catalogs) {
   3599     xmlCatalogEntryPtr catal;
   3600 
   3601     if (!xmlCatalogInitialized)
   3602 	xmlInitializeCatalog();
   3603 
   3604     catal = (xmlCatalogEntryPtr) catalogs;
   3605     if (catal != NULL)
   3606 	xmlFreeCatalogEntryList(catal);
   3607 }
   3608 
   3609 
   3610 /**
   3611  * xmlCatalogAddLocal:
   3612  * @catalogs:  a document's list of catalogs
   3613  * @URL:  the URL to a new local catalog
   3614  *
   3615  * Add the new entry to the catalog list
   3616  *
   3617  * Returns the updated list
   3618  */
   3619 void *
   3620 xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
   3621     xmlCatalogEntryPtr catal, add;
   3622 
   3623     if (!xmlCatalogInitialized)
   3624 	xmlInitializeCatalog();
   3625 
   3626     if (URL == NULL)
   3627 	return(catalogs);
   3628 
   3629     if (xmlDebugCatalogs)
   3630 	xmlGenericError(xmlGenericErrorContext,
   3631 		"Adding document catalog %s\n", URL);
   3632 
   3633     add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
   3634 	                     xmlCatalogDefaultPrefer, NULL);
   3635     if (add == NULL)
   3636 	return(catalogs);
   3637 
   3638     catal = (xmlCatalogEntryPtr) catalogs;
   3639     if (catal == NULL)
   3640 	return((void *) add);
   3641 
   3642     while (catal->next != NULL)
   3643 	catal = catal->next;
   3644     catal->next = add;
   3645     return(catalogs);
   3646 }
   3647 
   3648 /**
   3649  * xmlCatalogLocalResolve:
   3650  * @catalogs:  a document's list of catalogs
   3651  * @pubID:  the public ID string
   3652  * @sysID:  the system ID string
   3653  *
   3654  * Do a complete resolution lookup of an External Identifier using a
   3655  * document's private catalog list
   3656  *
   3657  * Returns the URI of the resource or NULL if not found, it must be freed
   3658  *      by the caller.
   3659  */
   3660 xmlChar *
   3661 xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
   3662 	               const xmlChar *sysID) {
   3663     xmlCatalogEntryPtr catal;
   3664     xmlChar *ret;
   3665 
   3666     if (!xmlCatalogInitialized)
   3667 	xmlInitializeCatalog();
   3668 
   3669     if ((pubID == NULL) && (sysID == NULL))
   3670 	return(NULL);
   3671 
   3672     if (xmlDebugCatalogs) {
   3673         if ((pubID != NULL) && (sysID != NULL)) {
   3674             xmlGenericError(xmlGenericErrorContext,
   3675                             "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
   3676         } else if (pubID != NULL) {
   3677             xmlGenericError(xmlGenericErrorContext,
   3678                             "Local Resolve: pubID %s\n", pubID);
   3679         } else {
   3680             xmlGenericError(xmlGenericErrorContext,
   3681                             "Local Resolve: sysID %s\n", sysID);
   3682         }
   3683     }
   3684 
   3685     catal = (xmlCatalogEntryPtr) catalogs;
   3686     if (catal == NULL)
   3687 	return(NULL);
   3688     ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
   3689     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
   3690 	return(ret);
   3691     return(NULL);
   3692 }
   3693 
   3694 /**
   3695  * xmlCatalogLocalResolveURI:
   3696  * @catalogs:  a document's list of catalogs
   3697  * @URI:  the URI
   3698  *
   3699  * Do a complete resolution lookup of an URI using a
   3700  * document's private catalog list
   3701  *
   3702  * Returns the URI of the resource or NULL if not found, it must be freed
   3703  *      by the caller.
   3704  */
   3705 xmlChar *
   3706 xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
   3707     xmlCatalogEntryPtr catal;
   3708     xmlChar *ret;
   3709 
   3710     if (!xmlCatalogInitialized)
   3711 	xmlInitializeCatalog();
   3712 
   3713     if (URI == NULL)
   3714 	return(NULL);
   3715 
   3716     if (xmlDebugCatalogs)
   3717 	xmlGenericError(xmlGenericErrorContext,
   3718 		"Resolve URI %s\n", URI);
   3719 
   3720     catal = (xmlCatalogEntryPtr) catalogs;
   3721     if (catal == NULL)
   3722 	return(NULL);
   3723     ret = xmlCatalogListXMLResolveURI(catal, URI);
   3724     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
   3725 	return(ret);
   3726     return(NULL);
   3727 }
   3728 
   3729 /************************************************************************
   3730  *									*
   3731  *			Deprecated interfaces				*
   3732  *									*
   3733  ************************************************************************/
   3734 /**
   3735  * xmlCatalogGetSystem:
   3736  * @sysID:  the system ID string
   3737  *
   3738  * Try to lookup the catalog reference associated to a system ID
   3739  * DEPRECATED, use xmlCatalogResolveSystem()
   3740  *
   3741  * Returns the resource if found or NULL otherwise.
   3742  */
   3743 const xmlChar *
   3744 xmlCatalogGetSystem(const xmlChar *sysID) {
   3745     xmlChar *ret;
   3746     static xmlChar result[1000];
   3747     static int msg = 0;
   3748 
   3749     if (!xmlCatalogInitialized)
   3750 	xmlInitializeCatalog();
   3751 
   3752     if (msg == 0) {
   3753 	xmlGenericError(xmlGenericErrorContext,
   3754 		"Use of deprecated xmlCatalogGetSystem() call\n");
   3755 	msg++;
   3756     }
   3757 
   3758     if (sysID == NULL)
   3759 	return(NULL);
   3760 
   3761     /*
   3762      * Check first the XML catalogs
   3763      */
   3764     if (xmlDefaultCatalog != NULL) {
   3765 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
   3766 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
   3767 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
   3768 	    result[sizeof(result) - 1] = 0;
   3769 	    return(result);
   3770 	}
   3771     }
   3772 
   3773     if (xmlDefaultCatalog != NULL)
   3774 	return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
   3775     return(NULL);
   3776 }
   3777 
   3778 /**
   3779  * xmlCatalogGetPublic:
   3780  * @pubID:  the public ID string
   3781  *
   3782  * Try to lookup the catalog reference associated to a public ID
   3783  * DEPRECATED, use xmlCatalogResolvePublic()
   3784  *
   3785  * Returns the resource if found or NULL otherwise.
   3786  */
   3787 const xmlChar *
   3788 xmlCatalogGetPublic(const xmlChar *pubID) {
   3789     xmlChar *ret;
   3790     static xmlChar result[1000];
   3791     static int msg = 0;
   3792 
   3793     if (!xmlCatalogInitialized)
   3794 	xmlInitializeCatalog();
   3795 
   3796     if (msg == 0) {
   3797 	xmlGenericError(xmlGenericErrorContext,
   3798 		"Use of deprecated xmlCatalogGetPublic() call\n");
   3799 	msg++;
   3800     }
   3801 
   3802     if (pubID == NULL)
   3803 	return(NULL);
   3804 
   3805     /*
   3806      * Check first the XML catalogs
   3807      */
   3808     if (xmlDefaultCatalog != NULL) {
   3809 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
   3810 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
   3811 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
   3812 	    result[sizeof(result) - 1] = 0;
   3813 	    return(result);
   3814 	}
   3815     }
   3816 
   3817     if (xmlDefaultCatalog != NULL)
   3818 	return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
   3819     return(NULL);
   3820 }
   3821 
   3822 #define bottom_catalog
   3823 #include "elfgcchack.h"
   3824 #endif /* LIBXML_CATALOG_ENABLED */
   3825