diff options
Diffstat (limited to 'catalog.c')
-rw-r--r-- | catalog.c | 3589 |
1 files changed, 3589 insertions, 0 deletions
diff --git a/catalog.c b/catalog.c new file mode 100644 index 0000000..602e16b --- /dev/null +++ b/catalog.c @@ -0,0 +1,3589 @@ +/** + * 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 +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <string.h> +#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 <libxml/globals.h> + +#define MAX_DELEGATE 50 +#define MAX_CATAL_DEPTH 50 + +/** + * TODO: + * + * macro to flag unimplemented blocks + * XML_CATALOG_PREFER user env to select between system/public prefered + * 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:///etc/xml/catalog" +#endif +#ifndef XML_SGML_DEFAULT_CATALOG +#define XML_SGML_DEFAULT_CATALOG "file:///etc/sgml/catalog" +#endif + +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_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; +}; + +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 informations + * + * 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 informations + * + * Handle a catalog error + */ +static void +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 + * + * 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 ret; + + 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 (name != NULL) + ret->name = xmlStrdup(name); + else + ret->name = NULL; + 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; + return(ret); +} + +static void +xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret); + +/** + * xmlFreeCatalogEntry: + * @ret: a Catalog entry + * + * Free the memory allocated to a Catalog entry + */ +static void +xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) { + 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); + ret = next; + } +} + +/** + * xmlFreeCatalogHashEntryList: + * @ret: a Catalog entry list + * + * Free the memory allocated to list of Catalog entries from the + * catalog file hash. + */ +static void +xmlFreeCatalogHashEntryList(xmlCatalogEntryPtr catal) { + xmlCatalogEntryPtr children, next; + + if (catal == NULL) + return; + + children = catal->children; + while (children != NULL) { + next = children->next; + children->dealloc = 0; + children->children = NULL; + xmlFreeCatalogEntry(children); + children = next; + } + catal->dealloc = 0; + xmlFreeCatalogEntry(catal); +} + +/** + * 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, + (xmlHashDeallocator) 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(xmlCatalogEntryPtr entry, FILE *out) { + 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"); +} + +static int +xmlDumpXMLCatalog(FILE *out, xmlCatalogEntryPtr catal) { + int ret; + xmlDocPtr doc; + xmlNsPtr ns; + xmlDtdPtr dtd; + xmlNodePtr node, catalog; + xmlOutputBufferPtr buf; + xmlCatalogEntryPtr cur; + + /* + * 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); + + /* + * add all the catalog entries + */ + cur = catal; + while (cur != NULL) { + 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_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; + } + + /* + * 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(xmlCatalogEntryPtr entry, xmlCatalogPtr catal) { + 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, + (xmlHashDeallocator) 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, + (xmlHashScanner) 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) { + if (xmlDefaultSAXHandler.error != NULL) { + xmlDefaultSAXHandler.error(NULL, "out of memory\n"); + } + return(NULL); + } + + buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); + if (buf == NULL) { + xmlFreeParserCtxt(ctxt); + return(NULL); + } + + inputStream = xmlNewInputStream(ctxt); + if (inputStream == NULL) { + xmlFreeParserCtxt(ctxt); + return(NULL); + } + + inputStream->filename = (char *) xmlCanonicPath((const xmlChar *)filename); + inputStream->buf = buf; + inputStream->base = inputStream->buf->buffer->content; + inputStream->cur = inputStream->buf->buffer->content; + inputStream->end = + &inputStream->buf->buffer->content[inputStream->buf->buffer->use]; + + inputPush(ctxt, inputStream); + if ((ctxt->directory == NULL) && (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; + + 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 = xmlMallocAtomic(size + 10); + if (content == NULL) { + xmlCatalogErrMemory("allocating catalog data"); + return (NULL); + } +#ifdef HAVE_STAT + len = read(fd, content, size); +#else + len = fread(content, 1, size, fd); +#endif + if (len < 0) { + xmlFree(content); + return (NULL); + } +#ifdef HAVE_STAT + close(fd); +#else + fclose(fd); +#endif + content[len] = 0; + + return(content); +} + +/************************************************************************ + * * + * The XML Catalog parser * + * * + ************************************************************************/ + +static xmlCatalogEntryPtr +xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename); +static void +xmlParseXMLCatalogNodeList(xmlNodePtr cur, xmlCatalogPrefer prefer, + xmlCatalogEntryPtr parent); +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 + * + * 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) { + 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); + } 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 + * + * 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) +{ + xmlChar *uri = NULL; + xmlChar *URL = NULL; + xmlChar *base = NULL; + xmlCatalogEntryPtr entry = NULL; + + if (cur == NULL) + return; + if (xmlStrEqual(cur->name, BAD_CAST "group")) { + xmlChar *prop; + + 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); + } + /* + * Recurse to propagate prefer to the subtree + * (xml:base handling is automated) + */ + xmlParseXMLCatalogNodeList(cur->children, prefer, parent); + } else if (xmlStrEqual(cur->name, BAD_CAST "public")) { + entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_PUBLIC, + BAD_CAST "public", BAD_CAST "publicId", BAD_CAST "uri", prefer); + } else if (xmlStrEqual(cur->name, BAD_CAST "system")) { + entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM, + BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri", prefer); + } 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); + } 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); + } 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); + } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) { + entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI, + BAD_CAST "uri", BAD_CAST "name", + BAD_CAST "uri", prefer); + } 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); + } 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); + } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) { + entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG, + BAD_CAST "nextCatalog", NULL, + BAD_CAST "catalog", prefer); + } + if ((entry != NULL) && (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 (base != NULL) + xmlFree(base); + if (uri != NULL) + xmlFree(uri); + if (URL != NULL) + xmlFree(URL); +} + +/** + * xmlParseXMLCatalogNodeList: + * @cur: the XML node list of siblings + * @prefer: the PUBLIC vs. SYSTEM current preference value + * @parent: the parent Catalog entry + * + * 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) { + while (cur != NULL) { + if ((cur->ns != NULL) && (cur->ns->href != NULL) && + (xmlStrEqual(cur->ns->href, XML_CATALOGS_NAMESPACE))) { + xmlParseXMLCatalogNode(cur, prefer, parent); + } + 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); + 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); + } 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); + if (catal->children != 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); + else + cur->next = xmlNewCatalogEntry(typ, orig, replace, + NULL, catal->prefer); + if (doregister) { + cur = 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\n", cur->name); + 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); + } + } + } + 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); + + /* + * 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; + + if (catal == NULL) + return(NULL); + if ((pubID == NULL) && (sysID == NULL)) + return(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); + 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); + 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) + return(ret); + } + } + catal = catal->next; + } + 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; + int count = 0; + + *id = NULL; + + if (RAW == '"') { + NEXT; + stop = '"'; + } else if (RAW == '\'') { + NEXT; + stop = '\''; + } else { + stop = ' '; + } + buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar)); + 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 * sizeof(xmlChar)); + if (tmp == NULL) { + xmlCatalogErrMemory("allocating public ID"); + xmlFree(buf); + return(NULL); + } + buf = tmp; + } + buf[len++] = *cur; + count++; + 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; + else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE")) + type = SGML_CATA_DELEGATE; + 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 (name == NULL) { + /* error */ + break; + } + if (!IS_BLANK_CH(*cur)) { + /* error */ + 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 *) "DELEGATE")) + type = SGML_CATA_DELEGATE; + 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; + 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 (!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); + res = xmlHashAddEntry(catal->sgml, name, entry); + if (res < 0) { + xmlFreeCatalogEntry(entry); + } + xmlFree(filename); + } + + } else if (type == SGML_CATA_CATALOG) { + if (super) { + xmlCatalogEntryPtr entry; + + entry = xmlNewCatalogEntry(type, sysid, NULL, NULL, + XML_CATA_PREFER_NONE); + res = xmlHashAddEntry(catal->sgml, sysid, entry); + if (res < 0) { + xmlFreeCatalogEntry(entry); + } + } 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; + + if (catal == NULL) + return(NULL); + + entry = (xmlCatalogEntryPtr) xmlHashLookup(catal, pubID); + if (entry == NULL) + return(NULL); + if (entry->type == SGML_CATA_PUBLIC) + return(entry->URL); + 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); + 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); + } + 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); + + 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) + sgml = xmlStrdup(sgml); + } + return(ret); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlACatalogDump: + * @catal: a Catalog + * @out: the file. + * + * Free up all the memory associated with catalogs + */ +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, + (xmlHashScanner) 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); + if (catal->sgml == NULL) + catal->sgml = xmlHashCreate(10); + res = xmlHashAddEntry(catal->sgml, orig, entry); + } + } + 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, + (xmlHashDeallocator) 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) + catalogs = XML_XML_DEFAULT_CATALOG; + + 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); + 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; + + if (pathss == NULL) + return; + + cur = pathss; + while ((cur != NULL) && (*cur != 0)) { + while (xmlIsBlank_ch(*cur)) cur++; + if (*cur != 0) { + paths = cur; + while ((*cur != 0) && (*cur != ':') && (!xmlIsBlank_ch(*cur))) + cur++; + path = xmlStrndup((const xmlChar *)paths, cur - paths); + if (path != NULL) { + xmlLoadCatalog((const char *) path); + xmlFree(path); + } + } + while (*cur == ':') + 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, + (xmlHashDeallocator)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. + * + * Free up all the memory associated with catalogs + */ +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); + xmlDefaultCatalog->xml = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL, + orig, NULL, xmlCatalogDefaultPrefer); + + 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; + case XML_CATA_PREFER_NONE: + break; + } + } + 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); + 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 */ |