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