mirror of
				https://gitlab.gnome.org/GNOME/libxml2.git
				synced 2025-10-24 13:33:01 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			3816 lines
		
	
	
		
			97 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3816 lines
		
	
	
		
			97 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * catalog.c: set of generic Catalog related routines
 | |
|  *
 | |
|  * Reference:  SGML Open Technical Resolution TR9401:1997.
 | |
|  *             http://www.jclark.com/sp/catalog.htm
 | |
|  *
 | |
|  *             XML Catalogs Working Draft 06 August 2001
 | |
|  *             http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 | |
|  *
 | |
|  * See Copyright for the status of this software.
 | |
|  *
 | |
|  * Daniel.Veillard@imag.fr
 | |
|  */
 | |
| 
 | |
| #define IN_LIBXML
 | |
| #include "libxml.h"
 | |
| 
 | |
| #ifdef LIBXML_CATALOG_ENABLED
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #ifdef HAVE_SYS_STAT_H
 | |
| #include <sys/stat.h>
 | |
| #endif
 | |
| #ifdef HAVE_UNISTD_H
 | |
| #include <unistd.h>
 | |
| #elif defined (_WIN32)
 | |
| #include <io.h>
 | |
| #endif
 | |
| #ifdef HAVE_FCNTL_H
 | |
| #include <fcntl.h>
 | |
| #endif
 | |
| #include <libxml/xmlmemory.h>
 | |
| #include <libxml/hash.h>
 | |
| #include <libxml/uri.h>
 | |
| #include <libxml/parserInternals.h>
 | |
| #include <libxml/catalog.h>
 | |
| #include <libxml/xmlerror.h>
 | |
| #include <libxml/threads.h>
 | |
| 
 | |
| #include "private/buf.h"
 | |
| #include "private/error.h"
 | |
| 
 | |
| #define MAX_DELEGATE	50
 | |
| #define MAX_CATAL_DEPTH	50
 | |
| 
 | |
| #ifdef _WIN32
 | |
| # define PATH_SEPARATOR ';'
 | |
| #else
 | |
| # define PATH_SEPARATOR ':'
 | |
| #endif
 | |
| 
 | |
| /**
 | |
|  * TODO:
 | |
|  *
 | |
|  * macro to flag unimplemented blocks
 | |
|  * XML_CATALOG_PREFER user env to select between system/public preferred
 | |
|  * option. C.f. Richard Tobin <richard@cogsci.ed.ac.uk>
 | |
|  *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with
 | |
|  *> values "system" and "public".  I have made the default be "system" to
 | |
|  *> match yours.
 | |
|  */
 | |
| #define TODO								\
 | |
|     xmlGenericError(xmlGenericErrorContext,				\
 | |
| 	    "Unimplemented block at %s:%d\n",				\
 | |
|             __FILE__, __LINE__);
 | |
| 
 | |
| #define XML_URN_PUBID "urn:publicid:"
 | |
| #define XML_CATAL_BREAK ((xmlChar *) -1)
 | |
| #ifndef XML_XML_DEFAULT_CATALOG
 | |
| #define XML_XML_DEFAULT_CATALOG "file://" SYSCONFDIR "/xml/catalog"
 | |
| #endif
 | |
| #ifndef XML_SGML_DEFAULT_CATALOG
 | |
| #define XML_SGML_DEFAULT_CATALOG "file://" SYSCONFDIR "/sgml/catalog"
 | |
| #endif
 | |
| 
 | |
| #if defined(_WIN32) && defined(_MSC_VER)
 | |
| #undef XML_XML_DEFAULT_CATALOG
 | |
| static char XML_XML_DEFAULT_CATALOG[256] = "file://" SYSCONFDIR "/xml/catalog";
 | |
| #if !defined(_WINDOWS_)
 | |
| void* __stdcall GetModuleHandleA(const char*);
 | |
| unsigned long __stdcall GetModuleFileNameA(void*, char*, unsigned long);
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| static xmlChar *xmlCatalogNormalizePublic(const xmlChar *pubID);
 | |
| static int xmlExpandCatalog(xmlCatalogPtr catal, const char *filename);
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Types, all private				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| typedef enum {
 | |
|     XML_CATA_REMOVED = -1,
 | |
|     XML_CATA_NONE = 0,
 | |
|     XML_CATA_CATALOG,
 | |
|     XML_CATA_BROKEN_CATALOG,
 | |
|     XML_CATA_NEXT_CATALOG,
 | |
|     XML_CATA_GROUP,
 | |
|     XML_CATA_PUBLIC,
 | |
|     XML_CATA_SYSTEM,
 | |
|     XML_CATA_REWRITE_SYSTEM,
 | |
|     XML_CATA_DELEGATE_PUBLIC,
 | |
|     XML_CATA_DELEGATE_SYSTEM,
 | |
|     XML_CATA_URI,
 | |
|     XML_CATA_REWRITE_URI,
 | |
|     XML_CATA_DELEGATE_URI,
 | |
|     SGML_CATA_SYSTEM,
 | |
|     SGML_CATA_PUBLIC,
 | |
|     SGML_CATA_ENTITY,
 | |
|     SGML_CATA_PENTITY,
 | |
|     SGML_CATA_DOCTYPE,
 | |
|     SGML_CATA_LINKTYPE,
 | |
|     SGML_CATA_NOTATION,
 | |
|     SGML_CATA_DELEGATE,
 | |
|     SGML_CATA_BASE,
 | |
|     SGML_CATA_CATALOG,
 | |
|     SGML_CATA_DOCUMENT,
 | |
|     SGML_CATA_SGMLDECL
 | |
| } xmlCatalogEntryType;
 | |
| 
 | |
| typedef struct _xmlCatalogEntry xmlCatalogEntry;
 | |
| typedef xmlCatalogEntry *xmlCatalogEntryPtr;
 | |
| struct _xmlCatalogEntry {
 | |
|     struct _xmlCatalogEntry *next;
 | |
|     struct _xmlCatalogEntry *parent;
 | |
|     struct _xmlCatalogEntry *children;
 | |
|     xmlCatalogEntryType type;
 | |
|     xmlChar *name;
 | |
|     xmlChar *value;
 | |
|     xmlChar *URL;  /* The expanded URL using the base */
 | |
|     xmlCatalogPrefer prefer;
 | |
|     int dealloc;
 | |
|     int depth;
 | |
|     struct _xmlCatalogEntry *group;
 | |
| };
 | |
| 
 | |
| typedef enum {
 | |
|     XML_XML_CATALOG_TYPE = 1,
 | |
|     XML_SGML_CATALOG_TYPE
 | |
| } xmlCatalogType;
 | |
| 
 | |
| #define XML_MAX_SGML_CATA_DEPTH 10
 | |
| struct _xmlCatalog {
 | |
|     xmlCatalogType type;	/* either XML or SGML */
 | |
| 
 | |
|     /*
 | |
|      * SGML Catalogs are stored as a simple hash table of catalog entries
 | |
|      * Catalog stack to check against overflows when building the
 | |
|      * SGML catalog
 | |
|      */
 | |
|     char *catalTab[XML_MAX_SGML_CATA_DEPTH];	/* stack of catals */
 | |
|     int          catalNr;	/* Number of current catal streams */
 | |
|     int          catalMax;	/* Max number of catal streams */
 | |
|     xmlHashTablePtr sgml;
 | |
| 
 | |
|     /*
 | |
|      * XML Catalogs are stored as a tree of Catalog entries
 | |
|      */
 | |
|     xmlCatalogPrefer prefer;
 | |
|     xmlCatalogEntryPtr xml;
 | |
| };
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Global variables				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /*
 | |
|  * Those are preferences
 | |
|  */
 | |
| static int xmlDebugCatalogs = 0;   /* used for debugging */
 | |
| static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
 | |
| static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
 | |
| 
 | |
| /*
 | |
|  * Hash table containing all the trees of XML catalogs parsed by
 | |
|  * the application.
 | |
|  */
 | |
| static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
 | |
| 
 | |
| /*
 | |
|  * The default catalog in use by the application
 | |
|  */
 | |
| static xmlCatalogPtr xmlDefaultCatalog = NULL;
 | |
| 
 | |
| /*
 | |
|  * A mutex for modifying the shared global catalog(s)
 | |
|  * xmlDefaultCatalog tree.
 | |
|  * It also protects xmlCatalogXMLFiles
 | |
|  * The core of this readers/writer scheme is in xmlFetchXMLCatalogFile()
 | |
|  */
 | |
| static xmlRMutexPtr xmlCatalogMutex = NULL;
 | |
| 
 | |
| /*
 | |
|  * Whether the catalog support was initialized.
 | |
|  */
 | |
| static int xmlCatalogInitialized = 0;
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Catalog error handlers				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogErrMemory:
 | |
|  * @extra:  extra information
 | |
|  *
 | |
|  * Handle an out of memory condition
 | |
|  */
 | |
| static void
 | |
| xmlCatalogErrMemory(const char *extra)
 | |
| {
 | |
|     __xmlRaiseError(NULL, NULL, NULL, NULL, NULL, XML_FROM_CATALOG,
 | |
|                     XML_ERR_NO_MEMORY, XML_ERR_ERROR, NULL, 0,
 | |
| 		    extra, NULL, NULL, 0, 0,
 | |
| 		    "Memory allocation failed : %s\n", extra);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogErr:
 | |
|  * @catal: the Catalog entry
 | |
|  * @node: the context node
 | |
|  * @msg:  the error message
 | |
|  * @extra:  extra information
 | |
|  *
 | |
|  * Handle a catalog error
 | |
|  */
 | |
| static void LIBXML_ATTR_FORMAT(4,0)
 | |
| xmlCatalogErr(xmlCatalogEntryPtr catal, xmlNodePtr node, int error,
 | |
|                const char *msg, const xmlChar *str1, const xmlChar *str2,
 | |
| 	       const xmlChar *str3)
 | |
| {
 | |
|     __xmlRaiseError(NULL, NULL, NULL, catal, node, XML_FROM_CATALOG,
 | |
|                     error, XML_ERR_ERROR, NULL, 0,
 | |
| 		    (const char *) str1, (const char *) str2,
 | |
| 		    (const char *) str3, 0, 0,
 | |
| 		    msg, str1, str2, str3);
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Allocation and Freeing				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlNewCatalogEntry:
 | |
|  * @type:  type of entry
 | |
|  * @name:  name of the entry
 | |
|  * @value:  value of the entry
 | |
|  * @prefer:  the PUBLIC vs. SYSTEM current preference value
 | |
|  * @group:  for members of a group, the group entry
 | |
|  *
 | |
|  * create a new Catalog entry, this type is shared both by XML and
 | |
|  * SGML catalogs, but the acceptable types values differs.
 | |
|  *
 | |
|  * Returns the xmlCatalogEntryPtr or NULL in case of error
 | |
|  */
 | |
| static xmlCatalogEntryPtr
 | |
| xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
 | |
| 	   const xmlChar *value, const xmlChar *URL, xmlCatalogPrefer prefer,
 | |
| 	   xmlCatalogEntryPtr group) {
 | |
|     xmlCatalogEntryPtr ret;
 | |
|     xmlChar *normid = NULL;
 | |
| 
 | |
|     ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
 | |
|     if (ret == NULL) {
 | |
|         xmlCatalogErrMemory("allocating catalog entry");
 | |
| 	return(NULL);
 | |
|     }
 | |
|     ret->next = NULL;
 | |
|     ret->parent = NULL;
 | |
|     ret->children = NULL;
 | |
|     ret->type = type;
 | |
|     if (type == XML_CATA_PUBLIC || type == XML_CATA_DELEGATE_PUBLIC) {
 | |
|         normid = xmlCatalogNormalizePublic(name);
 | |
|         if (normid != NULL)
 | |
|             name = (*normid != 0 ? normid : NULL);
 | |
|     }
 | |
|     if (name != NULL)
 | |
| 	ret->name = xmlStrdup(name);
 | |
|     else
 | |
| 	ret->name = NULL;
 | |
|     if (normid != NULL)
 | |
|         xmlFree(normid);
 | |
|     if (value != NULL)
 | |
| 	ret->value = xmlStrdup(value);
 | |
|     else
 | |
| 	ret->value = NULL;
 | |
|     if (URL == NULL)
 | |
| 	URL = value;
 | |
|     if (URL != NULL)
 | |
| 	ret->URL = xmlStrdup(URL);
 | |
|     else
 | |
| 	ret->URL = NULL;
 | |
|     ret->prefer = prefer;
 | |
|     ret->dealloc = 0;
 | |
|     ret->depth = 0;
 | |
|     ret->group = group;
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| static void
 | |
| xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
 | |
| 
 | |
| /**
 | |
|  * xmlFreeCatalogEntry:
 | |
|  * @payload:  a Catalog entry
 | |
|  *
 | |
|  * Free the memory allocated to a Catalog entry
 | |
|  */
 | |
| static void
 | |
| xmlFreeCatalogEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
 | |
|     xmlCatalogEntryPtr ret = (xmlCatalogEntryPtr) payload;
 | |
|     if (ret == NULL)
 | |
| 	return;
 | |
|     /*
 | |
|      * Entries stored in the file hash must be deallocated
 | |
|      * only by the file hash cleaner !
 | |
|      */
 | |
|     if (ret->dealloc == 1)
 | |
| 	return;
 | |
| 
 | |
|     if (xmlDebugCatalogs) {
 | |
| 	if (ret->name != NULL)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Free catalog entry %s\n", ret->name);
 | |
| 	else if (ret->value != NULL)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Free catalog entry %s\n", ret->value);
 | |
| 	else
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Free catalog entry\n");
 | |
|     }
 | |
| 
 | |
|     if (ret->name != NULL)
 | |
| 	xmlFree(ret->name);
 | |
|     if (ret->value != NULL)
 | |
| 	xmlFree(ret->value);
 | |
|     if (ret->URL != NULL)
 | |
| 	xmlFree(ret->URL);
 | |
|     xmlFree(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFreeCatalogEntryList:
 | |
|  * @ret:  a Catalog entry list
 | |
|  *
 | |
|  * Free the memory allocated to a full chained list of Catalog entries
 | |
|  */
 | |
| static void
 | |
| xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
 | |
|     xmlCatalogEntryPtr next;
 | |
| 
 | |
|     while (ret != NULL) {
 | |
| 	next = ret->next;
 | |
| 	xmlFreeCatalogEntry(ret, NULL);
 | |
| 	ret = next;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFreeCatalogHashEntryList:
 | |
|  * @payload:  a Catalog entry list
 | |
|  *
 | |
|  * Free the memory allocated to list of Catalog entries from the
 | |
|  * catalog file hash.
 | |
|  */
 | |
| static void
 | |
| xmlFreeCatalogHashEntryList(void *payload,
 | |
|                             const xmlChar *name ATTRIBUTE_UNUSED) {
 | |
|     xmlCatalogEntryPtr catal = (xmlCatalogEntryPtr) payload;
 | |
|     xmlCatalogEntryPtr children, next;
 | |
| 
 | |
|     if (catal == NULL)
 | |
| 	return;
 | |
| 
 | |
|     children = catal->children;
 | |
|     while (children != NULL) {
 | |
| 	next = children->next;
 | |
| 	children->dealloc = 0;
 | |
| 	children->children = NULL;
 | |
| 	xmlFreeCatalogEntry(children, NULL);
 | |
| 	children = next;
 | |
|     }
 | |
|     catal->dealloc = 0;
 | |
|     xmlFreeCatalogEntry(catal, NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCreateNewCatalog:
 | |
|  * @type:  type of catalog
 | |
|  * @prefer:  the PUBLIC vs. SYSTEM current preference value
 | |
|  *
 | |
|  * create a new Catalog, this type is shared both by XML and
 | |
|  * SGML catalogs, but the acceptable types values differs.
 | |
|  *
 | |
|  * Returns the xmlCatalogPtr or NULL in case of error
 | |
|  */
 | |
| static xmlCatalogPtr
 | |
| xmlCreateNewCatalog(xmlCatalogType type, xmlCatalogPrefer prefer) {
 | |
|     xmlCatalogPtr ret;
 | |
| 
 | |
|     ret = (xmlCatalogPtr) xmlMalloc(sizeof(xmlCatalog));
 | |
|     if (ret == NULL) {
 | |
|         xmlCatalogErrMemory("allocating catalog");
 | |
| 	return(NULL);
 | |
|     }
 | |
|     memset(ret, 0, sizeof(xmlCatalog));
 | |
|     ret->type = type;
 | |
|     ret->catalNr = 0;
 | |
|     ret->catalMax = XML_MAX_SGML_CATA_DEPTH;
 | |
|     ret->prefer = prefer;
 | |
|     if (ret->type == XML_SGML_CATALOG_TYPE)
 | |
| 	ret->sgml = xmlHashCreate(10);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFreeCatalog:
 | |
|  * @catal:  a Catalog
 | |
|  *
 | |
|  * Free the memory allocated to a Catalog
 | |
|  */
 | |
| void
 | |
| xmlFreeCatalog(xmlCatalogPtr catal) {
 | |
|     if (catal == NULL)
 | |
| 	return;
 | |
|     if (catal->xml != NULL)
 | |
| 	xmlFreeCatalogEntryList(catal->xml);
 | |
|     if (catal->sgml != NULL)
 | |
| 	xmlHashFree(catal->sgml, xmlFreeCatalogEntry);
 | |
|     xmlFree(catal);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Serializing Catalogs				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| #ifdef LIBXML_OUTPUT_ENABLED
 | |
| /**
 | |
|  * xmlCatalogDumpEntry:
 | |
|  * @entry:  the catalog entry
 | |
|  * @out:  the file.
 | |
|  *
 | |
|  * Serialize an SGML Catalog entry
 | |
|  */
 | |
| static void
 | |
| xmlCatalogDumpEntry(void *payload, void *data,
 | |
|                     const xmlChar *name ATTRIBUTE_UNUSED) {
 | |
|     xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
 | |
|     FILE *out = (FILE *) data;
 | |
|     if ((entry == NULL) || (out == NULL))
 | |
| 	return;
 | |
|     switch (entry->type) {
 | |
| 	case SGML_CATA_ENTITY:
 | |
| 	    fprintf(out, "ENTITY "); break;
 | |
| 	case SGML_CATA_PENTITY:
 | |
| 	    fprintf(out, "ENTITY %%"); break;
 | |
| 	case SGML_CATA_DOCTYPE:
 | |
| 	    fprintf(out, "DOCTYPE "); break;
 | |
| 	case SGML_CATA_LINKTYPE:
 | |
| 	    fprintf(out, "LINKTYPE "); break;
 | |
| 	case SGML_CATA_NOTATION:
 | |
| 	    fprintf(out, "NOTATION "); break;
 | |
| 	case SGML_CATA_PUBLIC:
 | |
| 	    fprintf(out, "PUBLIC "); break;
 | |
| 	case SGML_CATA_SYSTEM:
 | |
| 	    fprintf(out, "SYSTEM "); break;
 | |
| 	case SGML_CATA_DELEGATE:
 | |
| 	    fprintf(out, "DELEGATE "); break;
 | |
| 	case SGML_CATA_BASE:
 | |
| 	    fprintf(out, "BASE "); break;
 | |
| 	case SGML_CATA_CATALOG:
 | |
| 	    fprintf(out, "CATALOG "); break;
 | |
| 	case SGML_CATA_DOCUMENT:
 | |
| 	    fprintf(out, "DOCUMENT "); break;
 | |
| 	case SGML_CATA_SGMLDECL:
 | |
| 	    fprintf(out, "SGMLDECL "); break;
 | |
| 	default:
 | |
| 	    return;
 | |
|     }
 | |
|     switch (entry->type) {
 | |
| 	case SGML_CATA_ENTITY:
 | |
| 	case SGML_CATA_PENTITY:
 | |
| 	case SGML_CATA_DOCTYPE:
 | |
| 	case SGML_CATA_LINKTYPE:
 | |
| 	case SGML_CATA_NOTATION:
 | |
| 	    fprintf(out, "%s", (const char *) entry->name); break;
 | |
| 	case SGML_CATA_PUBLIC:
 | |
| 	case SGML_CATA_SYSTEM:
 | |
| 	case SGML_CATA_SGMLDECL:
 | |
| 	case SGML_CATA_DOCUMENT:
 | |
| 	case SGML_CATA_CATALOG:
 | |
| 	case SGML_CATA_BASE:
 | |
| 	case SGML_CATA_DELEGATE:
 | |
| 	    fprintf(out, "\"%s\"", entry->name); break;
 | |
| 	default:
 | |
| 	    break;
 | |
|     }
 | |
|     switch (entry->type) {
 | |
| 	case SGML_CATA_ENTITY:
 | |
| 	case SGML_CATA_PENTITY:
 | |
| 	case SGML_CATA_DOCTYPE:
 | |
| 	case SGML_CATA_LINKTYPE:
 | |
| 	case SGML_CATA_NOTATION:
 | |
| 	case SGML_CATA_PUBLIC:
 | |
| 	case SGML_CATA_SYSTEM:
 | |
| 	case SGML_CATA_DELEGATE:
 | |
| 	    fprintf(out, " \"%s\"", entry->value); break;
 | |
| 	default:
 | |
| 	    break;
 | |
|     }
 | |
|     fprintf(out, "\n");
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlDumpXMLCatalogNode:
 | |
|  * @catal:  top catalog entry
 | |
|  * @catalog: pointer to the xml tree
 | |
|  * @doc: the containing document
 | |
|  * @ns: the current namespace
 | |
|  * @cgroup: group node for group members
 | |
|  *
 | |
|  * Serializes a Catalog entry, called by xmlDumpXMLCatalog and recursively
 | |
|  * for group entries
 | |
|  */
 | |
| static void xmlDumpXMLCatalogNode(xmlCatalogEntryPtr catal, xmlNodePtr catalog,
 | |
| 		    xmlDocPtr doc, xmlNsPtr ns, xmlCatalogEntryPtr cgroup) {
 | |
|     xmlNodePtr node;
 | |
|     xmlCatalogEntryPtr cur;
 | |
|     /*
 | |
|      * add all the catalog entries
 | |
|      */
 | |
|     cur = catal;
 | |
|     while (cur != NULL) {
 | |
|         if (cur->group == cgroup) {
 | |
| 	    switch (cur->type) {
 | |
| 	        case XML_CATA_REMOVED:
 | |
| 		    break;
 | |
| 	        case XML_CATA_BROKEN_CATALOG:
 | |
| 	        case XML_CATA_CATALOG:
 | |
| 		    if (cur == catal) {
 | |
| 			cur = cur->children;
 | |
| 		        continue;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case XML_CATA_NEXT_CATALOG:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "nextCatalog", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
|                     break;
 | |
| 		case XML_CATA_NONE:
 | |
| 		    break;
 | |
| 		case XML_CATA_GROUP:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "group", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "id", cur->name);
 | |
| 		    if (cur->value != NULL) {
 | |
| 		        xmlNsPtr xns;
 | |
| 			xns = xmlSearchNsByHref(doc, node, XML_XML_NAMESPACE);
 | |
| 			if (xns != NULL)
 | |
| 			    xmlSetNsProp(node, xns, BAD_CAST "base",
 | |
| 					 cur->value);
 | |
| 		    }
 | |
| 		    switch (cur->prefer) {
 | |
| 			case XML_CATA_PREFER_NONE:
 | |
| 		            break;
 | |
| 			case XML_CATA_PREFER_PUBLIC:
 | |
| 		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "public");
 | |
| 			    break;
 | |
| 			case XML_CATA_PREFER_SYSTEM:
 | |
| 		            xmlSetProp(node, BAD_CAST "prefer", BAD_CAST "system");
 | |
| 			    break;
 | |
| 		    }
 | |
| 		    xmlDumpXMLCatalogNode(cur->next, node, doc, ns, cur);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 	            break;
 | |
| 		case XML_CATA_PUBLIC:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "public", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "publicId", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "uri", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_SYSTEM:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "system", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "systemId", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "uri", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_REWRITE_SYSTEM:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteSystem", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_DELEGATE_PUBLIC:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegatePublic", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "publicIdStartString", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_DELEGATE_SYSTEM:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateSystem", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "systemIdStartString", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_URI:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "uri", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "name", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "uri", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_REWRITE_URI:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "rewriteURI", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "rewritePrefix", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case XML_CATA_DELEGATE_URI:
 | |
| 		    node = xmlNewDocNode(doc, ns, BAD_CAST "delegateURI", NULL);
 | |
| 		    xmlSetProp(node, BAD_CAST "uriStartString", cur->name);
 | |
| 		    xmlSetProp(node, BAD_CAST "catalog", cur->value);
 | |
| 		    xmlAddChild(catalog, node);
 | |
| 		    break;
 | |
| 		case SGML_CATA_SYSTEM:
 | |
| 		case SGML_CATA_PUBLIC:
 | |
| 		case SGML_CATA_ENTITY:
 | |
| 		case SGML_CATA_PENTITY:
 | |
| 		case SGML_CATA_DOCTYPE:
 | |
| 		case SGML_CATA_LINKTYPE:
 | |
| 		case SGML_CATA_NOTATION:
 | |
| 		case SGML_CATA_DELEGATE:
 | |
| 		case SGML_CATA_BASE:
 | |
| 		case SGML_CATA_CATALOG:
 | |
| 		case SGML_CATA_DOCUMENT:
 | |
| 		case SGML_CATA_SGMLDECL:
 | |
| 		    break;
 | |
| 	    }
 | |
|         }
 | |
| 	cur = cur->next;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int
 | |
| xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) {
 | |
|     int ret;
 | |
|     xmlDocPtr doc;
 | |
|     xmlNsPtr ns;
 | |
|     xmlDtdPtr dtd;
 | |
|     xmlNodePtr catalog;
 | |
|     xmlOutputBufferPtr buf;
 | |
| 
 | |
|     /*
 | |
|      * Rebuild a catalog
 | |
|      */
 | |
|     doc = xmlNewDoc(NULL);
 | |
|     if (doc == NULL)
 | |
| 	return(-1);
 | |
|     dtd = xmlNewDtd(doc, BAD_CAST "catalog",
 | |
| 	       BAD_CAST "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN",
 | |
| BAD_CAST "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd");
 | |
| 
 | |
|     xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);
 | |
| 
 | |
|     ns = xmlNewNs(NULL, XML_CATALOGS_NAMESPACE, NULL);
 | |
|     if (ns == NULL) {
 | |
| 	xmlFreeDoc(doc);
 | |
| 	return(-1);
 | |
|     }
 | |
|     catalog = xmlNewDocNode(doc, ns, BAD_CAST "catalog", NULL);
 | |
|     if (catalog == NULL) {
 | |
| 	xmlFreeNs(ns);
 | |
| 	xmlFreeDoc(doc);
 | |
| 	return(-1);
 | |
|     }
 | |
|     catalog->nsDef = ns;
 | |
|     xmlAddChild((xmlNodePtr) doc, catalog);
 | |
| 
 | |
|     xmlDumpXMLCatalogNode(catal, catalog, doc, ns, NULL);
 | |
| 
 | |
|     /*
 | |
|      * reserialize it
 | |
|      */
 | |
|     buf = xmlOutputBufferCreateFile(out, NULL);
 | |
|     if (buf == NULL) {
 | |
| 	xmlFreeDoc(doc);
 | |
| 	return(-1);
 | |
|     }
 | |
|     ret = xmlSaveFormatFileTo(buf, doc, NULL, 1);
 | |
| 
 | |
|     /*
 | |
|      * Free it
 | |
|      */
 | |
|     xmlFreeDoc(doc);
 | |
| 
 | |
|     return(ret);
 | |
| }
 | |
| #endif /* LIBXML_OUTPUT_ENABLED */
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Converting SGML Catalogs to XML			*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogConvertEntry:
 | |
|  * @entry:  the entry
 | |
|  * @catal:  pointer to the catalog being converted
 | |
|  *
 | |
|  * Convert one entry from the catalog
 | |
|  */
 | |
| static void
 | |
| xmlCatalogConvertEntry(void *payload, void *data,
 | |
|                        const xmlChar *name ATTRIBUTE_UNUSED) {
 | |
|     xmlCatalogEntryPtr entry = (xmlCatalogEntryPtr) payload;
 | |
|     xmlCatalogPtr catal = (xmlCatalogPtr) data;
 | |
|     if ((entry == NULL) || (catal == NULL) || (catal->sgml == NULL) ||
 | |
| 	(catal->xml == NULL))
 | |
| 	return;
 | |
|     switch (entry->type) {
 | |
| 	case SGML_CATA_ENTITY:
 | |
| 	    entry->type = XML_CATA_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_PENTITY:
 | |
| 	    entry->type = XML_CATA_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_DOCTYPE:
 | |
| 	    entry->type = XML_CATA_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_LINKTYPE:
 | |
| 	    entry->type = XML_CATA_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_NOTATION:
 | |
| 	    entry->type = XML_CATA_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_PUBLIC:
 | |
| 	    entry->type = XML_CATA_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_SYSTEM:
 | |
| 	    entry->type = XML_CATA_SYSTEM;
 | |
| 	    break;
 | |
| 	case SGML_CATA_DELEGATE:
 | |
| 	    entry->type = XML_CATA_DELEGATE_PUBLIC;
 | |
| 	    break;
 | |
| 	case SGML_CATA_CATALOG:
 | |
| 	    entry->type = XML_CATA_CATALOG;
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    xmlHashRemoveEntry(catal->sgml, entry->name, xmlFreeCatalogEntry);
 | |
| 	    return;
 | |
|     }
 | |
|     /*
 | |
|      * Conversion successful, remove from the SGML catalog
 | |
|      * and add it to the default XML one
 | |
|      */
 | |
|     xmlHashRemoveEntry(catal->sgml, entry->name, NULL);
 | |
|     entry->parent = catal->xml;
 | |
|     entry->next = NULL;
 | |
|     if (catal->xml->children == NULL)
 | |
| 	catal->xml->children = entry;
 | |
|     else {
 | |
| 	xmlCatalogEntryPtr prev;
 | |
| 
 | |
| 	prev = catal->xml->children;
 | |
| 	while (prev->next != NULL)
 | |
| 	    prev = prev->next;
 | |
| 	prev->next = entry;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlConvertSGMLCatalog:
 | |
|  * @catal: the catalog
 | |
|  *
 | |
|  * Convert all the SGML catalog entries as XML ones
 | |
|  *
 | |
|  * Returns the number of entries converted if successful, -1 otherwise
 | |
|  */
 | |
| int
 | |
| xmlConvertSGMLCatalog(xmlCatalogPtr catal) {
 | |
| 
 | |
|     if ((catal == NULL) || (catal->type != XML_SGML_CATALOG_TYPE))
 | |
| 	return(-1);
 | |
| 
 | |
|     if (xmlDebugCatalogs) {
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Converting SGML catalog to XML\n");
 | |
|     }
 | |
|     xmlHashScan(catal->sgml, xmlCatalogConvertEntry, &catal);
 | |
|     return(0);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Helper function					*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogUnWrapURN:
 | |
|  * @urn:  an "urn:publicid:" to unwrap
 | |
|  *
 | |
|  * Expand the URN into the equivalent Public Identifier
 | |
|  *
 | |
|  * Returns the new identifier or NULL, the string must be deallocated
 | |
|  *         by the caller.
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlCatalogUnWrapURN(const xmlChar *urn) {
 | |
|     xmlChar result[2000];
 | |
|     unsigned int i = 0;
 | |
| 
 | |
|     if (xmlStrncmp(urn, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1))
 | |
| 	return(NULL);
 | |
|     urn += sizeof(XML_URN_PUBID) - 1;
 | |
| 
 | |
|     while (*urn != 0) {
 | |
| 	if (i > sizeof(result) - 4)
 | |
| 	    break;
 | |
| 	if (*urn == '+') {
 | |
| 	    result[i++] = ' ';
 | |
| 	    urn++;
 | |
| 	} else if (*urn == ':') {
 | |
| 	    result[i++] = '/';
 | |
| 	    result[i++] = '/';
 | |
| 	    urn++;
 | |
| 	} else if (*urn == ';') {
 | |
| 	    result[i++] = ':';
 | |
| 	    result[i++] = ':';
 | |
| 	    urn++;
 | |
| 	} else if (*urn == '%') {
 | |
| 	    if ((urn[1] == '2') && (urn[2] == 'B'))
 | |
| 		result[i++] = '+';
 | |
| 	    else if ((urn[1] == '3') && (urn[2] == 'A'))
 | |
| 		result[i++] = ':';
 | |
| 	    else if ((urn[1] == '2') && (urn[2] == 'F'))
 | |
| 		result[i++] = '/';
 | |
| 	    else if ((urn[1] == '3') && (urn[2] == 'B'))
 | |
| 		result[i++] = ';';
 | |
| 	    else if ((urn[1] == '2') && (urn[2] == '7'))
 | |
| 		result[i++] = '\'';
 | |
| 	    else if ((urn[1] == '3') && (urn[2] == 'F'))
 | |
| 		result[i++] = '?';
 | |
| 	    else if ((urn[1] == '2') && (urn[2] == '3'))
 | |
| 		result[i++] = '#';
 | |
| 	    else if ((urn[1] == '2') && (urn[2] == '5'))
 | |
| 		result[i++] = '%';
 | |
| 	    else {
 | |
| 		result[i++] = *urn;
 | |
| 		urn++;
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    urn += 3;
 | |
| 	} else {
 | |
| 	    result[i++] = *urn;
 | |
| 	    urn++;
 | |
| 	}
 | |
|     }
 | |
|     result[i] = 0;
 | |
| 
 | |
|     return(xmlStrdup(result));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseCatalogFile:
 | |
|  * @filename:  the filename
 | |
|  *
 | |
|  * parse an XML file and build a tree. It's like xmlParseFile()
 | |
|  * except it bypass all catalog lookups.
 | |
|  *
 | |
|  * Returns the resulting document tree or NULL in case of error
 | |
|  */
 | |
| 
 | |
| xmlDocPtr
 | |
| xmlParseCatalogFile(const char *filename) {
 | |
|     xmlDocPtr ret;
 | |
|     xmlParserCtxtPtr ctxt;
 | |
|     char *directory = NULL;
 | |
|     xmlParserInputPtr inputStream;
 | |
|     xmlParserInputBufferPtr buf;
 | |
| 
 | |
|     ctxt = xmlNewParserCtxt();
 | |
|     if (ctxt == NULL) {
 | |
|         xmlCatalogErrMemory("allocating parser context");
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE);
 | |
|     if (buf == NULL) {
 | |
| 	xmlFreeParserCtxt(ctxt);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     inputStream = xmlNewInputStream(ctxt);
 | |
|     if (inputStream == NULL) {
 | |
| 	xmlFreeParserInputBuffer(buf);
 | |
| 	xmlFreeParserCtxt(ctxt);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename);
 | |
|     inputStream->buf = buf;
 | |
|     xmlBufResetInput(buf->buffer, inputStream);
 | |
| 
 | |
|     inputPush(ctxt, inputStream);
 | |
|     if (ctxt->directory == NULL)
 | |
|         directory = xmlParserGetDirectory(filename);
 | |
|     if ((ctxt->directory == NULL) && (directory != NULL))
 | |
|         ctxt->directory = directory;
 | |
|     ctxt->valid = 0;
 | |
|     ctxt->validate = 0;
 | |
|     ctxt->loadsubset = 0;
 | |
|     ctxt->pedantic = 0;
 | |
|     ctxt->dictNames = 1;
 | |
| 
 | |
|     xmlParseDocument(ctxt);
 | |
| 
 | |
|     if (ctxt->wellFormed)
 | |
| 	ret = ctxt->myDoc;
 | |
|     else {
 | |
|         ret = NULL;
 | |
|         xmlFreeDoc(ctxt->myDoc);
 | |
|         ctxt->myDoc = NULL;
 | |
|     }
 | |
|     xmlFreeParserCtxt(ctxt);
 | |
| 
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlLoadFileContent:
 | |
|  * @filename:  a file path
 | |
|  *
 | |
|  * Load a file content into memory.
 | |
|  *
 | |
|  * Returns a pointer to the 0 terminated string or NULL in case of error
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlLoadFileContent(const char *filename)
 | |
| {
 | |
| #ifdef HAVE_STAT
 | |
|     int fd;
 | |
| #else
 | |
|     FILE *fd;
 | |
| #endif
 | |
|     int len;
 | |
|     long size;
 | |
| 
 | |
| #ifdef HAVE_STAT
 | |
|     struct stat info;
 | |
| #endif
 | |
|     xmlChar *content;
 | |
| 
 | |
|     if (filename == NULL)
 | |
|         return (NULL);
 | |
| 
 | |
| #ifdef HAVE_STAT
 | |
|     if (stat(filename, &info) < 0)
 | |
|         return (NULL);
 | |
| #endif
 | |
| 
 | |
| #ifdef HAVE_STAT
 | |
|     if ((fd = open(filename, O_RDONLY)) < 0)
 | |
| #else
 | |
|     if ((fd = fopen(filename, "rb")) == NULL)
 | |
| #endif
 | |
|     {
 | |
|         return (NULL);
 | |
|     }
 | |
| #ifdef HAVE_STAT
 | |
|     size = info.st_size;
 | |
| #else
 | |
|     if (fseek(fd, 0, SEEK_END) || (size = ftell(fd)) == EOF || fseek(fd, 0, SEEK_SET)) {        /* File operations denied? ok, just close and return failure */
 | |
|         fclose(fd);
 | |
|         return (NULL);
 | |
|     }
 | |
| #endif
 | |
|     content = (xmlChar*)xmlMallocAtomic(size + 10);
 | |
|     if (content == NULL) {
 | |
|         xmlCatalogErrMemory("allocating catalog data");
 | |
| #ifdef HAVE_STAT
 | |
| 	close(fd);
 | |
| #else
 | |
| 	fclose(fd);
 | |
| #endif
 | |
|         return (NULL);
 | |
|     }
 | |
| #ifdef HAVE_STAT
 | |
|     len = read(fd, content, size);
 | |
|     close(fd);
 | |
| #else
 | |
|     len = fread(content, 1, size, fd);
 | |
|     fclose(fd);
 | |
| #endif
 | |
|     if (len < 0) {
 | |
|         xmlFree(content);
 | |
|         return (NULL);
 | |
|     }
 | |
|     content[len] = 0;
 | |
| 
 | |
|     return(content);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogNormalizePublic:
 | |
|  * @pubID:  the public ID string
 | |
|  *
 | |
|  *  Normalizes the Public Identifier
 | |
|  *
 | |
|  * Implements 6.2. Public Identifier Normalization
 | |
|  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 | |
|  *
 | |
|  * Returns the new string or NULL, the string must be deallocated
 | |
|  *         by the caller.
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlCatalogNormalizePublic(const xmlChar *pubID)
 | |
| {
 | |
|     int ok = 1;
 | |
|     int white;
 | |
|     const xmlChar *p;
 | |
|     xmlChar *ret;
 | |
|     xmlChar *q;
 | |
| 
 | |
|     if (pubID == NULL)
 | |
|         return(NULL);
 | |
| 
 | |
|     white = 1;
 | |
|     for (p = pubID;*p != 0 && ok;p++) {
 | |
|         if (!xmlIsBlank_ch(*p))
 | |
|             white = 0;
 | |
|         else if (*p == 0x20 && !white)
 | |
|             white = 1;
 | |
|         else
 | |
|             ok = 0;
 | |
|     }
 | |
|     if (ok && !white)	/* is normalized */
 | |
|         return(NULL);
 | |
| 
 | |
|     ret = xmlStrdup(pubID);
 | |
|     q = ret;
 | |
|     white = 0;
 | |
|     for (p = pubID;*p != 0;p++) {
 | |
|         if (xmlIsBlank_ch(*p)) {
 | |
|             if (q != ret)
 | |
|                 white = 1;
 | |
|         } else {
 | |
|             if (white) {
 | |
|                 *(q++) = 0x20;
 | |
|                 white = 0;
 | |
|             }
 | |
|             *(q++) = *p;
 | |
|         }
 | |
|     }
 | |
|     *q = 0;
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			The XML Catalog parser				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| static xmlCatalogEntryPtr
 | |
| xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
 | |
| static void
 | |
| xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
 | |
| 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup);
 | |
| static xmlChar *
 | |
| xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
 | |
| 	              const xmlChar *sysID);
 | |
| static xmlChar *
 | |
| xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI);
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * xmlGetXMLCatalogEntryType:
 | |
|  * @name:  the name
 | |
|  *
 | |
|  * lookup the internal type associated to an XML catalog entry name
 | |
|  *
 | |
|  * Returns the type associated with that name
 | |
|  */
 | |
| static xmlCatalogEntryType
 | |
| xmlGetXMLCatalogEntryType(const xmlChar *name) {
 | |
|     xmlCatalogEntryType type = XML_CATA_NONE;
 | |
|     if (xmlStrEqual(name, (const xmlChar *) "system"))
 | |
| 	type = XML_CATA_SYSTEM;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "public"))
 | |
| 	type = XML_CATA_PUBLIC;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "rewriteSystem"))
 | |
| 	type = XML_CATA_REWRITE_SYSTEM;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "delegatePublic"))
 | |
| 	type = XML_CATA_DELEGATE_PUBLIC;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "delegateSystem"))
 | |
| 	type = XML_CATA_DELEGATE_SYSTEM;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "uri"))
 | |
| 	type = XML_CATA_URI;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "rewriteURI"))
 | |
| 	type = XML_CATA_REWRITE_URI;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "delegateURI"))
 | |
| 	type = XML_CATA_DELEGATE_URI;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "nextCatalog"))
 | |
| 	type = XML_CATA_NEXT_CATALOG;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "catalog"))
 | |
| 	type = XML_CATA_CATALOG;
 | |
|     return(type);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseXMLCatalogOneNode:
 | |
|  * @cur:  the XML node
 | |
|  * @type:  the type of Catalog entry
 | |
|  * @name:  the name of the node
 | |
|  * @attrName:  the attribute holding the value
 | |
|  * @uriAttrName:  the attribute holding the URI-Reference
 | |
|  * @prefer:  the PUBLIC vs. SYSTEM current preference value
 | |
|  * @cgroup:  the group which includes this node
 | |
|  *
 | |
|  * Finishes the examination of an XML tree node of a catalog and build
 | |
|  * a Catalog entry from it.
 | |
|  *
 | |
|  * Returns the new Catalog entry node or NULL in case of error.
 | |
|  */
 | |
| static xmlCatalogEntryPtr
 | |
| xmlParseXMLCatalogOneNode(xmlNodePtr cur, xmlCatalogEntryType type,
 | |
| 			  const xmlChar *name, const xmlChar *attrName,
 | |
| 			  const xmlChar *uriAttrName, xmlCatalogPrefer prefer,
 | |
| 			  xmlCatalogEntryPtr cgroup) {
 | |
|     int ok = 1;
 | |
|     xmlChar *uriValue;
 | |
|     xmlChar *nameValue = NULL;
 | |
|     xmlChar *base = NULL;
 | |
|     xmlChar *URL = NULL;
 | |
|     xmlCatalogEntryPtr ret = NULL;
 | |
| 
 | |
|     if (attrName != NULL) {
 | |
| 	nameValue = xmlGetProp(cur, attrName);
 | |
| 	if (nameValue == NULL) {
 | |
| 	    xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
 | |
| 			  "%s entry lacks '%s'\n", name, attrName, NULL);
 | |
| 	    ok = 0;
 | |
| 	}
 | |
|     }
 | |
|     uriValue = xmlGetProp(cur, uriAttrName);
 | |
|     if (uriValue == NULL) {
 | |
| 	xmlCatalogErr(ret, cur, XML_CATALOG_MISSING_ATTR,
 | |
| 		"%s entry lacks '%s'\n", name, uriAttrName, NULL);
 | |
| 	ok = 0;
 | |
|     }
 | |
|     if (!ok) {
 | |
| 	if (nameValue != NULL)
 | |
| 	    xmlFree(nameValue);
 | |
| 	if (uriValue != NULL)
 | |
| 	    xmlFree(uriValue);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     base = xmlNodeGetBase(cur->doc, cur);
 | |
|     URL = xmlBuildURI(uriValue, base);
 | |
|     if (URL != NULL) {
 | |
| 	if (xmlDebugCatalogs > 1) {
 | |
| 	    if (nameValue != NULL)
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Found %s: '%s' '%s'\n", name, nameValue, URL);
 | |
| 	    else
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Found %s: '%s'\n", name, URL);
 | |
| 	}
 | |
| 	ret = xmlNewCatalogEntry(type, nameValue, uriValue, URL, prefer, cgroup);
 | |
|     } else {
 | |
| 	xmlCatalogErr(ret, cur, XML_CATALOG_ENTRY_BROKEN,
 | |
| 		"%s entry '%s' broken ?: %s\n", name, uriAttrName, uriValue);
 | |
|     }
 | |
|     if (nameValue != NULL)
 | |
| 	xmlFree(nameValue);
 | |
|     if (uriValue != NULL)
 | |
| 	xmlFree(uriValue);
 | |
|     if (base != NULL)
 | |
| 	xmlFree(base);
 | |
|     if (URL != NULL)
 | |
| 	xmlFree(URL);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseXMLCatalogNode:
 | |
|  * @cur:  the XML node
 | |
|  * @prefer:  the PUBLIC vs. SYSTEM current preference value
 | |
|  * @parent:  the parent Catalog entry
 | |
|  * @cgroup:  the group which includes this node
 | |
|  *
 | |
|  * Examines an XML tree node of a catalog and build
 | |
|  * a Catalog entry from it adding it to its parent. The examination can
 | |
|  * be recursive.
 | |
|  */
 | |
| static void
 | |
| xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
 | |
| 	               xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup)
 | |
| {
 | |
|     xmlChar *base = NULL;
 | |
|     xmlCatalogEntryPtr entry = NULL;
 | |
| 
 | |
|     if (cur == NULL)
 | |
|         return;
 | |
|     if (xmlStrEqual(cur->name, BAD_CAST "group")) {
 | |
|         xmlChar *prop;
 | |
| 	xmlCatalogPrefer pref = XML_CATA_PREFER_NONE;
 | |
| 
 | |
|         prop = xmlGetProp(cur, BAD_CAST "prefer");
 | |
|         if (prop != NULL) {
 | |
|             if (xmlStrEqual(prop, BAD_CAST "system")) {
 | |
|                 prefer = XML_CATA_PREFER_SYSTEM;
 | |
|             } else if (xmlStrEqual(prop, BAD_CAST "public")) {
 | |
|                 prefer = XML_CATA_PREFER_PUBLIC;
 | |
|             } else {
 | |
| 		xmlCatalogErr(parent, cur, XML_CATALOG_PREFER_VALUE,
 | |
|                               "Invalid value for prefer: '%s'\n",
 | |
| 			      prop, NULL, NULL);
 | |
|             }
 | |
|             xmlFree(prop);
 | |
| 	    pref = prefer;
 | |
|         }
 | |
| 	prop = xmlGetProp(cur, BAD_CAST "id");
 | |
| 	base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE);
 | |
| 	entry = xmlNewCatalogEntry(XML_CATA_GROUP, prop, base, NULL, pref, cgroup);
 | |
| 	xmlFree(prop);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "public")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC,
 | |
| 		BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
 | |
| 		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
 | |
| 		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
 | |
| 		BAD_CAST "rewritePrefix", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
 | |
| 		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
 | |
| 		BAD_CAST "catalog", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
 | |
| 		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
 | |
| 		BAD_CAST "catalog", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
 | |
| 		BAD_CAST "uri", BAD_CAST "name",
 | |
| 		BAD_CAST "uri", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
 | |
| 		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
 | |
| 		BAD_CAST "rewritePrefix", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
 | |
| 		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
 | |
| 		BAD_CAST "catalog", prefer, cgroup);
 | |
|     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
 | |
| 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
 | |
| 		BAD_CAST "nextCatalog", NULL,
 | |
| 		BAD_CAST "catalog", prefer, cgroup);
 | |
|     }
 | |
|     if (entry != NULL) {
 | |
|         if (parent != NULL) {
 | |
| 	    entry->parent = parent;
 | |
| 	    if (parent->children == NULL)
 | |
| 		parent->children = entry;
 | |
| 	    else {
 | |
| 		xmlCatalogEntryPtr prev;
 | |
| 
 | |
| 		prev = parent->children;
 | |
| 		while (prev->next != NULL)
 | |
| 		    prev = prev->next;
 | |
| 		prev->next = entry;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (entry->type == XML_CATA_GROUP) {
 | |
| 	    /*
 | |
| 	     * Recurse to propagate prefer to the subtree
 | |
| 	     * (xml:base handling is automated)
 | |
| 	     */
 | |
|             xmlParseXMLCatalogNodeList(cur->children, prefer, parent, entry);
 | |
| 	}
 | |
|     }
 | |
|     if (base != NULL)
 | |
| 	xmlFree(base);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseXMLCatalogNodeList:
 | |
|  * @cur:  the XML node list of siblings
 | |
|  * @prefer:  the PUBLIC vs. SYSTEM current preference value
 | |
|  * @parent:  the parent Catalog entry
 | |
|  * @cgroup:  the group which includes this list
 | |
|  *
 | |
|  * Examines a list of XML sibling nodes of a catalog and build
 | |
|  * a list of Catalog entry from it adding it to the parent.
 | |
|  * The examination will recurse to examine node subtrees.
 | |
|  */
 | |
| static void
 | |
| xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer,
 | |
| 	                   xmlCatalogEntryPtr parent, xmlCatalogEntryPtr cgroup) {
 | |
|     while (cur != NULL) {
 | |
| 	if ((cur->ns != NULL) && (cur->ns->href != NULL) &&
 | |
| 	    (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
 | |
| 	    xmlParseXMLCatalogNode(cur, prefer, parent, cgroup);
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
|     }
 | |
|     /* TODO: sort the list according to REWRITE lengths and prefer value */
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseXMLCatalogFile:
 | |
|  * @prefer:  the PUBLIC vs. SYSTEM current preference value
 | |
|  * @filename:  the filename for the catalog
 | |
|  *
 | |
|  * Parses the catalog file to extract the XML tree and then analyze the
 | |
|  * tree to build a list of Catalog entries corresponding to this catalog
 | |
|  *
 | |
|  * Returns the resulting Catalog entries list
 | |
|  */
 | |
| static xmlCatalogEntryPtr
 | |
| xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename) {
 | |
|     xmlDocPtr doc;
 | |
|     xmlNodePtr cur;
 | |
|     xmlChar *prop;
 | |
|     xmlCatalogEntryPtr parent = NULL;
 | |
| 
 | |
|     if (filename == NULL)
 | |
|         return(NULL);
 | |
| 
 | |
|     doc = xmlParseCatalogFile((const char *) filename);
 | |
|     if (doc == NULL) {
 | |
| 	if (xmlDebugCatalogs)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Failed to parse catalog %s\n", filename);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"%d Parsing catalog %s\n", xmlGetThreadId(), filename);
 | |
| 
 | |
|     cur = xmlDocGetRootElement(doc);
 | |
|     if ((cur != NULL) && (xmlStrEqual(cur->name, BAD_CAST "catalog")) &&
 | |
| 	(cur->ns != NULL) && (cur->ns->href != NULL) &&
 | |
| 	(xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) {
 | |
| 
 | |
| 	parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
 | |
| 				    (const xmlChar *)filename, NULL, prefer, NULL);
 | |
|         if (parent == NULL) {
 | |
| 	    xmlFreeDoc(doc);
 | |
| 	    return(NULL);
 | |
| 	}
 | |
| 
 | |
| 	prop = xmlGetProp(cur, BAD_CAST "prefer");
 | |
| 	if (prop != NULL) {
 | |
| 	    if (xmlStrEqual(prop, BAD_CAST "system")) {
 | |
| 		prefer = XML_CATA_PREFER_SYSTEM;
 | |
| 	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
 | |
| 		prefer = XML_CATA_PREFER_PUBLIC;
 | |
| 	    } else {
 | |
| 		xmlCatalogErr(NULL, cur, XML_CATALOG_PREFER_VALUE,
 | |
| 			      "Invalid value for prefer: '%s'\n",
 | |
| 			      prop, NULL, NULL);
 | |
| 	    }
 | |
| 	    xmlFree(prop);
 | |
| 	}
 | |
| 	cur = cur->children;
 | |
| 	xmlParseXMLCatalogNodeList(cur, prefer, parent, NULL);
 | |
|     } else {
 | |
| 	xmlCatalogErr(NULL, (xmlNodePtr) doc, XML_CATALOG_NOT_CATALOG,
 | |
| 		      "File %s is not an XML Catalog\n",
 | |
| 		      filename, NULL, NULL);
 | |
| 	xmlFreeDoc(doc);
 | |
| 	return(NULL);
 | |
|     }
 | |
|     xmlFreeDoc(doc);
 | |
|     return(parent);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFetchXMLCatalogFile:
 | |
|  * @catal:  an existing but incomplete catalog entry
 | |
|  *
 | |
|  * Fetch and parse the subcatalog referenced by an entry
 | |
|  *
 | |
|  * Returns 0 in case of success, -1 otherwise
 | |
|  */
 | |
| static int
 | |
| xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
 | |
|     xmlCatalogEntryPtr doc;
 | |
| 
 | |
|     if (catal == NULL)
 | |
| 	return(-1);
 | |
|     if (catal->URL == NULL)
 | |
| 	return(-1);
 | |
| 
 | |
|     /*
 | |
|      * lock the whole catalog for modification
 | |
|      */
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
|     if (catal->children != NULL) {
 | |
| 	/* Okay someone else did it in the meantime */
 | |
| 	xmlRMutexUnlock(xmlCatalogMutex);
 | |
| 	return(0);
 | |
|     }
 | |
| 
 | |
|     if (xmlCatalogXMLFiles != NULL) {
 | |
| 	doc = (xmlCatalogEntryPtr)
 | |
| 	    xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
 | |
| 	if (doc != NULL) {
 | |
| 	    if (xmlDebugCatalogs)
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Found %s in file hash\n", catal->URL);
 | |
| 
 | |
| 	    if (catal->type == XML_CATA_CATALOG)
 | |
| 		catal->children = doc->children;
 | |
| 	    else
 | |
| 		catal->children = doc;
 | |
| 	    catal->dealloc = 0;
 | |
| 	    xmlRMutexUnlock(xmlCatalogMutex);
 | |
| 	    return(0);
 | |
| 	}
 | |
| 	if (xmlDebugCatalogs)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		"%s not found in file hash\n", catal->URL);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Fetch and parse. Note that xmlParseXMLCatalogFile does not
 | |
|      * use the existing catalog, there is no recursion allowed at
 | |
|      * that level.
 | |
|      */
 | |
|     doc = xmlParseXMLCatalogFile(catal->prefer, catal->URL);
 | |
|     if (doc == NULL) {
 | |
| 	catal->type = XML_CATA_BROKEN_CATALOG;
 | |
| 	xmlRMutexUnlock(xmlCatalogMutex);
 | |
| 	return(-1);
 | |
|     }
 | |
| 
 | |
|     if (catal->type == XML_CATA_CATALOG)
 | |
| 	catal->children = doc->children;
 | |
|     else
 | |
| 	catal->children = doc;
 | |
| 
 | |
|     doc->dealloc = 1;
 | |
| 
 | |
|     if (xmlCatalogXMLFiles == NULL)
 | |
| 	xmlCatalogXMLFiles = xmlHashCreate(10);
 | |
|     if (xmlCatalogXMLFiles != NULL) {
 | |
| 	if (xmlDebugCatalogs)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		"%s added to file hash\n", catal->URL);
 | |
| 	xmlHashAddEntry(xmlCatalogXMLFiles, catal->URL, doc);
 | |
|     }
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
|     return(0);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			XML Catalog handling				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlAddXMLCatalog:
 | |
|  * @catal:  top of an XML catalog
 | |
|  * @type:  the type of record to add to the catalog
 | |
|  * @orig:  the system, public or prefix to match (or NULL)
 | |
|  * @replace:  the replacement value for the match
 | |
|  *
 | |
|  * Add an entry in the XML catalog, it may overwrite existing but
 | |
|  * different entries.
 | |
|  *
 | |
|  * Returns 0 if successful, -1 otherwise
 | |
|  */
 | |
| static int
 | |
| xmlAddXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *type,
 | |
| 	      const xmlChar *orig, const xmlChar *replace) {
 | |
|     xmlCatalogEntryPtr cur;
 | |
|     xmlCatalogEntryType typ;
 | |
|     int doregister = 0;
 | |
| 
 | |
|     if ((catal == NULL) ||
 | |
| 	((catal->type != XML_CATA_CATALOG) &&
 | |
| 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
 | |
| 	return(-1);
 | |
|     if (catal->children == NULL) {
 | |
| 	xmlFetchXMLCatalogFile(catal);
 | |
|     }
 | |
|     if (catal->children == NULL)
 | |
| 	doregister = 1;
 | |
| 
 | |
|     typ = xmlGetXMLCatalogEntryType(type);
 | |
|     if (typ == XML_CATA_NONE) {
 | |
| 	if (xmlDebugCatalogs)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Failed to add unknown element %s to catalog\n", type);
 | |
| 	return(-1);
 | |
|     }
 | |
| 
 | |
|     cur = catal->children;
 | |
|     /*
 | |
|      * Might be a simple "update in place"
 | |
|      */
 | |
|     if (cur != NULL) {
 | |
| 	while (cur != NULL) {
 | |
| 	    if ((orig != NULL) && (cur->type == typ) &&
 | |
| 		(xmlStrEqual(orig, cur->name))) {
 | |
| 		if (xmlDebugCatalogs)
 | |
| 		    xmlGenericError(xmlGenericErrorContext,
 | |
| 			    "Updating element %s to catalog\n", type);
 | |
| 		if (cur->value != NULL)
 | |
| 		    xmlFree(cur->value);
 | |
| 		if (cur->URL != NULL)
 | |
| 		    xmlFree(cur->URL);
 | |
| 		cur->value = xmlStrdup(replace);
 | |
| 		cur->URL = xmlStrdup(replace);
 | |
| 		return(0);
 | |
| 	    }
 | |
| 	    if (cur->next == NULL)
 | |
| 		break;
 | |
| 	    cur = cur->next;
 | |
| 	}
 | |
|     }
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Adding element %s to catalog\n", type);
 | |
|     if (cur == NULL)
 | |
| 	catal->children = xmlNewCatalogEntry(typ, orig, replace,
 | |
| 		                             NULL, catal->prefer, NULL);
 | |
|     else
 | |
| 	cur->next = xmlNewCatalogEntry(typ, orig, replace,
 | |
| 		                       NULL, catal->prefer, NULL);
 | |
|     if (doregister) {
 | |
|         catal->type = XML_CATA_CATALOG;
 | |
| 	cur = (xmlCatalogEntryPtr)xmlHashLookup(xmlCatalogXMLFiles, catal->URL);
 | |
| 	if (cur != NULL)
 | |
| 	    cur->children = catal->children;
 | |
|     }
 | |
| 
 | |
|     return(0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlDelXMLCatalog:
 | |
|  * @catal:  top of an XML catalog
 | |
|  * @value:  the value to remove from the catalog
 | |
|  *
 | |
|  * Remove entries in the XML catalog where the value or the URI
 | |
|  * is equal to @value
 | |
|  *
 | |
|  * Returns the number of entries removed if successful, -1 otherwise
 | |
|  */
 | |
| static int
 | |
| xmlDelXMLCatalog(xmlCatalogEntryPtr catal, const xmlChar *value) {
 | |
|     xmlCatalogEntryPtr cur;
 | |
|     int ret = 0;
 | |
| 
 | |
|     if ((catal == NULL) ||
 | |
| 	((catal->type != XML_CATA_CATALOG) &&
 | |
| 	 (catal->type != XML_CATA_BROKEN_CATALOG)))
 | |
| 	return(-1);
 | |
|     if (value == NULL)
 | |
| 	return(-1);
 | |
|     if (catal->children == NULL) {
 | |
| 	xmlFetchXMLCatalogFile(catal);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Scan the children
 | |
|      */
 | |
|     cur = catal->children;
 | |
|     while (cur != NULL) {
 | |
| 	if (((cur->name != NULL) && (xmlStrEqual(value, cur->name))) ||
 | |
| 	    (xmlStrEqual(value, cur->value))) {
 | |
| 	    if (xmlDebugCatalogs) {
 | |
| 		if (cur->name != NULL)
 | |
| 		    xmlGenericError(xmlGenericErrorContext,
 | |
| 			    "Removing element %s from catalog\n", cur->name);
 | |
| 		else
 | |
| 		    xmlGenericError(xmlGenericErrorContext,
 | |
| 			    "Removing element %s from catalog\n", cur->value);
 | |
| 	    }
 | |
| 	    cur->type = XML_CATA_REMOVED;
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
|     }
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogXMLResolve:
 | |
|  * @catal:  a catalog list
 | |
|  * @pubID:  the public ID string
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier for a
 | |
|  * list of catalog entries.
 | |
|  *
 | |
|  * Implements (or tries to) 7.1. External Identifier Resolution
 | |
|  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlCatalogXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
 | |
| 	              const xmlChar *sysID) {
 | |
|     xmlChar *ret = NULL;
 | |
|     xmlCatalogEntryPtr cur;
 | |
|     int haveDelegate = 0;
 | |
|     int haveNext = 0;
 | |
| 
 | |
|     /*
 | |
|      * protection against loops
 | |
|      */
 | |
|     if (catal->depth > MAX_CATAL_DEPTH) {
 | |
| 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
 | |
| 		      "Detected recursion in catalog %s\n",
 | |
| 		      catal->name, NULL, NULL);
 | |
| 	return(NULL);
 | |
|     }
 | |
|     catal->depth++;
 | |
| 
 | |
|     /*
 | |
|      * First tries steps 2/ 3/ 4/ if a system ID is provided.
 | |
|      */
 | |
|     if (sysID != NULL) {
 | |
| 	xmlCatalogEntryPtr rewrite = NULL;
 | |
| 	int lenrewrite = 0, len;
 | |
| 	cur = catal;
 | |
| 	haveDelegate = 0;
 | |
| 	while (cur != NULL) {
 | |
| 	    switch (cur->type) {
 | |
| 		case XML_CATA_SYSTEM:
 | |
| 		    if (xmlStrEqual(sysID, cur->name)) {
 | |
| 			if (xmlDebugCatalogs)
 | |
| 			    xmlGenericError(xmlGenericErrorContext,
 | |
| 				    "Found system match %s, using %s\n",
 | |
| 				            cur->name, cur->URL);
 | |
| 			catal->depth--;
 | |
| 			return(xmlStrdup(cur->URL));
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case XML_CATA_REWRITE_SYSTEM:
 | |
| 		    len = xmlStrlen(cur->name);
 | |
| 		    if ((len > lenrewrite) &&
 | |
| 			(!xmlStrncmp(sysID, cur->name, len))) {
 | |
| 			lenrewrite = len;
 | |
| 			rewrite = cur;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case XML_CATA_DELEGATE_SYSTEM:
 | |
| 		    if (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))
 | |
| 			haveDelegate++;
 | |
| 		    break;
 | |
| 		case XML_CATA_NEXT_CATALOG:
 | |
| 		    haveNext++;
 | |
| 		    break;
 | |
| 		default:
 | |
| 		    break;
 | |
| 	    }
 | |
| 	    cur = cur->next;
 | |
| 	}
 | |
| 	if (rewrite != NULL) {
 | |
| 	    if (xmlDebugCatalogs)
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Using rewriting rule %s\n", rewrite->name);
 | |
| 	    ret = xmlStrdup(rewrite->URL);
 | |
| 	    if (ret != NULL)
 | |
| 		ret = xmlStrcat(ret, &sysID[lenrewrite]);
 | |
| 	    catal->depth--;
 | |
| 	    return(ret);
 | |
| 	}
 | |
| 	if (haveDelegate) {
 | |
| 	    const xmlChar *delegates[MAX_DELEGATE];
 | |
| 	    int nbList = 0, i;
 | |
| 
 | |
| 	    /*
 | |
| 	     * Assume the entries have been sorted by decreasing substring
 | |
| 	     * matches when the list was produced.
 | |
| 	     */
 | |
| 	    cur = catal;
 | |
| 	    while (cur != NULL) {
 | |
| 		if ((cur->type == XML_CATA_DELEGATE_SYSTEM) &&
 | |
| 		    (!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
 | |
| 		    for (i = 0;i < nbList;i++)
 | |
| 			if (xmlStrEqual(cur->URL, delegates[i]))
 | |
| 			    break;
 | |
| 		    if (i < nbList) {
 | |
| 			cur = cur->next;
 | |
| 			continue;
 | |
| 		    }
 | |
| 		    if (nbList < MAX_DELEGATE)
 | |
| 			delegates[nbList++] = cur->URL;
 | |
| 
 | |
| 		    if (cur->children == NULL) {
 | |
| 			xmlFetchXMLCatalogFile(cur);
 | |
| 		    }
 | |
| 		    if (cur->children != NULL) {
 | |
| 			if (xmlDebugCatalogs)
 | |
| 			    xmlGenericError(xmlGenericErrorContext,
 | |
| 				    "Trying system delegate %s\n", cur->URL);
 | |
| 			ret = xmlCatalogListXMLResolve(
 | |
| 				cur->children, NULL, sysID);
 | |
| 			if (ret != NULL) {
 | |
| 			    catal->depth--;
 | |
| 			    return(ret);
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		cur = cur->next;
 | |
| 	    }
 | |
| 	    /*
 | |
| 	     * Apply the cut algorithm explained in 4/
 | |
| 	     */
 | |
| 	    catal->depth--;
 | |
| 	    return(XML_CATAL_BREAK);
 | |
| 	}
 | |
|     }
 | |
|     /*
 | |
|      * Then tries 5/ 6/ if a public ID is provided
 | |
|      */
 | |
|     if (pubID != NULL) {
 | |
| 	cur = catal;
 | |
| 	haveDelegate = 0;
 | |
| 	while (cur != NULL) {
 | |
| 	    switch (cur->type) {
 | |
| 		case XML_CATA_PUBLIC:
 | |
| 		    if (xmlStrEqual(pubID, cur->name)) {
 | |
| 			if (xmlDebugCatalogs)
 | |
| 			    xmlGenericError(xmlGenericErrorContext,
 | |
| 				    "Found public match %s\n", cur->name);
 | |
| 			catal->depth--;
 | |
| 			return(xmlStrdup(cur->URL));
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case XML_CATA_DELEGATE_PUBLIC:
 | |
| 		    if (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)) &&
 | |
| 			(cur->prefer == XML_CATA_PREFER_PUBLIC))
 | |
| 			haveDelegate++;
 | |
| 		    break;
 | |
| 		case XML_CATA_NEXT_CATALOG:
 | |
| 		    if (sysID == NULL)
 | |
| 			haveNext++;
 | |
| 		    break;
 | |
| 		default:
 | |
| 		    break;
 | |
| 	    }
 | |
| 	    cur = cur->next;
 | |
| 	}
 | |
| 	if (haveDelegate) {
 | |
| 	    const xmlChar *delegates[MAX_DELEGATE];
 | |
| 	    int nbList = 0, i;
 | |
| 
 | |
| 	    /*
 | |
| 	     * Assume the entries have been sorted by decreasing substring
 | |
| 	     * matches when the list was produced.
 | |
| 	     */
 | |
| 	    cur = catal;
 | |
| 	    while (cur != NULL) {
 | |
| 		if ((cur->type == XML_CATA_DELEGATE_PUBLIC) &&
 | |
| 		    (cur->prefer == XML_CATA_PREFER_PUBLIC) &&
 | |
| 		    (!xmlStrncmp(pubID, cur->name, xmlStrlen(cur->name)))) {
 | |
| 
 | |
| 		    for (i = 0;i < nbList;i++)
 | |
| 			if (xmlStrEqual(cur->URL, delegates[i]))
 | |
| 			    break;
 | |
| 		    if (i < nbList) {
 | |
| 			cur = cur->next;
 | |
| 			continue;
 | |
| 		    }
 | |
| 		    if (nbList < MAX_DELEGATE)
 | |
| 			delegates[nbList++] = cur->URL;
 | |
| 
 | |
| 		    if (cur->children == NULL) {
 | |
| 			xmlFetchXMLCatalogFile(cur);
 | |
| 		    }
 | |
| 		    if (cur->children != NULL) {
 | |
| 			if (xmlDebugCatalogs)
 | |
| 			    xmlGenericError(xmlGenericErrorContext,
 | |
| 				    "Trying public delegate %s\n", cur->URL);
 | |
| 			ret = xmlCatalogListXMLResolve(
 | |
| 				cur->children, pubID, NULL);
 | |
| 			if (ret != NULL) {
 | |
| 			    catal->depth--;
 | |
| 			    return(ret);
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 		cur = cur->next;
 | |
| 	    }
 | |
| 	    /*
 | |
| 	     * Apply the cut algorithm explained in 4/
 | |
| 	     */
 | |
| 	    catal->depth--;
 | |
| 	    return(XML_CATAL_BREAK);
 | |
| 	}
 | |
|     }
 | |
|     if (haveNext) {
 | |
| 	cur = catal;
 | |
| 	while (cur != NULL) {
 | |
| 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
 | |
| 		if (cur->children == NULL) {
 | |
| 		    xmlFetchXMLCatalogFile(cur);
 | |
| 		}
 | |
| 		if (cur->children != NULL) {
 | |
| 		    ret = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
 | |
| 		    if (ret != NULL) {
 | |
| 			catal->depth--;
 | |
| 			return(ret);
 | |
| 		    } else if (catal->depth > MAX_CATAL_DEPTH) {
 | |
| 		        return(NULL);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    cur = cur->next;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     catal->depth--;
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogXMLResolveURI:
 | |
|  * @catal:  a catalog list
 | |
|  * @URI:  the URI
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier for a
 | |
|  * list of catalog entries.
 | |
|  *
 | |
|  * Implements (or tries to) 7.2.2. URI Resolution
 | |
|  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlCatalogXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
 | |
|     xmlChar *ret = NULL;
 | |
|     xmlCatalogEntryPtr cur;
 | |
|     int haveDelegate = 0;
 | |
|     int haveNext = 0;
 | |
|     xmlCatalogEntryPtr rewrite = NULL;
 | |
|     int lenrewrite = 0, len;
 | |
| 
 | |
|     if (catal == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (URI == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (catal->depth > MAX_CATAL_DEPTH) {
 | |
| 	xmlCatalogErr(catal, NULL, XML_CATALOG_RECURSION,
 | |
| 		      "Detected recursion in catalog %s\n",
 | |
| 		      catal->name, NULL, NULL);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * First tries steps 2/ 3/ 4/ if a system ID is provided.
 | |
|      */
 | |
|     cur = catal;
 | |
|     haveDelegate = 0;
 | |
|     while (cur != NULL) {
 | |
| 	switch (cur->type) {
 | |
| 	    case XML_CATA_URI:
 | |
| 		if (xmlStrEqual(URI, cur->name)) {
 | |
| 		    if (xmlDebugCatalogs)
 | |
| 			xmlGenericError(xmlGenericErrorContext,
 | |
| 				"Found URI match %s\n", cur->name);
 | |
| 		    return(xmlStrdup(cur->URL));
 | |
| 		}
 | |
| 		break;
 | |
| 	    case XML_CATA_REWRITE_URI:
 | |
| 		len = xmlStrlen(cur->name);
 | |
| 		if ((len > lenrewrite) &&
 | |
| 		    (!xmlStrncmp(URI, cur->name, len))) {
 | |
| 		    lenrewrite = len;
 | |
| 		    rewrite = cur;
 | |
| 		}
 | |
| 		break;
 | |
| 	    case XML_CATA_DELEGATE_URI:
 | |
| 		if (!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))
 | |
| 		    haveDelegate++;
 | |
| 		break;
 | |
| 	    case XML_CATA_NEXT_CATALOG:
 | |
| 		haveNext++;
 | |
| 		break;
 | |
| 	    default:
 | |
| 		break;
 | |
| 	}
 | |
| 	cur = cur->next;
 | |
|     }
 | |
|     if (rewrite != NULL) {
 | |
| 	if (xmlDebugCatalogs)
 | |
| 	    xmlGenericError(xmlGenericErrorContext,
 | |
| 		    "Using rewriting rule %s\n", rewrite->name);
 | |
| 	ret = xmlStrdup(rewrite->URL);
 | |
| 	if (ret != NULL)
 | |
| 	    ret = xmlStrcat(ret, &URI[lenrewrite]);
 | |
| 	return(ret);
 | |
|     }
 | |
|     if (haveDelegate) {
 | |
| 	const xmlChar *delegates[MAX_DELEGATE];
 | |
| 	int nbList = 0, i;
 | |
| 
 | |
| 	/*
 | |
| 	 * Assume the entries have been sorted by decreasing substring
 | |
| 	 * matches when the list was produced.
 | |
| 	 */
 | |
| 	cur = catal;
 | |
| 	while (cur != NULL) {
 | |
| 	    if (((cur->type == XML_CATA_DELEGATE_SYSTEM) ||
 | |
| 	         (cur->type == XML_CATA_DELEGATE_URI)) &&
 | |
| 		(!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
 | |
| 		for (i = 0;i < nbList;i++)
 | |
| 		    if (xmlStrEqual(cur->URL, delegates[i]))
 | |
| 			break;
 | |
| 		if (i < nbList) {
 | |
| 		    cur = cur->next;
 | |
| 		    continue;
 | |
| 		}
 | |
| 		if (nbList < MAX_DELEGATE)
 | |
| 		    delegates[nbList++] = cur->URL;
 | |
| 
 | |
| 		if (cur->children == NULL) {
 | |
| 		    xmlFetchXMLCatalogFile(cur);
 | |
| 		}
 | |
| 		if (cur->children != NULL) {
 | |
| 		    if (xmlDebugCatalogs)
 | |
| 			xmlGenericError(xmlGenericErrorContext,
 | |
| 				"Trying URI delegate %s\n", cur->URL);
 | |
| 		    ret = xmlCatalogListXMLResolveURI(
 | |
| 			    cur->children, URI);
 | |
| 		    if (ret != NULL)
 | |
| 			return(ret);
 | |
| 		}
 | |
| 	    }
 | |
| 	    cur = cur->next;
 | |
| 	}
 | |
| 	/*
 | |
| 	 * Apply the cut algorithm explained in 4/
 | |
| 	 */
 | |
| 	return(XML_CATAL_BREAK);
 | |
|     }
 | |
|     if (haveNext) {
 | |
| 	cur = catal;
 | |
| 	while (cur != NULL) {
 | |
| 	    if (cur->type == XML_CATA_NEXT_CATALOG) {
 | |
| 		if (cur->children == NULL) {
 | |
| 		    xmlFetchXMLCatalogFile(cur);
 | |
| 		}
 | |
| 		if (cur->children != NULL) {
 | |
| 		    ret = xmlCatalogListXMLResolveURI(cur->children, URI);
 | |
| 		    if (ret != NULL)
 | |
| 			return(ret);
 | |
| 		}
 | |
| 	    }
 | |
| 	    cur = cur->next;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogListXMLResolve:
 | |
|  * @catal:  a catalog list
 | |
|  * @pubID:  the public ID string
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier for a
 | |
|  * list of catalogs
 | |
|  *
 | |
|  * Implements (or tries to) 7.1. External Identifier Resolution
 | |
|  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlCatalogListXMLResolve(xmlCatalogEntryPtr catal, const xmlChar *pubID,
 | |
| 	              const xmlChar *sysID) {
 | |
|     xmlChar *ret = NULL;
 | |
|     xmlChar *urnID = NULL;
 | |
|     xmlChar *normid;
 | |
| 
 | |
|     if (catal == NULL)
 | |
|         return(NULL);
 | |
|     if ((pubID == NULL) && (sysID == NULL))
 | |
| 	return(NULL);
 | |
| 
 | |
|     normid = xmlCatalogNormalizePublic(pubID);
 | |
|     if (normid != NULL)
 | |
|         pubID = (*normid != 0 ? normid : NULL);
 | |
| 
 | |
|     if (!xmlStrncmp(pubID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
 | |
| 	urnID = xmlCatalogUnWrapURN(pubID);
 | |
| 	if (xmlDebugCatalogs) {
 | |
| 	    if (urnID == NULL)
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Public URN ID %s expanded to NULL\n", pubID);
 | |
| 	    else
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Public URN ID expanded to %s\n", urnID);
 | |
| 	}
 | |
| 	ret = xmlCatalogListXMLResolve(catal, urnID, sysID);
 | |
| 	if (urnID != NULL)
 | |
| 	    xmlFree(urnID);
 | |
| 	if (normid != NULL)
 | |
| 	    xmlFree(normid);
 | |
| 	return(ret);
 | |
|     }
 | |
|     if (!xmlStrncmp(sysID, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
 | |
| 	urnID = xmlCatalogUnWrapURN(sysID);
 | |
| 	if (xmlDebugCatalogs) {
 | |
| 	    if (urnID == NULL)
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"System URN ID %s expanded to NULL\n", sysID);
 | |
| 	    else
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"System URN ID expanded to %s\n", urnID);
 | |
| 	}
 | |
| 	if (pubID == NULL)
 | |
| 	    ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
 | |
| 	else if (xmlStrEqual(pubID, urnID))
 | |
| 	    ret = xmlCatalogListXMLResolve(catal, pubID, NULL);
 | |
| 	else {
 | |
| 	    ret = xmlCatalogListXMLResolve(catal, pubID, urnID);
 | |
| 	}
 | |
| 	if (urnID != NULL)
 | |
| 	    xmlFree(urnID);
 | |
| 	if (normid != NULL)
 | |
| 	    xmlFree(normid);
 | |
| 	return(ret);
 | |
|     }
 | |
|     while (catal != NULL) {
 | |
| 	if (catal->type == XML_CATA_CATALOG) {
 | |
| 	    if (catal->children == NULL) {
 | |
| 		xmlFetchXMLCatalogFile(catal);
 | |
| 	    }
 | |
| 	    if (catal->children != NULL) {
 | |
| 		ret = xmlCatalogXMLResolve(catal->children, pubID, sysID);
 | |
| 		if (ret != NULL) {
 | |
| 		    break;
 | |
|                 } else if (catal->children->depth > MAX_CATAL_DEPTH) {
 | |
| 	            ret = NULL;
 | |
| 		    break;
 | |
| 	        }
 | |
| 	    }
 | |
| 	}
 | |
| 	catal = catal->next;
 | |
|     }
 | |
|     if (normid != NULL)
 | |
| 	xmlFree(normid);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogListXMLResolveURI:
 | |
|  * @catal:  a catalog list
 | |
|  * @URI:  the URI
 | |
|  *
 | |
|  * Do a complete resolution lookup of an URI for a list of catalogs
 | |
|  *
 | |
|  * Implements (or tries to) 7.2. URI Resolution
 | |
|  * from http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found
 | |
|  */
 | |
| static xmlChar *
 | |
| xmlCatalogListXMLResolveURI(xmlCatalogEntryPtr catal, const xmlChar *URI) {
 | |
|     xmlChar *ret = NULL;
 | |
|     xmlChar *urnID = NULL;
 | |
| 
 | |
|     if (catal == NULL)
 | |
|         return(NULL);
 | |
|     if (URI == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (!xmlStrncmp(URI, BAD_CAST XML_URN_PUBID, sizeof(XML_URN_PUBID) - 1)) {
 | |
| 	urnID = xmlCatalogUnWrapURN(URI);
 | |
| 	if (xmlDebugCatalogs) {
 | |
| 	    if (urnID == NULL)
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"URN ID %s expanded to NULL\n", URI);
 | |
| 	    else
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"URN ID expanded to %s\n", urnID);
 | |
| 	}
 | |
| 	ret = xmlCatalogListXMLResolve(catal, urnID, NULL);
 | |
| 	if (urnID != NULL)
 | |
| 	    xmlFree(urnID);
 | |
| 	return(ret);
 | |
|     }
 | |
|     while (catal != NULL) {
 | |
| 	if (catal->type == XML_CATA_CATALOG) {
 | |
| 	    if (catal->children == NULL) {
 | |
| 		xmlFetchXMLCatalogFile(catal);
 | |
| 	    }
 | |
| 	    if (catal->children != NULL) {
 | |
| 		ret = xmlCatalogXMLResolveURI(catal->children, URI);
 | |
| 		if (ret != NULL)
 | |
| 		    return(ret);
 | |
| 	    }
 | |
| 	}
 | |
| 	catal = catal->next;
 | |
|     }
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			The SGML Catalog parser				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| 
 | |
| #define RAW *cur
 | |
| #define NEXT cur++;
 | |
| #define SKIP(x) cur += x;
 | |
| 
 | |
| #define SKIP_BLANKS while (IS_BLANK_CH(*cur)) NEXT;
 | |
| 
 | |
| /**
 | |
|  * xmlParseSGMLCatalogComment:
 | |
|  * @cur:  the current character
 | |
|  *
 | |
|  * Skip a comment in an SGML catalog
 | |
|  *
 | |
|  * Returns new current character
 | |
|  */
 | |
| static const xmlChar *
 | |
| xmlParseSGMLCatalogComment(const xmlChar *cur) {
 | |
|     if ((cur[0] != '-') || (cur[1] != '-'))
 | |
| 	return(cur);
 | |
|     SKIP(2);
 | |
|     while ((cur[0] != 0) && ((cur[0] != '-') || ((cur[1] != '-'))))
 | |
| 	NEXT;
 | |
|     if (cur[0] == 0) {
 | |
| 	return(NULL);
 | |
|     }
 | |
|     return(cur + 2);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseSGMLCatalogPubid:
 | |
|  * @cur:  the current character
 | |
|  * @id:  the return location
 | |
|  *
 | |
|  * Parse an SGML catalog ID
 | |
|  *
 | |
|  * Returns new current character and store the value in @id
 | |
|  */
 | |
| static const xmlChar *
 | |
| xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
 | |
|     xmlChar *buf = NULL, *tmp;
 | |
|     int len = 0;
 | |
|     int size = 50;
 | |
|     xmlChar stop;
 | |
| 
 | |
|     *id = NULL;
 | |
| 
 | |
|     if (RAW == '"') {
 | |
|         NEXT;
 | |
| 	stop = '"';
 | |
|     } else if (RAW == '\'') {
 | |
|         NEXT;
 | |
| 	stop = '\'';
 | |
|     } else {
 | |
| 	stop = ' ';
 | |
|     }
 | |
|     buf = (xmlChar *) xmlMallocAtomic(size);
 | |
|     if (buf == NULL) {
 | |
|         xmlCatalogErrMemory("allocating public ID");
 | |
| 	return(NULL);
 | |
|     }
 | |
|     while (IS_PUBIDCHAR_CH(*cur) || (*cur == '?')) {
 | |
| 	if ((*cur == stop) && (stop != ' '))
 | |
| 	    break;
 | |
| 	if ((stop == ' ') && (IS_BLANK_CH(*cur)))
 | |
| 	    break;
 | |
| 	if (len + 1 >= size) {
 | |
| 	    size *= 2;
 | |
| 	    tmp = (xmlChar *) xmlRealloc(buf, size);
 | |
| 	    if (tmp == NULL) {
 | |
| 		xmlCatalogErrMemory("allocating public ID");
 | |
| 		xmlFree(buf);
 | |
| 		return(NULL);
 | |
| 	    }
 | |
| 	    buf = tmp;
 | |
| 	}
 | |
| 	buf[len++] = *cur;
 | |
| 	NEXT;
 | |
|     }
 | |
|     buf[len] = 0;
 | |
|     if (stop == ' ') {
 | |
| 	if (!IS_BLANK_CH(*cur)) {
 | |
| 	    xmlFree(buf);
 | |
| 	    return(NULL);
 | |
| 	}
 | |
|     } else {
 | |
| 	if (*cur != stop) {
 | |
| 	    xmlFree(buf);
 | |
| 	    return(NULL);
 | |
| 	}
 | |
| 	NEXT;
 | |
|     }
 | |
|     *id = buf;
 | |
|     return(cur);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseSGMLCatalogName:
 | |
|  * @cur:  the current character
 | |
|  * @name:  the return location
 | |
|  *
 | |
|  * Parse an SGML catalog name
 | |
|  *
 | |
|  * Returns new current character and store the value in @name
 | |
|  */
 | |
| static const xmlChar *
 | |
| xmlParseSGMLCatalogName(const xmlChar *cur, xmlChar **name) {
 | |
|     xmlChar buf[XML_MAX_NAMELEN + 5];
 | |
|     int len = 0;
 | |
|     int c;
 | |
| 
 | |
|     *name = NULL;
 | |
| 
 | |
|     /*
 | |
|      * Handler for more complex cases
 | |
|      */
 | |
|     c = *cur;
 | |
|     if ((!IS_LETTER(c) && (c != '_') && (c != ':'))) {
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     while (((IS_LETTER(c)) || (IS_DIGIT(c)) ||
 | |
|             (c == '.') || (c == '-') ||
 | |
| 	    (c == '_') || (c == ':'))) {
 | |
| 	buf[len++] = c;
 | |
| 	cur++;
 | |
| 	c = *cur;
 | |
| 	if (len >= XML_MAX_NAMELEN)
 | |
| 	    return(NULL);
 | |
|     }
 | |
|     *name = xmlStrndup(buf, len);
 | |
|     return(cur);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlGetSGMLCatalogEntryType:
 | |
|  * @name:  the entry name
 | |
|  *
 | |
|  * Get the Catalog entry type for a given SGML Catalog name
 | |
|  *
 | |
|  * Returns Catalog entry type
 | |
|  */
 | |
| static xmlCatalogEntryType
 | |
| xmlGetSGMLCatalogEntryType(const xmlChar *name) {
 | |
|     xmlCatalogEntryType type = XML_CATA_NONE;
 | |
|     if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
 | |
| 	type = SGML_CATA_SYSTEM;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
 | |
| 	type = SGML_CATA_PUBLIC;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
 | |
| 	type = SGML_CATA_DELEGATE;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
 | |
| 	type = SGML_CATA_ENTITY;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
 | |
| 	type = SGML_CATA_DOCTYPE;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
 | |
| 	type = SGML_CATA_LINKTYPE;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
 | |
| 	type = SGML_CATA_NOTATION;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
 | |
| 	type = SGML_CATA_SGMLDECL;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
 | |
| 	type = SGML_CATA_DOCUMENT;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
 | |
| 	type = SGML_CATA_CATALOG;
 | |
|     else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
 | |
| 	type = SGML_CATA_BASE;
 | |
|     return(type);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlParseSGMLCatalog:
 | |
|  * @catal:  the SGML Catalog
 | |
|  * @value:  the content of the SGML Catalog serialization
 | |
|  * @file:  the filepath for the catalog
 | |
|  * @super:  should this be handled as a Super Catalog in which case
 | |
|  *          parsing is not recursive
 | |
|  *
 | |
|  * Parse an SGML catalog content and fill up the @catal hash table with
 | |
|  * the new entries found.
 | |
|  *
 | |
|  * Returns 0 in case of success, -1 in case of error.
 | |
|  */
 | |
| static int
 | |
| xmlParseSGMLCatalog(xmlCatalogPtr catal, const xmlChar *value,
 | |
| 	            const char *file, int super) {
 | |
|     const xmlChar *cur = value;
 | |
|     xmlChar *base = NULL;
 | |
|     int res;
 | |
| 
 | |
|     if ((cur == NULL) || (file == NULL))
 | |
|         return(-1);
 | |
|     base = xmlStrdup((const xmlChar *) file);
 | |
| 
 | |
|     while ((cur != NULL) && (cur[0] != 0)) {
 | |
| 	SKIP_BLANKS;
 | |
| 	if (cur[0] == 0)
 | |
| 	    break;
 | |
| 	if ((cur[0] == '-') && (cur[1] == '-')) {
 | |
| 	    cur = xmlParseSGMLCatalogComment(cur);
 | |
| 	    if (cur == NULL) {
 | |
| 		/* error */
 | |
| 		break;
 | |
| 	    }
 | |
| 	} else {
 | |
| 	    xmlChar *sysid = NULL;
 | |
| 	    xmlChar *name = NULL;
 | |
| 	    xmlCatalogEntryType type = XML_CATA_NONE;
 | |
| 
 | |
| 	    cur = xmlParseSGMLCatalogName(cur, &name);
 | |
| 	    if (cur == NULL || name == NULL) {
 | |
| 		/* error */
 | |
| 		break;
 | |
| 	    }
 | |
| 	    if (!IS_BLANK_CH(*cur)) {
 | |
| 		/* error */
 | |
| 		xmlFree(name);
 | |
| 		break;
 | |
| 	    }
 | |
| 	    SKIP_BLANKS;
 | |
| 	    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
 | |
|                 type = SGML_CATA_SYSTEM;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
 | |
|                 type = SGML_CATA_PUBLIC;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
 | |
|                 type = SGML_CATA_DELEGATE;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
 | |
|                 type = SGML_CATA_ENTITY;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
 | |
|                 type = SGML_CATA_DOCTYPE;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
 | |
|                 type = SGML_CATA_LINKTYPE;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
 | |
|                 type = SGML_CATA_NOTATION;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
 | |
|                 type = SGML_CATA_SGMLDECL;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
 | |
|                 type = SGML_CATA_DOCUMENT;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
 | |
|                 type = SGML_CATA_CATALOG;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
 | |
|                 type = SGML_CATA_BASE;
 | |
| 	    else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
 | |
| 		xmlFree(name);
 | |
| 		cur = xmlParseSGMLCatalogName(cur, &name);
 | |
| 		if (name == NULL) {
 | |
| 		    /* error */
 | |
| 		    break;
 | |
| 		}
 | |
| 		xmlFree(name);
 | |
| 		continue;
 | |
| 	    }
 | |
| 	    xmlFree(name);
 | |
| 	    name = NULL;
 | |
| 
 | |
| 	    switch(type) {
 | |
| 		case SGML_CATA_ENTITY:
 | |
| 		    if (*cur == '%')
 | |
| 			type = SGML_CATA_PENTITY;
 | |
|                     /* Falls through. */
 | |
| 		case SGML_CATA_PENTITY:
 | |
| 		case SGML_CATA_DOCTYPE:
 | |
| 		case SGML_CATA_LINKTYPE:
 | |
| 		case SGML_CATA_NOTATION:
 | |
| 		    cur = xmlParseSGMLCatalogName(cur, &name);
 | |
| 		    if (cur == NULL) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    if (!IS_BLANK_CH(*cur)) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    SKIP_BLANKS;
 | |
| 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
 | |
| 		    if (cur == NULL) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case SGML_CATA_PUBLIC:
 | |
| 		case SGML_CATA_SYSTEM:
 | |
| 		case SGML_CATA_DELEGATE:
 | |
| 		    cur = xmlParseSGMLCatalogPubid(cur, &name);
 | |
| 		    if (cur == NULL) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    if (type != SGML_CATA_SYSTEM) {
 | |
| 		        xmlChar *normid;
 | |
| 
 | |
| 		        normid = xmlCatalogNormalizePublic(name);
 | |
| 		        if (normid != NULL) {
 | |
| 		            if (name != NULL)
 | |
| 		                xmlFree(name);
 | |
| 		            if (*normid != 0)
 | |
| 		                name = normid;
 | |
| 		            else {
 | |
| 		                xmlFree(normid);
 | |
| 		                name = NULL;
 | |
| 		            }
 | |
| 		        }
 | |
| 		    }
 | |
| 		    if (!IS_BLANK_CH(*cur)) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    SKIP_BLANKS;
 | |
| 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
 | |
| 		    if (cur == NULL) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		case SGML_CATA_BASE:
 | |
| 		case SGML_CATA_CATALOG:
 | |
| 		case SGML_CATA_DOCUMENT:
 | |
| 		case SGML_CATA_SGMLDECL:
 | |
| 		    cur = xmlParseSGMLCatalogPubid(cur, &sysid);
 | |
| 		    if (cur == NULL) {
 | |
| 			/* error */
 | |
| 			break;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		default:
 | |
| 		    break;
 | |
| 	    }
 | |
| 	    if (cur == NULL) {
 | |
| 		if (name != NULL)
 | |
| 		    xmlFree(name);
 | |
| 		if (sysid != NULL)
 | |
| 		    xmlFree(sysid);
 | |
| 		break;
 | |
| 	    } else if (type == SGML_CATA_BASE) {
 | |
| 		if (base != NULL)
 | |
| 		    xmlFree(base);
 | |
| 		base = xmlStrdup(sysid);
 | |
| 	    } else if ((type == SGML_CATA_PUBLIC) ||
 | |
| 		       (type == SGML_CATA_SYSTEM)) {
 | |
| 		xmlChar *filename;
 | |
| 
 | |
| 		filename = xmlBuildURI(sysid, base);
 | |
| 		if (filename != NULL) {
 | |
| 		    xmlCatalogEntryPtr entry;
 | |
| 
 | |
| 		    entry = xmlNewCatalogEntry(type, name, filename,
 | |
| 			                       NULL, XML_CATA_PREFER_NONE, NULL);
 | |
| 		    res = xmlHashAddEntry(catal->sgml, name, entry);
 | |
| 		    if (res < 0) {
 | |
| 			xmlFreeCatalogEntry(entry, NULL);
 | |
| 		    }
 | |
| 		    xmlFree(filename);
 | |
| 		}
 | |
| 
 | |
| 	    } else if (type == SGML_CATA_CATALOG) {
 | |
| 		if (super) {
 | |
| 		    xmlCatalogEntryPtr entry;
 | |
| 
 | |
| 		    entry = xmlNewCatalogEntry(type, sysid, NULL, NULL,
 | |
| 			                       XML_CATA_PREFER_NONE, NULL);
 | |
| 		    res = xmlHashAddEntry(catal->sgml, sysid, entry);
 | |
| 		    if (res < 0) {
 | |
| 			xmlFreeCatalogEntry(entry, NULL);
 | |
| 		    }
 | |
| 		} else {
 | |
| 		    xmlChar *filename;
 | |
| 
 | |
| 		    filename = xmlBuildURI(sysid, base);
 | |
| 		    if (filename != NULL) {
 | |
| 			xmlExpandCatalog(catal, (const char *)filename);
 | |
| 			xmlFree(filename);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    /*
 | |
| 	     * drop anything else we won't handle it
 | |
| 	     */
 | |
| 	    if (name != NULL)
 | |
| 		xmlFree(name);
 | |
| 	    if (sysid != NULL)
 | |
| 		xmlFree(sysid);
 | |
| 	}
 | |
|     }
 | |
|     if (base != NULL)
 | |
| 	xmlFree(base);
 | |
|     if (cur == NULL)
 | |
| 	return(-1);
 | |
|     return(0);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			SGML Catalog handling				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogGetSGMLPublic:
 | |
|  * @catal:  an SGML catalog hash
 | |
|  * @pubID:  the public ID string
 | |
|  *
 | |
|  * Try to lookup the catalog local reference associated to a public ID
 | |
|  *
 | |
|  * Returns the local resource if found or NULL otherwise.
 | |
|  */
 | |
| static const xmlChar *
 | |
| xmlCatalogGetSGMLPublic(xmlHashTablePtr catal, const xmlChar *pubID) {
 | |
|     xmlCatalogEntryPtr entry;
 | |
|     xmlChar *normid;
 | |
| 
 | |
|     if (catal == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     normid = xmlCatalogNormalizePublic(pubID);
 | |
|     if (normid != NULL)
 | |
|         pubID = (*normid != 0 ? normid : NULL);
 | |
| 
 | |
|     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID);
 | |
|     if (entry == NULL) {
 | |
| 	if (normid != NULL)
 | |
| 	    xmlFree(normid);
 | |
| 	return(NULL);
 | |
|     }
 | |
|     if (entry->type == SGML_CATA_PUBLIC) {
 | |
| 	if (normid != NULL)
 | |
| 	    xmlFree(normid);
 | |
| 	return(entry->URL);
 | |
|     }
 | |
|     if (normid != NULL)
 | |
|         xmlFree(normid);
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogGetSGMLSystem:
 | |
|  * @catal:  an SGML catalog hash
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Try to lookup the catalog local reference for a system ID
 | |
|  *
 | |
|  * Returns the local resource if found or NULL otherwise.
 | |
|  */
 | |
| static const xmlChar *
 | |
| xmlCatalogGetSGMLSystem(xmlHashTablePtr catal, const xmlChar *sysID) {
 | |
|     xmlCatalogEntryPtr entry;
 | |
| 
 | |
|     if (catal == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, sysID);
 | |
|     if (entry == NULL)
 | |
| 	return(NULL);
 | |
|     if (entry->type == SGML_CATA_SYSTEM)
 | |
| 	return(entry->URL);
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogSGMLResolve:
 | |
|  * @catal:  the SGML catalog
 | |
|  * @pubID:  the public ID string
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found
 | |
|  */
 | |
| static const xmlChar *
 | |
| xmlCatalogSGMLResolve(xmlCatalogPtr catal, const xmlChar *pubID,
 | |
| 	              const xmlChar *sysID) {
 | |
|     const xmlChar *ret = NULL;
 | |
| 
 | |
|     if (catal->sgml == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (pubID != NULL)
 | |
| 	ret = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
 | |
|     if (ret != NULL)
 | |
| 	return(ret);
 | |
|     if (sysID != NULL)
 | |
| 	ret = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
 | |
|     if (ret != NULL)
 | |
| 	return(ret);
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Specific Public interfaces			*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlLoadSGMLSuperCatalog:
 | |
|  * @filename:  a file path
 | |
|  *
 | |
|  * Load an SGML super catalog. It won't expand CATALOG or DELEGATE
 | |
|  * references. This is only needed for manipulating SGML Super Catalogs
 | |
|  * like adding and removing CATALOG or DELEGATE entries.
 | |
|  *
 | |
|  * Returns the catalog parsed or NULL in case of error
 | |
|  */
 | |
| xmlCatalogPtr
 | |
| xmlLoadSGMLSuperCatalog(const char *filename)
 | |
| {
 | |
|     xmlChar *content;
 | |
|     xmlCatalogPtr catal;
 | |
|     int ret;
 | |
| 
 | |
|     content = xmlLoadFileContent(filename);
 | |
|     if (content == NULL)
 | |
|         return(NULL);
 | |
| 
 | |
|     catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
 | |
|     if (catal == NULL) {
 | |
| 	xmlFree(content);
 | |
| 	return(NULL);
 | |
|     }
 | |
| 
 | |
|     ret = xmlParseSGMLCatalog(catal, content, filename, 1);
 | |
|     xmlFree(content);
 | |
|     if (ret < 0) {
 | |
| 	xmlFreeCatalog(catal);
 | |
| 	return(NULL);
 | |
|     }
 | |
|     return (catal);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlLoadACatalog:
 | |
|  * @filename:  a file path
 | |
|  *
 | |
|  * Load the catalog and build the associated data structures.
 | |
|  * This can be either an XML Catalog or an SGML Catalog
 | |
|  * It will recurse in SGML CATALOG entries. On the other hand XML
 | |
|  * Catalogs are not handled recursively.
 | |
|  *
 | |
|  * Returns the catalog parsed or NULL in case of error
 | |
|  */
 | |
| xmlCatalogPtr
 | |
| xmlLoadACatalog(const char *filename)
 | |
| {
 | |
|     xmlChar *content;
 | |
|     xmlChar *first;
 | |
|     xmlCatalogPtr catal;
 | |
|     int ret;
 | |
| 
 | |
|     content = xmlLoadFileContent(filename);
 | |
|     if (content == NULL)
 | |
|         return(NULL);
 | |
| 
 | |
| 
 | |
|     first = content;
 | |
| 
 | |
|     while ((*first != 0) && (*first != '-') && (*first != '<') &&
 | |
| 	   (!(((*first >= 'A') && (*first <= 'Z')) ||
 | |
| 	      ((*first >= 'a') && (*first <= 'z')))))
 | |
| 	first++;
 | |
| 
 | |
|     if (*first != '<') {
 | |
| 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
 | |
| 	if (catal == NULL) {
 | |
| 	    xmlFree(content);
 | |
| 	    return(NULL);
 | |
| 	}
 | |
|         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
 | |
| 	if (ret < 0) {
 | |
| 	    xmlFreeCatalog(catal);
 | |
| 	    xmlFree(content);
 | |
| 	    return(NULL);
 | |
| 	}
 | |
|     } else {
 | |
| 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE, xmlCatalogDefaultPrefer);
 | |
| 	if (catal == NULL) {
 | |
| 	    xmlFree(content);
 | |
| 	    return(NULL);
 | |
| 	}
 | |
|         catal->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
 | |
| 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
 | |
|     }
 | |
|     xmlFree(content);
 | |
|     return (catal);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlExpandCatalog:
 | |
|  * @catal:  a catalog
 | |
|  * @filename:  a file path
 | |
|  *
 | |
|  * Load the catalog and expand the existing catal structure.
 | |
|  * This can be either an XML Catalog or an SGML Catalog
 | |
|  *
 | |
|  * Returns 0 in case of success, -1 in case of error
 | |
|  */
 | |
| static int
 | |
| xmlExpandCatalog(xmlCatalogPtr catal, const char *filename)
 | |
| {
 | |
|     int ret;
 | |
| 
 | |
|     if ((catal == NULL) || (filename == NULL))
 | |
| 	return(-1);
 | |
| 
 | |
| 
 | |
|     if (catal->type == XML_SGML_CATALOG_TYPE) {
 | |
| 	xmlChar *content;
 | |
| 
 | |
| 	content = xmlLoadFileContent(filename);
 | |
| 	if (content == NULL)
 | |
| 	    return(-1);
 | |
| 
 | |
|         ret = xmlParseSGMLCatalog(catal, content, filename, 0);
 | |
| 	if (ret < 0) {
 | |
| 	    xmlFree(content);
 | |
| 	    return(-1);
 | |
| 	}
 | |
| 	xmlFree(content);
 | |
|     } else {
 | |
| 	xmlCatalogEntryPtr tmp, cur;
 | |
| 	tmp = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
 | |
| 		       NULL, BAD_CAST filename, xmlCatalogDefaultPrefer, NULL);
 | |
| 
 | |
| 	cur = catal->xml;
 | |
| 	if (cur == NULL) {
 | |
| 	    catal->xml = tmp;
 | |
| 	} else {
 | |
| 	    while (cur->next != NULL) cur = cur->next;
 | |
| 	    cur->next = tmp;
 | |
| 	}
 | |
|     }
 | |
|     return (0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlACatalogResolveSystem:
 | |
|  * @catal:  a Catalog
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Try to lookup the catalog resource for a system ID
 | |
|  *
 | |
|  * Returns the resource if found or NULL otherwise, the value returned
 | |
|  *      must be freed by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlACatalogResolveSystem(xmlCatalogPtr catal, const xmlChar *sysID) {
 | |
|     xmlChar *ret = NULL;
 | |
| 
 | |
|     if ((sysID == NULL) || (catal == NULL))
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Resolve sysID %s\n", sysID);
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
| 	ret = xmlCatalogListXMLResolve(catal->xml, NULL, sysID);
 | |
| 	if (ret == XML_CATAL_BREAK)
 | |
| 	    ret = NULL;
 | |
|     } else {
 | |
| 	const xmlChar *sgml;
 | |
| 
 | |
| 	sgml = xmlCatalogGetSGMLSystem(catal->sgml, sysID);
 | |
| 	if (sgml != NULL)
 | |
| 	    ret = xmlStrdup(sgml);
 | |
|     }
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlACatalogResolvePublic:
 | |
|  * @catal:  a Catalog
 | |
|  * @pubID:  the public ID string
 | |
|  *
 | |
|  * Try to lookup the catalog local reference associated to a public ID in that catalog
 | |
|  *
 | |
|  * Returns the local resource if found or NULL otherwise, the value returned
 | |
|  *      must be freed by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlACatalogResolvePublic(xmlCatalogPtr catal, const xmlChar *pubID) {
 | |
|     xmlChar *ret = NULL;
 | |
| 
 | |
|     if ((pubID == NULL) || (catal == NULL))
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Resolve pubID %s\n", pubID);
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
| 	ret = xmlCatalogListXMLResolve(catal->xml, pubID, NULL);
 | |
| 	if (ret == XML_CATAL_BREAK)
 | |
| 	    ret = NULL;
 | |
|     } else {
 | |
| 	const xmlChar *sgml;
 | |
| 
 | |
| 	sgml = xmlCatalogGetSGMLPublic(catal->sgml, pubID);
 | |
| 	if (sgml != NULL)
 | |
| 	    ret = xmlStrdup(sgml);
 | |
|     }
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlACatalogResolve:
 | |
|  * @catal:  a Catalog
 | |
|  * @pubID:  the public ID string
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found, it must be freed
 | |
|  *      by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlACatalogResolve(xmlCatalogPtr catal, const xmlChar * pubID,
 | |
|                    const xmlChar * sysID)
 | |
| {
 | |
|     xmlChar *ret = NULL;
 | |
| 
 | |
|     if (((pubID == NULL) && (sysID == NULL)) || (catal == NULL))
 | |
|         return (NULL);
 | |
| 
 | |
|     if (xmlDebugCatalogs) {
 | |
|          if ((pubID != NULL) && (sysID != NULL)) {
 | |
|              xmlGenericError(xmlGenericErrorContext,
 | |
|                              "Resolve: pubID %s sysID %s\n", pubID, sysID);
 | |
|          } else if (pubID != NULL) {
 | |
|              xmlGenericError(xmlGenericErrorContext,
 | |
|                              "Resolve: pubID %s\n", pubID);
 | |
|          } else {
 | |
|              xmlGenericError(xmlGenericErrorContext,
 | |
|                              "Resolve: sysID %s\n", sysID);
 | |
|          }
 | |
|     }
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
|         ret = xmlCatalogListXMLResolve(catal->xml, pubID, sysID);
 | |
| 	if (ret == XML_CATAL_BREAK)
 | |
| 	    ret = NULL;
 | |
|     } else {
 | |
|         const xmlChar *sgml;
 | |
| 
 | |
|         sgml = xmlCatalogSGMLResolve(catal, pubID, sysID);
 | |
|         if (sgml != NULL)
 | |
|             ret = xmlStrdup(sgml);
 | |
|     }
 | |
|     return (ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlACatalogResolveURI:
 | |
|  * @catal:  a Catalog
 | |
|  * @URI:  the URI
 | |
|  *
 | |
|  * Do a complete resolution lookup of an URI
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found, it must be freed
 | |
|  *      by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlACatalogResolveURI(xmlCatalogPtr catal, const xmlChar *URI) {
 | |
|     xmlChar *ret = NULL;
 | |
| 
 | |
|     if ((URI == NULL) || (catal == NULL))
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Resolve URI %s\n", URI);
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
| 	ret = xmlCatalogListXMLResolveURI(catal->xml, URI);
 | |
| 	if (ret == XML_CATAL_BREAK)
 | |
| 	    ret = NULL;
 | |
|     } else {
 | |
| 	const xmlChar *sgml;
 | |
| 
 | |
| 	sgml = xmlCatalogSGMLResolve(catal, NULL, URI);
 | |
| 	if (sgml != NULL)
 | |
|             ret = xmlStrdup(sgml);
 | |
|     }
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| #ifdef LIBXML_OUTPUT_ENABLED
 | |
| /**
 | |
|  * xmlACatalogDump:
 | |
|  * @catal:  a Catalog
 | |
|  * @out:  the file.
 | |
|  *
 | |
|  * Dump the given catalog to the given file.
 | |
|  */
 | |
| void
 | |
| xmlACatalogDump(xmlCatalogPtr catal, FILE *out) {
 | |
|     if ((out == NULL) || (catal == NULL))
 | |
| 	return;
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
| 	xmlDumpXMLCatalog(out, catal->xml);
 | |
|     } else {
 | |
| 	xmlHashScan(catal->sgml, xmlCatalogDumpEntry, out);
 | |
|     }
 | |
| }
 | |
| #endif /* LIBXML_OUTPUT_ENABLED */
 | |
| 
 | |
| /**
 | |
|  * xmlACatalogAdd:
 | |
|  * @catal:  a Catalog
 | |
|  * @type:  the type of record to add to the catalog
 | |
|  * @orig:  the system, public or prefix to match
 | |
|  * @replace:  the replacement value for the match
 | |
|  *
 | |
|  * Add an entry in the catalog, it may overwrite existing but
 | |
|  * different entries.
 | |
|  *
 | |
|  * Returns 0 if successful, -1 otherwise
 | |
|  */
 | |
| int
 | |
| xmlACatalogAdd(xmlCatalogPtr catal, const xmlChar * type,
 | |
|               const xmlChar * orig, const xmlChar * replace)
 | |
| {
 | |
|     int res = -1;
 | |
| 
 | |
|     if (catal == NULL)
 | |
| 	return(-1);
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
|         res = xmlAddXMLCatalog(catal->xml, type, orig, replace);
 | |
|     } else {
 | |
|         xmlCatalogEntryType cattype;
 | |
| 
 | |
|         cattype = xmlGetSGMLCatalogEntryType(type);
 | |
|         if (cattype != XML_CATA_NONE) {
 | |
|             xmlCatalogEntryPtr entry;
 | |
| 
 | |
|             entry = xmlNewCatalogEntry(cattype, orig, replace, NULL,
 | |
|                                        XML_CATA_PREFER_NONE, NULL);
 | |
| 	    if (catal->sgml == NULL)
 | |
| 		catal->sgml = xmlHashCreate(10);
 | |
|             res = xmlHashAddEntry(catal->sgml, orig, entry);
 | |
|             if (res < 0)
 | |
|                 xmlFreeCatalogEntry(entry, NULL);
 | |
|         }
 | |
|     }
 | |
|     return (res);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlACatalogRemove:
 | |
|  * @catal:  a Catalog
 | |
|  * @value:  the value to remove
 | |
|  *
 | |
|  * Remove an entry from the catalog
 | |
|  *
 | |
|  * Returns the number of entries removed if successful, -1 otherwise
 | |
|  */
 | |
| int
 | |
| xmlACatalogRemove(xmlCatalogPtr catal, const xmlChar *value) {
 | |
|     int res = -1;
 | |
| 
 | |
|     if ((catal == NULL) || (value == NULL))
 | |
| 	return(-1);
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
| 	res = xmlDelXMLCatalog(catal->xml, value);
 | |
|     } else {
 | |
| 	res = xmlHashRemoveEntry(catal->sgml, value, xmlFreeCatalogEntry);
 | |
| 	if (res == 0)
 | |
| 	    res = 1;
 | |
|     }
 | |
|     return(res);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlNewCatalog:
 | |
|  * @sgml:  should this create an SGML catalog
 | |
|  *
 | |
|  * create a new Catalog.
 | |
|  *
 | |
|  * Returns the xmlCatalogPtr or NULL in case of error
 | |
|  */
 | |
| xmlCatalogPtr
 | |
| xmlNewCatalog(int sgml) {
 | |
|     xmlCatalogPtr catal = NULL;
 | |
| 
 | |
|     if (sgml) {
 | |
| 	catal = xmlCreateNewCatalog(XML_SGML_CATALOG_TYPE,
 | |
| 		                    xmlCatalogDefaultPrefer);
 | |
|         if ((catal != NULL) && (catal->sgml == NULL))
 | |
| 	    catal->sgml = xmlHashCreate(10);
 | |
|     } else
 | |
| 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
 | |
| 		                    xmlCatalogDefaultPrefer);
 | |
|     return(catal);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogIsEmpty:
 | |
|  * @catal:  should this create an SGML catalog
 | |
|  *
 | |
|  * Check is a catalog is empty
 | |
|  *
 | |
|  * Returns 1 if the catalog is empty, 0 if not, amd -1 in case of error.
 | |
|  */
 | |
| int
 | |
| xmlCatalogIsEmpty(xmlCatalogPtr catal) {
 | |
|     if (catal == NULL)
 | |
| 	return(-1);
 | |
| 
 | |
|     if (catal->type == XML_XML_CATALOG_TYPE) {
 | |
| 	if (catal->xml == NULL)
 | |
| 	    return(1);
 | |
| 	if ((catal->xml->type != XML_CATA_CATALOG) &&
 | |
| 	    (catal->xml->type != XML_CATA_BROKEN_CATALOG))
 | |
| 	    return(-1);
 | |
| 	if (catal->xml->children == NULL)
 | |
| 	    return(1);
 | |
|         return(0);
 | |
|     } else {
 | |
| 	int res;
 | |
| 
 | |
| 	if (catal->sgml == NULL)
 | |
| 	    return(1);
 | |
| 	res = xmlHashSize(catal->sgml);
 | |
| 	if (res == 0)
 | |
| 	    return(1);
 | |
| 	if (res < 0)
 | |
| 	    return(-1);
 | |
|     }
 | |
|     return(0);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *   Public interfaces manipulating the global shared default catalog	*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlInitializeCatalogData:
 | |
|  *
 | |
|  * Do the catalog initialization only of global data, doesn't try to load
 | |
|  * any catalog actually.
 | |
|  * this function is not thread safe, catalog initialization should
 | |
|  * preferably be done once at startup
 | |
|  */
 | |
| static void
 | |
| xmlInitializeCatalogData(void) {
 | |
|     if (xmlCatalogInitialized != 0)
 | |
| 	return;
 | |
| 
 | |
|     if (getenv("XML_DEBUG_CATALOG"))
 | |
| 	xmlDebugCatalogs = 1;
 | |
|     xmlCatalogMutex = xmlNewRMutex();
 | |
| 
 | |
|     xmlCatalogInitialized = 1;
 | |
| }
 | |
| /**
 | |
|  * xmlInitializeCatalog:
 | |
|  *
 | |
|  * Do the catalog initialization.
 | |
|  * this function is not thread safe, catalog initialization should
 | |
|  * preferably be done once at startup
 | |
|  */
 | |
| void
 | |
| xmlInitializeCatalog(void) {
 | |
|     if (xmlCatalogInitialized != 0)
 | |
| 	return;
 | |
| 
 | |
|     xmlInitializeCatalogData();
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
| 
 | |
|     if (getenv("XML_DEBUG_CATALOG"))
 | |
| 	xmlDebugCatalogs = 1;
 | |
| 
 | |
|     if (xmlDefaultCatalog == NULL) {
 | |
| 	const char *catalogs;
 | |
| 	char *path;
 | |
| 	const char *cur, *paths;
 | |
| 	xmlCatalogPtr catal;
 | |
| 	xmlCatalogEntryPtr *nextent;
 | |
| 
 | |
| 	catalogs = (const char *) getenv("XML_CATALOG_FILES");
 | |
| 	if (catalogs == NULL)
 | |
| #if defined(_WIN32) && defined(_MSC_VER)
 | |
|     {
 | |
| 		void* hmodule;
 | |
| 		hmodule = GetModuleHandleA("libxml2.dll");
 | |
| 		if (hmodule == NULL)
 | |
| 			hmodule = GetModuleHandleA(NULL);
 | |
| 		if (hmodule != NULL) {
 | |
| 			char buf[256];
 | |
| 			unsigned long len = GetModuleFileNameA(hmodule, buf, 255);
 | |
| 			if (len != 0) {
 | |
| 				char* p = &(buf[len]);
 | |
| 				while (*p != '\\' && p > buf)
 | |
| 					p--;
 | |
| 				if (p != buf) {
 | |
| 					xmlChar* uri;
 | |
| 					strncpy(p, "\\..\\etc\\catalog", 255 - (p - buf));
 | |
| 					uri = xmlCanonicPath((const xmlChar*)buf);
 | |
| 					if (uri != NULL) {
 | |
| 						strncpy(XML_XML_DEFAULT_CATALOG, (char* )uri, 255);
 | |
| 						xmlFree(uri);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		catalogs = XML_XML_DEFAULT_CATALOG;
 | |
|     }
 | |
| #else
 | |
| 	    catalogs = XML_XML_DEFAULT_CATALOG;
 | |
| #endif
 | |
| 
 | |
| 	catal = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
 | |
| 		xmlCatalogDefaultPrefer);
 | |
| 	if (catal != NULL) {
 | |
| 	    /* the XML_CATALOG_FILES envvar is allowed to contain a
 | |
| 	       space-separated list of entries. */
 | |
| 	    cur = catalogs;
 | |
| 	    nextent = &catal->xml;
 | |
| 	    while (*cur != '\0') {
 | |
| 		while (xmlIsBlank_ch(*cur))
 | |
| 		    cur++;
 | |
| 		if (*cur != 0) {
 | |
| 		    paths = cur;
 | |
| 		    while ((*cur != 0) && (!xmlIsBlank_ch(*cur)))
 | |
| 			cur++;
 | |
| 		    path = (char *) xmlStrndup((const xmlChar *)paths, cur - paths);
 | |
| 		    if (path != NULL) {
 | |
| 			*nextent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
 | |
| 				NULL, BAD_CAST path, xmlCatalogDefaultPrefer, NULL);
 | |
| 			if (*nextent != NULL)
 | |
| 			    nextent = &((*nextent)->next);
 | |
| 			xmlFree(path);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    xmlDefaultCatalog = catal;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * xmlLoadCatalog:
 | |
|  * @filename:  a file path
 | |
|  *
 | |
|  * Load the catalog and makes its definitions effective for the default
 | |
|  * external entity loader. It will recurse in SGML CATALOG entries.
 | |
|  * this function is not thread safe, catalog initialization should
 | |
|  * preferably be done once at startup
 | |
|  *
 | |
|  * Returns 0 in case of success -1 in case of error
 | |
|  */
 | |
| int
 | |
| xmlLoadCatalog(const char *filename)
 | |
| {
 | |
|     int ret;
 | |
|     xmlCatalogPtr catal;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalogData();
 | |
| 
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
| 
 | |
|     if (xmlDefaultCatalog == NULL) {
 | |
| 	catal = xmlLoadACatalog(filename);
 | |
| 	if (catal == NULL) {
 | |
| 	    xmlRMutexUnlock(xmlCatalogMutex);
 | |
| 	    return(-1);
 | |
| 	}
 | |
| 
 | |
| 	xmlDefaultCatalog = catal;
 | |
| 	xmlRMutexUnlock(xmlCatalogMutex);
 | |
| 	return(0);
 | |
|     }
 | |
| 
 | |
|     ret = xmlExpandCatalog(xmlDefaultCatalog, filename);
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlLoadCatalogs:
 | |
|  * @pathss:  a list of directories separated by a colon or a space.
 | |
|  *
 | |
|  * Load the catalogs and makes their definitions effective for the default
 | |
|  * external entity loader.
 | |
|  * this function is not thread safe, catalog initialization should
 | |
|  * preferably be done once at startup
 | |
|  */
 | |
| void
 | |
| xmlLoadCatalogs(const char *pathss) {
 | |
|     const char *cur;
 | |
|     const char *paths;
 | |
|     xmlChar *path;
 | |
| #ifdef _WIN32
 | |
|     int i, iLen;
 | |
| #endif
 | |
| 
 | |
|     if (pathss == NULL)
 | |
| 	return;
 | |
| 
 | |
|     cur = pathss;
 | |
|     while (*cur != 0) {
 | |
| 	while (xmlIsBlank_ch(*cur)) cur++;
 | |
| 	if (*cur != 0) {
 | |
| 	    paths = cur;
 | |
| 	    while ((*cur != 0) && (*cur != PATH_SEPARATOR) && (!xmlIsBlank_ch(*cur)))
 | |
| 		cur++;
 | |
| 	    path = xmlStrndup((const xmlChar *)paths, cur - paths);
 | |
| 	    if (path != NULL) {
 | |
| #ifdef _WIN32
 | |
|         iLen = strlen((const char*)path);
 | |
|         for(i = 0; i < iLen; i++) {
 | |
|             if(path[i] == '\\') {
 | |
|                 path[i] = '/';
 | |
|             }
 | |
|         }
 | |
| #endif
 | |
| 		xmlLoadCatalog((const char *) path);
 | |
| 		xmlFree(path);
 | |
| 	    }
 | |
| 	}
 | |
| 	while (*cur == PATH_SEPARATOR)
 | |
| 	    cur++;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogCleanup:
 | |
|  *
 | |
|  * Free up all the memory associated with catalogs
 | |
|  */
 | |
| void
 | |
| xmlCatalogCleanup(void) {
 | |
|     if (xmlCatalogInitialized == 0)
 | |
|         return;
 | |
| 
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Catalogs cleanup\n");
 | |
|     if (xmlCatalogXMLFiles != NULL)
 | |
| 	xmlHashFree(xmlCatalogXMLFiles, xmlFreeCatalogHashEntryList);
 | |
|     xmlCatalogXMLFiles = NULL;
 | |
|     if (xmlDefaultCatalog != NULL)
 | |
| 	xmlFreeCatalog(xmlDefaultCatalog);
 | |
|     xmlDefaultCatalog = NULL;
 | |
|     xmlDebugCatalogs = 0;
 | |
|     xmlCatalogInitialized = 0;
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
|     xmlFreeRMutex(xmlCatalogMutex);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogResolveSystem:
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Try to lookup the catalog resource for a system ID
 | |
|  *
 | |
|  * Returns the resource if found or NULL otherwise, the value returned
 | |
|  *      must be freed by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlCatalogResolveSystem(const xmlChar *sysID) {
 | |
|     xmlChar *ret;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     ret = xmlACatalogResolveSystem(xmlDefaultCatalog, sysID);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogResolvePublic:
 | |
|  * @pubID:  the public ID string
 | |
|  *
 | |
|  * Try to lookup the catalog reference associated to a public ID
 | |
|  *
 | |
|  * Returns the resource if found or NULL otherwise, the value returned
 | |
|  *      must be freed by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlCatalogResolvePublic(const xmlChar *pubID) {
 | |
|     xmlChar *ret;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     ret = xmlACatalogResolvePublic(xmlDefaultCatalog, pubID);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogResolve:
 | |
|  * @pubID:  the public ID string
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found, it must be freed
 | |
|  *      by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlCatalogResolve(const xmlChar *pubID, const xmlChar *sysID) {
 | |
|     xmlChar *ret;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     ret = xmlACatalogResolve(xmlDefaultCatalog, pubID, sysID);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogResolveURI:
 | |
|  * @URI:  the URI
 | |
|  *
 | |
|  * Do a complete resolution lookup of an URI
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found, it must be freed
 | |
|  *      by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlCatalogResolveURI(const xmlChar *URI) {
 | |
|     xmlChar *ret;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     ret = xmlACatalogResolveURI(xmlDefaultCatalog, URI);
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| #ifdef LIBXML_OUTPUT_ENABLED
 | |
| /**
 | |
|  * xmlCatalogDump:
 | |
|  * @out:  the file.
 | |
|  *
 | |
|  * Dump all the global catalog content to the given file.
 | |
|  */
 | |
| void
 | |
| xmlCatalogDump(FILE *out) {
 | |
|     if (out == NULL)
 | |
| 	return;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     xmlACatalogDump(xmlDefaultCatalog, out);
 | |
| }
 | |
| #endif /* LIBXML_OUTPUT_ENABLED */
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogAdd:
 | |
|  * @type:  the type of record to add to the catalog
 | |
|  * @orig:  the system, public or prefix to match
 | |
|  * @replace:  the replacement value for the match
 | |
|  *
 | |
|  * Add an entry in the catalog, it may overwrite existing but
 | |
|  * different entries.
 | |
|  * If called before any other catalog routine, allows to override the
 | |
|  * default shared catalog put in place by xmlInitializeCatalog();
 | |
|  *
 | |
|  * Returns 0 if successful, -1 otherwise
 | |
|  */
 | |
| int
 | |
| xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
 | |
|     int res = -1;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalogData();
 | |
| 
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
|     /*
 | |
|      * Specific case where one want to override the default catalog
 | |
|      * put in place by xmlInitializeCatalog();
 | |
|      */
 | |
|     if ((xmlDefaultCatalog == NULL) &&
 | |
| 	(xmlStrEqual(type, BAD_CAST "catalog"))) {
 | |
| 	xmlDefaultCatalog = xmlCreateNewCatalog(XML_XML_CATALOG_TYPE,
 | |
| 		                          xmlCatalogDefaultPrefer);
 | |
| 	if (xmlDefaultCatalog != NULL) {
 | |
| 	   xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
 | |
| 				    orig, NULL,  xmlCatalogDefaultPrefer, NULL);
 | |
| 	}
 | |
| 	xmlRMutexUnlock(xmlCatalogMutex);
 | |
| 	return(0);
 | |
|     }
 | |
| 
 | |
|     res = xmlACatalogAdd(xmlDefaultCatalog, type, orig, replace);
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
|     return(res);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogRemove:
 | |
|  * @value:  the value to remove
 | |
|  *
 | |
|  * Remove an entry from the catalog
 | |
|  *
 | |
|  * Returns the number of entries removed if successful, -1 otherwise
 | |
|  */
 | |
| int
 | |
| xmlCatalogRemove(const xmlChar *value) {
 | |
|     int res;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
|     res = xmlACatalogRemove(xmlDefaultCatalog, value);
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
|     return(res);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogConvert:
 | |
|  *
 | |
|  * Convert all the SGML catalog entries as XML ones
 | |
|  *
 | |
|  * Returns the number of entries converted if successful, -1 otherwise
 | |
|  */
 | |
| int
 | |
| xmlCatalogConvert(void) {
 | |
|     int res = -1;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     xmlRMutexLock(xmlCatalogMutex);
 | |
|     res = xmlConvertSGMLCatalog(xmlDefaultCatalog);
 | |
|     xmlRMutexUnlock(xmlCatalogMutex);
 | |
|     return(res);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *	Public interface manipulating the common preferences		*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogGetDefaults:
 | |
|  *
 | |
|  * Used to get the user preference w.r.t. to what catalogs should
 | |
|  * be accepted
 | |
|  *
 | |
|  * Returns the current xmlCatalogAllow value
 | |
|  */
 | |
| xmlCatalogAllow
 | |
| xmlCatalogGetDefaults(void) {
 | |
|     return(xmlCatalogDefaultAllow);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogSetDefaults:
 | |
|  * @allow:  what catalogs should be accepted
 | |
|  *
 | |
|  * Used to set the user preference w.r.t. to what catalogs should
 | |
|  * be accepted
 | |
|  */
 | |
| void
 | |
| xmlCatalogSetDefaults(xmlCatalogAllow allow) {
 | |
|     if (xmlDebugCatalogs) {
 | |
| 	switch (allow) {
 | |
| 	    case XML_CATA_ALLOW_NONE:
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Disabling catalog usage\n");
 | |
| 		break;
 | |
| 	    case XML_CATA_ALLOW_GLOBAL:
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Allowing only global catalogs\n");
 | |
| 		break;
 | |
| 	    case XML_CATA_ALLOW_DOCUMENT:
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Allowing only catalogs from the document\n");
 | |
| 		break;
 | |
| 	    case XML_CATA_ALLOW_ALL:
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Allowing all catalogs\n");
 | |
| 		break;
 | |
| 	}
 | |
|     }
 | |
|     xmlCatalogDefaultAllow = allow;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogSetDefaultPrefer:
 | |
|  * @prefer:  the default preference for delegation
 | |
|  *
 | |
|  * Allows to set the preference between public and system for deletion
 | |
|  * in XML Catalog resolution. C.f. section 4.1.1 of the spec
 | |
|  * Values accepted are XML_CATA_PREFER_PUBLIC or XML_CATA_PREFER_SYSTEM
 | |
|  *
 | |
|  * Returns the previous value of the default preference for delegation
 | |
|  */
 | |
| xmlCatalogPrefer
 | |
| xmlCatalogSetDefaultPrefer(xmlCatalogPrefer prefer) {
 | |
|     xmlCatalogPrefer ret = xmlCatalogDefaultPrefer;
 | |
| 
 | |
|     if (prefer == XML_CATA_PREFER_NONE)
 | |
| 	return(ret);
 | |
| 
 | |
|     if (xmlDebugCatalogs) {
 | |
| 	switch (prefer) {
 | |
| 	    case XML_CATA_PREFER_PUBLIC:
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Setting catalog preference to PUBLIC\n");
 | |
| 		break;
 | |
| 	    case XML_CATA_PREFER_SYSTEM:
 | |
| 		xmlGenericError(xmlGenericErrorContext,
 | |
| 			"Setting catalog preference to SYSTEM\n");
 | |
| 		break;
 | |
| 	    default:
 | |
| 		return(ret);
 | |
| 	}
 | |
|     }
 | |
|     xmlCatalogDefaultPrefer = prefer;
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogSetDebug:
 | |
|  * @level:  the debug level of catalogs required
 | |
|  *
 | |
|  * Used to set the debug level for catalog operation, 0 disable
 | |
|  * debugging, 1 enable it
 | |
|  *
 | |
|  * Returns the previous value of the catalog debugging level
 | |
|  */
 | |
| int
 | |
| xmlCatalogSetDebug(int level) {
 | |
|     int ret = xmlDebugCatalogs;
 | |
| 
 | |
|     if (level <= 0)
 | |
|         xmlDebugCatalogs = 0;
 | |
|     else
 | |
| 	xmlDebugCatalogs = level;
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *   Minimal interfaces used for per-document catalogs by the parser	*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogFreeLocal:
 | |
|  * @catalogs:  a document's list of catalogs
 | |
|  *
 | |
|  * Free up the memory associated to the catalog list
 | |
|  */
 | |
| void
 | |
| xmlCatalogFreeLocal(void *catalogs) {
 | |
|     xmlCatalogEntryPtr catal;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     catal = (xmlCatalogEntryPtr) catalogs;
 | |
|     if (catal != NULL)
 | |
| 	xmlFreeCatalogEntryList(catal);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogAddLocal:
 | |
|  * @catalogs:  a document's list of catalogs
 | |
|  * @URL:  the URL to a new local catalog
 | |
|  *
 | |
|  * Add the new entry to the catalog list
 | |
|  *
 | |
|  * Returns the updated list
 | |
|  */
 | |
| void *
 | |
| xmlCatalogAddLocal(void *catalogs, const xmlChar *URL) {
 | |
|     xmlCatalogEntryPtr catal, add;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     if (URL == NULL)
 | |
| 	return(catalogs);
 | |
| 
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Adding document catalog %s\n", URL);
 | |
| 
 | |
|     add = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, URL, NULL,
 | |
| 	                     xmlCatalogDefaultPrefer, NULL);
 | |
|     if (add == NULL)
 | |
| 	return(catalogs);
 | |
| 
 | |
|     catal = (xmlCatalogEntryPtr) catalogs;
 | |
|     if (catal == NULL)
 | |
| 	return((void *) add);
 | |
| 
 | |
|     while (catal->next != NULL)
 | |
| 	catal = catal->next;
 | |
|     catal->next = add;
 | |
|     return(catalogs);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogLocalResolve:
 | |
|  * @catalogs:  a document's list of catalogs
 | |
|  * @pubID:  the public ID string
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Do a complete resolution lookup of an External Identifier using a
 | |
|  * document's private catalog list
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found, it must be freed
 | |
|  *      by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlCatalogLocalResolve(void *catalogs, const xmlChar *pubID,
 | |
| 	               const xmlChar *sysID) {
 | |
|     xmlCatalogEntryPtr catal;
 | |
|     xmlChar *ret;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     if ((pubID == NULL) && (sysID == NULL))
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (xmlDebugCatalogs) {
 | |
|         if ((pubID != NULL) && (sysID != NULL)) {
 | |
|             xmlGenericError(xmlGenericErrorContext,
 | |
|                             "Local Resolve: pubID %s sysID %s\n", pubID, sysID);
 | |
|         } else if (pubID != NULL) {
 | |
|             xmlGenericError(xmlGenericErrorContext,
 | |
|                             "Local Resolve: pubID %s\n", pubID);
 | |
|         } else {
 | |
|             xmlGenericError(xmlGenericErrorContext,
 | |
|                             "Local Resolve: sysID %s\n", sysID);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     catal = (xmlCatalogEntryPtr) catalogs;
 | |
|     if (catal == NULL)
 | |
| 	return(NULL);
 | |
|     ret = xmlCatalogListXMLResolve(catal, pubID, sysID);
 | |
|     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
 | |
| 	return(ret);
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogLocalResolveURI:
 | |
|  * @catalogs:  a document's list of catalogs
 | |
|  * @URI:  the URI
 | |
|  *
 | |
|  * Do a complete resolution lookup of an URI using a
 | |
|  * document's private catalog list
 | |
|  *
 | |
|  * Returns the URI of the resource or NULL if not found, it must be freed
 | |
|  *      by the caller.
 | |
|  */
 | |
| xmlChar *
 | |
| xmlCatalogLocalResolveURI(void *catalogs, const xmlChar *URI) {
 | |
|     xmlCatalogEntryPtr catal;
 | |
|     xmlChar *ret;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     if (URI == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     if (xmlDebugCatalogs)
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Resolve URI %s\n", URI);
 | |
| 
 | |
|     catal = (xmlCatalogEntryPtr) catalogs;
 | |
|     if (catal == NULL)
 | |
| 	return(NULL);
 | |
|     ret = xmlCatalogListXMLResolveURI(catal, URI);
 | |
|     if ((ret != NULL) && (ret != XML_CATAL_BREAK))
 | |
| 	return(ret);
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /************************************************************************
 | |
|  *									*
 | |
|  *			Deprecated interfaces				*
 | |
|  *									*
 | |
|  ************************************************************************/
 | |
| /**
 | |
|  * xmlCatalogGetSystem:
 | |
|  * @sysID:  the system ID string
 | |
|  *
 | |
|  * Try to lookup the catalog reference associated to a system ID
 | |
|  * DEPRECATED, use xmlCatalogResolveSystem()
 | |
|  *
 | |
|  * Returns the resource if found or NULL otherwise.
 | |
|  */
 | |
| const xmlChar *
 | |
| xmlCatalogGetSystem(const xmlChar *sysID) {
 | |
|     xmlChar *ret;
 | |
|     static xmlChar result[1000];
 | |
|     static int msg = 0;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     if (msg == 0) {
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Use of deprecated xmlCatalogGetSystem() call\n");
 | |
| 	msg++;
 | |
|     }
 | |
| 
 | |
|     if (sysID == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     /*
 | |
|      * Check first the XML catalogs
 | |
|      */
 | |
|     if (xmlDefaultCatalog != NULL) {
 | |
| 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, NULL, sysID);
 | |
| 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
 | |
| 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
 | |
| 	    result[sizeof(result) - 1] = 0;
 | |
| 	    return(result);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (xmlDefaultCatalog != NULL)
 | |
| 	return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog->sgml, sysID));
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlCatalogGetPublic:
 | |
|  * @pubID:  the public ID string
 | |
|  *
 | |
|  * Try to lookup the catalog reference associated to a public ID
 | |
|  * DEPRECATED, use xmlCatalogResolvePublic()
 | |
|  *
 | |
|  * Returns the resource if found or NULL otherwise.
 | |
|  */
 | |
| const xmlChar *
 | |
| xmlCatalogGetPublic(const xmlChar *pubID) {
 | |
|     xmlChar *ret;
 | |
|     static xmlChar result[1000];
 | |
|     static int msg = 0;
 | |
| 
 | |
|     if (!xmlCatalogInitialized)
 | |
| 	xmlInitializeCatalog();
 | |
| 
 | |
|     if (msg == 0) {
 | |
| 	xmlGenericError(xmlGenericErrorContext,
 | |
| 		"Use of deprecated xmlCatalogGetPublic() call\n");
 | |
| 	msg++;
 | |
|     }
 | |
| 
 | |
|     if (pubID == NULL)
 | |
| 	return(NULL);
 | |
| 
 | |
|     /*
 | |
|      * Check first the XML catalogs
 | |
|      */
 | |
|     if (xmlDefaultCatalog != NULL) {
 | |
| 	ret = xmlCatalogListXMLResolve(xmlDefaultCatalog->xml, pubID, NULL);
 | |
| 	if ((ret != NULL) && (ret != XML_CATAL_BREAK)) {
 | |
| 	    snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
 | |
| 	    result[sizeof(result) - 1] = 0;
 | |
| 	    return(result);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (xmlDefaultCatalog != NULL)
 | |
| 	return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog->sgml, pubID));
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| #endif /* LIBXML_CATALOG_ENABLED */
 |