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