blob: 8aa17e65fa2ccbe65e305ad3a8dcb5b89a2b115f [file] [log] [blame]
/**
* 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
*/
#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
#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>
#define MAX_DELEGATE 50
/**
* TODO:
*
* macro to flag unimplemented blocks
*/
#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_DEFAULT_CATALOG
#define XML_DEFAULT_CATALOG "/etc/xml/catalog"
#endif
/************************************************************************
* *
* Types, all private *
* *
************************************************************************/
typedef enum {
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;
xmlCatalogPrefer prefer;
int dealloc;
};
static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_PUBLIC;
static xmlHashTablePtr xmlDefaultCatalog;
static xmlHashTablePtr xmlCatalogXMLFiles = NULL;
static xmlCatalogEntryPtr xmlDefaultXMLCatalogList = NULL;
static int xmlCatalogInitialized = 0;
/* Catalog stack */
static const char * catalTab[10]; /* stack of catals */
static int catalNr = 0; /* Number of current catal streams */
static int catalMax = 10; /* Max number of catal streams */
static int xmlDebugCatalogs = 0; /* used for debugging */
/************************************************************************
* *
* alloc or dealloc *
* *
************************************************************************/
static xmlCatalogEntryPtr
xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
const xmlChar *value, xmlCatalogPrefer prefer) {
xmlCatalogEntryPtr ret;
ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
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;
ret->prefer = prefer;
ret->dealloc = 1;
return(ret);
}
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
static void
xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
if (ret == NULL)
return;
if ((ret->children != NULL) && (ret->dealloc == 1))
xmlFreeCatalogEntryList(ret->children);
if (ret->name != NULL)
xmlFree(ret->name);
if (ret->value != NULL)
xmlFree(ret->value);
xmlFree(ret);
}
static void
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
xmlCatalogEntryPtr next;
while (ret != NULL) {
next = ret->next;
xmlFreeCatalogEntry(ret);
ret = next;
}
}
/**
* xmlCatalogDumpEntry:
* @entry: the
* @out: the file.
*
* Free up all the memory associated with catalogs
*/
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", 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");
}
/**
* xmlCatalogConvertEntry:
* @entry: the entry
* @res: pointer to te number converted
*
* Free up all the memory associated with catalogs
*/
static void
xmlCatalogConvertEntry(xmlCatalogEntryPtr entry, int *res) {
if ((entry == NULL) || (xmlDefaultXMLCatalogList == 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(xmlDefaultCatalog, entry->name,
(xmlHashDeallocator) xmlFreeCatalogEntry);
return;
}
/*
* Conversion successful, remove from the SGML catalog
* and add it to the default XML one
*/
xmlHashRemoveEntry(xmlDefaultCatalog, entry->name, NULL);
entry->parent = xmlDefaultXMLCatalogList;
entry->next = NULL;
if (xmlDefaultXMLCatalogList->children == NULL)
xmlDefaultXMLCatalogList->children = entry;
else {
xmlCatalogEntryPtr prev;
prev = xmlDefaultXMLCatalogList->children;
while (prev->next != NULL)
prev = prev->next;
prev->next = entry;
}
if (res != NULL)
(*res)++;
}
/************************************************************************
* *
* Helper function *
* *
************************************************************************/
/**
* xmlCatalogUnWrapURN:
* @urn: an "urn:publicid:" to unwrapp
*
* 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) - 3)
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[1] == 'B'))
result[i++] = '+';
else if ((urn[1] == '3') && (urn[1] == 'A'))
result[i++] = ':';
else if ((urn[1] == '2') && (urn[1] == 'F'))
result[i++] = '/';
else if ((urn[1] == '3') && (urn[1] == 'B'))
result[i++] = ';';
else if ((urn[1] == '2') && (urn[1] == '7'))
result[i++] = '\'';
else if ((urn[1] == '3') && (urn[1] == 'F'))
result[i++] = '?';
else if ((urn[1] == '2') && (urn[1] == '3'))
result[i++] = '#';
else if ((urn[1] == '2') && (urn[1] == '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 = xmlMemStrdup(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);
}
/************************************************************************
* *
* The XML Catalog parser *
* *
************************************************************************/
static xmlCatalogEntryPtr
xmlParseXMLCatalogFile(xmlCatalogPrefer prefer, const xmlChar *filename);
static xmlCatalogEntryPtr
xmlParseXMLCatalog(const xmlChar *value, xmlCatalogPrefer prefer,
const char *file);
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);
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);
}
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) {
xmlGenericError(xmlGenericErrorContext,
"%s entry lacks '%s'\n", name, attrName);
ok = 0;
}
}
uriValue = xmlGetProp(cur, uriAttrName);
if (uriValue == NULL) {
xmlGenericError(xmlGenericErrorContext,
"%s entry lacks '%s'\n", name, uriAttrName);
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, URL, prefer);
} else {
xmlGenericError(xmlGenericErrorContext,
"%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);
}
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 {
xmlGenericError(xmlGenericErrorContext,
"Invalid value for prefer: '%s'\n", prop);
}
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);
}
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 */
}
static xmlCatalogEntryPtr
xmlParseXMLCatalog(const xmlChar *value, xmlCatalogPrefer prefer,
const char *file) {
xmlDocPtr doc;
xmlNodePtr cur;
xmlChar *prop;
xmlCatalogEntryPtr parent = NULL;
if ((value == NULL) || (file == NULL))
return(NULL);
if (xmlDebugCatalogs)
xmlGenericError(xmlGenericErrorContext,
"Parsing catalog %s's content\n", file);
doc = xmlParseDoc((xmlChar *) value);
if (doc == NULL)
return(NULL);
doc->URL = xmlStrdup((const xmlChar *) file);
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))) {
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 {
xmlGenericError(xmlGenericErrorContext,
"Invalid value for prefer: '%s'\n",
prop);
}
xmlFree(prop);
}
parent = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
(const xmlChar *)file, prefer);
if (parent == NULL) {
xmlFreeDoc(doc);
return(NULL);
}
cur = cur->children;
xmlParseXMLCatalogNodeList(cur, prefer, parent);
} else {
xmlGenericError(xmlGenericErrorContext,
"File %s is not an XML Catalog\n", file);
xmlFreeDoc(doc);
return(NULL);
}
xmlFreeDoc(doc);
return(parent);
}
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,
"Parsing catalog %s\n", 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, 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 {
xmlGenericError(xmlGenericErrorContext,
"Invalid value for prefer: '%s'\n",
prop);
}
xmlFree(prop);
}
cur = cur->children;
xmlParseXMLCatalogNodeList(cur, prefer, parent);
} else {
xmlGenericError(xmlGenericErrorContext,
"File %s is not an XML Catalog\n", filename);
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
* It tries to be thread safe but by lack of an atomic test and
* set there is a risk of loosing memory.
*
* Returns 0 in case of success, -1 otherwise
*/
static int
xmlFetchXMLCatalogFile(xmlCatalogEntryPtr catal) {
xmlCatalogEntryPtr children = NULL, doc;
if (catal == NULL)
return(-1);
if (catal->value == NULL)
return(-1);
if (catal->children != NULL)
return(-1);
if (xmlCatalogXMLFiles != NULL)
children = (xmlCatalogEntryPtr)
xmlHashLookup(xmlCatalogXMLFiles, catal->value);
if (children != NULL) {
catal->children = children;
catal->dealloc = 0;
return(0);
}
/*
* Fetch and parse
*/
doc = xmlParseXMLCatalogFile(catal->prefer, catal->value);
if (doc == NULL) {
catal->type = XML_CATA_BROKEN_CATALOG;
return(-1);
}
if ((catal->type == XML_CATA_CATALOG) &&
(doc->type == XML_CATA_CATALOG)) {
children = doc->children;
doc->children = NULL;
xmlFreeCatalogEntryList(doc);
} else {
children = doc;
}
/*
* Where a real test and set would be needed !
*/
if (catal->children == NULL) {
catal->children = children;
catal->dealloc = 1;
if (xmlCatalogXMLFiles == NULL)
xmlCatalogXMLFiles = xmlHashCreate(10);
if (xmlCatalogXMLFiles != NULL) {
if (children != NULL)
xmlHashAddEntry(xmlCatalogXMLFiles, catal->value, children);
}
} else {
/*
* Another thread filled it before us
*/
xmlFreeCatalogEntryList(children);
}
return(0);
}
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_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);
}
/**
* 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;
if ((catal == NULL) ||
((catal->type != XML_CATA_CATALOG) &&
(catal->type != XML_CATA_BROKEN_CATALOG)))
return(-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);
cur->value = 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, catal->prefer);
else
cur->next = xmlNewCatalogEntry(typ, orig, replace, catal->prefer);
return(0);
}
/**
* xmlDelXMLCatalog:
* @catal: top of an XML catalog
* @value: the value to remove from teh 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, prev, tmp;
int ret = 0;
if ((catal == NULL) ||
((catal->type != XML_CATA_CATALOG) &&
(catal->type != XML_CATA_BROKEN_CATALOG)))
return(-1);
if (value == NULL)
return(-1);
/*
* Scan the children
*/
cur = catal->children;
prev = NULL;
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);
}
ret++;
tmp = cur;
cur = tmp->next;
if (prev == NULL) {
catal->children = cur;
} else {
prev->next = cur;
}
xmlFreeCatalogEntry(tmp);
continue;
}
prev = cur;
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;
/*
* 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);
return(xmlStrdup(cur->value));
}
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->value);
if (ret != NULL)
ret = xmlStrcat(ret, &sysID[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) &&
(!xmlStrncmp(sysID, cur->name, xmlStrlen(cur->name)))) {
for (i = 0;i < nbList;i++)
if (xmlStrEqual(cur->value, delegates[i]))
break;
if (i < nbList) {
cur = cur->next;
continue;
}
if (nbList < MAX_DELEGATE)
delegates[nbList++] = cur->value;
if (cur->children == NULL) {
xmlFetchXMLCatalogFile(cur);
}
if (cur->children != NULL) {
if (xmlDebugCatalogs)
xmlGenericError(xmlGenericErrorContext,
"Trying system delegate %s\n", cur->value);
ret = xmlCatalogListXMLResolve(cur->children, NULL,
sysID);
if (ret != NULL)
return(ret);
}
}
cur = cur->next;
}
/*
* Apply the cut algorithm explained in 4/
*/
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);
return(xmlStrdup(cur->value));
}
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->value, delegates[i]))
break;
if (i < nbList) {
cur = cur->next;
continue;
}
if (nbList < MAX_DELEGATE)
delegates[nbList++] = cur->value;
if (cur->children == NULL) {
xmlFetchXMLCatalogFile(cur);
}
if (cur->children != NULL) {
if (xmlDebugCatalogs)
xmlGenericError(xmlGenericErrorContext,
"Trying public delegate %s\n", cur->value);
ret = xmlCatalogListXMLResolve(cur->children, pubID,
NULL);
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 = xmlCatalogListXMLResolve(cur->children, pubID, sysID);
if (ret != NULL)
return(ret);
}
}
cur = cur->next;
}
}
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->value));
}
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->value);
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) &&
(!xmlStrncmp(URI, cur->name, xmlStrlen(cur->name)))) {
for (i = 0;i < nbList;i++)
if (xmlStrEqual(cur->value, delegates[i]))
break;
if (i < nbList) {
cur = cur->next;
continue;
}
if (nbList < MAX_DELEGATE)
delegates[nbList++] = cur->value;
if (cur->children == NULL) {
xmlFetchXMLCatalogFile(cur);
}
if (cur->children != NULL) {
if (xmlDebugCatalogs)
xmlGenericError(xmlGenericErrorContext,
"Trying URI delegate %s\n", cur->value);
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, 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 = 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(*cur)) NEXT;
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);
}
static const xmlChar *
xmlParseSGMLCatalogPubid(const xmlChar *cur, xmlChar **id) {
xmlChar *buf = NULL;
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 *) xmlMalloc(size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"malloc of %d byte failed\n", size);
return(NULL);
}
while (xmlIsPubidChar(*cur)) {
if ((*cur == stop) && (stop != ' '))
break;
if ((stop == ' ') && (IS_BLANK(*cur)))
break;
if (len + 1 >= size) {
size *= 2;
buf = (xmlChar *) xmlRealloc(buf, size * sizeof(xmlChar));
if (buf == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", size);
return(NULL);
}
}
buf[len++] = *cur;
count++;
NEXT;
}
buf[len] = 0;
if (stop == ' ') {
if (!IS_BLANK(*cur)) {
xmlFree(buf);
return(NULL);
}
} else {
if (*cur != stop) {
xmlFree(buf);
return(NULL);
}
NEXT;
}
*id = buf;
return(cur);
}
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);
}
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);
}
static int
xmlParseSGMLCatalog(const xmlChar *value, const char *file) {
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(*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(*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(*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,
XML_CATA_PREFER_NONE);
res = xmlHashAddEntry(xmlDefaultCatalog, name, entry);
if (res < 0) {
xmlFreeCatalogEntry(entry);
}
xmlFree(filename);
}
} else if (type == SGML_CATA_CATALOG) {
xmlChar *filename;
filename = xmlBuildURI(sysid, base);
if (filename != NULL) {
xmlLoadCatalog((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);
}
/**
* xmlCatalogGetSGMLPublic:
* @catal: an SGML catalog hash
* @pubId: the public ID string
*
* Try to lookup the system ID associated to a public ID
*
* Returns the system ID 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->value);
return(NULL);
}
/**
* xmlCatalogGetSGMLSystem:
* @catal: an SGML catalog hash
* @sysId: the public ID string
*
* Try to lookup the catalog local reference for a system ID
*
* Returns the system ID 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->value);
return(NULL);
}
/**
* xmlCatalogSGMLResolve:
* @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(const xmlChar *pubID, const xmlChar *sysID) {
const xmlChar *ret = NULL;
if (xmlDefaultCatalog == NULL)
return(NULL);
if (pubID != NULL)
ret = xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID);
if (ret != NULL)
return(ret);
if (sysID != NULL)
ret = xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID);
return(NULL);
}
/************************************************************************
* *
* Public interfaces *
* *
************************************************************************/
/**
* xmlInitializeCatalog:
*
* Do the catalog initialization.
* TODO: this function is not thread safe, catalog initialization should
* preferably be done once at startup
*/
void
xmlInitializeCatalog(void) {
const char *catalogs;
if (xmlCatalogInitialized != 0)
return;
if (getenv("XML_DEBUG_CATALOG"))
xmlDebugCatalogs = 1;
if ((xmlDefaultXMLCatalogList == NULL) && (xmlDefaultCatalog == NULL)) {
catalogs = getenv("XML_CATALOG_FILES");
if (catalogs == NULL)
catalogs = XML_DEFAULT_CATALOG;
xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG,
NULL, BAD_CAST catalogs, xmlCatalogDefaultPrefer);
}
xmlCatalogInitialized = 1;
}
/**
* 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.
* TODO: 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 fd, len, ret, i;
struct stat info;
xmlChar *content;
if (filename == NULL)
return(-1);
if (xmlDefaultCatalog == NULL)
xmlDefaultCatalog = xmlHashCreate(20);
if (xmlDefaultCatalog == NULL)
return(-1);
/*
* Need to be done after ...
*/
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
#ifdef HAVE_STAT
if (stat(filename, &info) < 0)
return(-1);
#endif
/*
* Prevent loops
*/
for (i = 0;i < catalNr;i++) {
if (xmlStrEqual((const xmlChar *)catalTab[i],
(const xmlChar *)filename)) {
xmlGenericError(xmlGenericErrorContext,
"xmlLoadCatalog: %s seems to induce a loop\n",
filename);
return(-1);
}
}
if (catalNr >= catalMax) {
xmlGenericError(xmlGenericErrorContext,
"xmlLoadCatalog: %s catalog list too deep\n",
filename);
return(-1);
}
catalTab[catalNr++] = filename;
if ((fd = open(filename, O_RDONLY)) < 0) {
catalNr--;
return(-1);
}
content = xmlMalloc(info.st_size + 10);
if (content == NULL) {
xmlGenericError(xmlGenericErrorContext,
"realloc of %d byte failed\n", info.st_size + 10);
catalNr--;
return(-1);
}
len = read(fd, content, info.st_size);
if (len < 0) {
xmlFree(content);
catalNr--;
return(-1);
}
content[len] = 0;
close(fd);
if ((content[0] == ' ') || (content[0] == '-') ||
((content[0] >= 'A') && (content[0] <= 'Z')) ||
((content[0] >= 'a') && (content[0] <= 'z')))
ret = xmlParseSGMLCatalog(content, filename);
else {
xmlCatalogEntryPtr catal, tmp;
/* TODO: allow to switch the default preference */
catal = xmlParseXMLCatalog(content, XML_CATA_PREFER_PUBLIC, filename);
if (catal != NULL) {
if (xmlDefaultXMLCatalogList == NULL)
xmlDefaultXMLCatalogList = catal;
else {
tmp = xmlDefaultXMLCatalogList;
while (tmp->next != NULL)
tmp = tmp->next;
tmp->next = catal;
}
ret = 0;
} else
ret = -1;
}
xmlFree(content);
catalNr--;
return(ret);
}
/**
* xmlLoadCatalogs:
* @paths: a list of file path separated by ':' or spaces
*
* Load the catalogs and makes their definitions effective for the default
* external entity loader.
* TODO: 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 (IS_BLANK(*cur)) cur++;
if (*cur != 0) {
paths = cur;
while ((*cur != 0) && (*cur != ':') && (!IS_BLANK(*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 (xmlDebugCatalogs)
xmlGenericError(xmlGenericErrorContext,
"Catalogs cleanup\n");
if (xmlCatalogXMLFiles != NULL)
xmlHashFree(xmlCatalogXMLFiles, NULL);
xmlCatalogXMLFiles = NULL;
if (xmlDefaultXMLCatalogList != NULL)
xmlFreeCatalogEntryList(xmlDefaultXMLCatalogList);
xmlDefaultXMLCatalogList = NULL;
if (xmlDefaultCatalog != NULL)
xmlHashFree(xmlDefaultCatalog,
(xmlHashDeallocator) xmlFreeCatalogEntry);
xmlDefaultCatalog = NULL;
xmlDebugCatalogs = 0;
xmlDefaultCatalog = NULL;
xmlCatalogInitialized = 0;
}
/**
* xmlCatalogGetSystem:
* @pubId: the public ID string
*
* Try to lookup the system ID associated to a public ID
* DEPRECATED, use xmlCatalogResolveSystem()
*
* Returns the system ID if found or NULL otherwise.
*/
const xmlChar *
xmlCatalogGetSystem(const xmlChar *sysID) {
xmlChar *ret;
static xmlChar result[1000];
if (sysID == NULL)
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
/*
* Check first the XML catalogs
*/
if (xmlDefaultXMLCatalogList != NULL) {
ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, NULL, sysID);
if (ret != NULL) {
snprintf((char *) result, sizeof(result) - 1, "%s", (char *) ret);
result[sizeof(result) - 1] = 0;
return(result);
}
}
if (xmlDefaultCatalog != NULL)
return(xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID));
return(NULL);
}
/**
* xmlCatalogResolveSystem:
* @sysId: the public ID string
*
* Try to lookup the catalog resource for a system ID
*
* Returns the system ID if found or NULL otherwise, the value returned
* must be freed by the caller.
*/
xmlChar *
xmlCatalogResolveSystem(const xmlChar *sysID) {
xmlCatalogEntryPtr catal;
xmlChar *ret;
const xmlChar *sgml;
if (sysID == NULL)
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
/*
* Check first the XML catalogs
*/
catal = xmlDefaultXMLCatalogList;
if (catal != NULL) {
ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, NULL, sysID);
if ((ret != NULL) && (ret != XML_CATAL_BREAK))
return(ret);
}
if (xmlDefaultCatalog != NULL) {
sgml = xmlCatalogGetSGMLSystem(xmlDefaultCatalog, sysID);
if (sgml != NULL)
return(xmlStrdup(sgml));
}
return(NULL);
}
/**
* xmlCatalogGetPublic:
* @pubId: the public ID string
*
* Try to lookup the system ID associated to a public ID
* DEPRECATED, use xmlCatalogResolvePublic()
*
* Returns the system ID if found or NULL otherwise.
*/
const xmlChar *
xmlCatalogGetPublic(const xmlChar *pubID) {
xmlChar *ret;
static xmlChar result[1000];
if (pubID == NULL)
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
/*
* Check first the XML catalogs
*/
if (xmlDefaultXMLCatalogList != NULL) {
ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, 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, pubID));
return(NULL);
}
/**
* xmlCatalogResolvePublic:
* @pubId: the public ID string
*
* Try to lookup the system ID associated to a public ID
*
* Returns the system ID if found or NULL otherwise, the value returned
* must be freed by the caller.
*/
xmlChar *
xmlCatalogResolvePublic(const xmlChar *pubID) {
xmlCatalogEntryPtr catal;
xmlChar *ret;
const xmlChar *sgml;
if (pubID == NULL)
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
/*
* Check first the XML catalogs
*/
catal = xmlDefaultXMLCatalogList;
if (catal != NULL) {
ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, NULL);
if ((ret != NULL) && (ret != XML_CATAL_BREAK))
return(ret);
}
if (xmlDefaultCatalog != NULL) {
sgml = xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID);
if (sgml != NULL)
return(xmlStrdup(sgml));
}
return(NULL);
}
/**
* 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) {
if ((pubID == NULL) && (sysID == NULL))
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
if (xmlDebugCatalogs) {
if (pubID != NULL) {
xmlGenericError(xmlGenericErrorContext,
"Resolve: pubID %s\n", pubID);
} else {
xmlGenericError(xmlGenericErrorContext,
"Resolve: sysID %s\n", sysID);
}
}
if (xmlDefaultXMLCatalogList != NULL) {
xmlChar *ret;
ret = xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, sysID);
if ((ret != NULL) && (ret != XML_CATAL_BREAK))
return(ret);
} else {
const xmlChar *ret;
ret = xmlCatalogSGMLResolve(pubID, sysID);
if (ret != NULL)
return(xmlStrdup(ret));
}
return(NULL);
}
/**
* xmlCatalogResolveURI:
* @pubId: 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) {
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
if (URI == NULL)
return(NULL);
if (xmlDebugCatalogs)
xmlGenericError(xmlGenericErrorContext,
"Resolve URI %s\n", URI);
if (xmlDefaultXMLCatalogList != NULL) {
xmlChar *ret;
ret = xmlCatalogListXMLResolveURI(xmlDefaultXMLCatalogList, URI);
if ((ret != NULL) && (ret != XML_CATAL_BREAK))
return(ret);
} else {
const xmlChar *ret;
ret = xmlCatalogSGMLResolve(NULL, URI);
if (ret != NULL)
return(xmlStrdup(ret));
}
return(NULL);
}
/**
* xmlCatalogDump:
* @out: the file.
*
* Free up all the memory associated with catalogs
*/
void
xmlCatalogDump(FILE *out) {
if (out == NULL)
return;
if (xmlDefaultXMLCatalogList != NULL) {
xmlDumpXMLCatalog(out, xmlDefaultXMLCatalogList);
} else if (xmlDefaultCatalog != NULL) {
xmlHashScan(xmlDefaultCatalog,
(xmlHashScanner) xmlCatalogDumpEntry, out);
}
}
/**
* 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.
*
* Returns 0 if successful, -1 otherwise
*/
int
xmlCatalogAdd(const xmlChar *type, const xmlChar *orig, const xmlChar *replace) {
int res = -1;
if ((xmlDefaultXMLCatalogList == NULL) &&
(xmlStrEqual(type, BAD_CAST "catalog"))) {
xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG, NULL,
orig, xmlCatalogDefaultPrefer);
return(0);
}
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
if (xmlDefaultXMLCatalogList != NULL) {
res = xmlAddXMLCatalog(xmlDefaultXMLCatalogList, type, orig, replace);
} else if (xmlDefaultCatalog != NULL) {
xmlCatalogEntryType typ;
typ = xmlGetSGMLCatalogEntryType(type);
if (type != XML_CATA_NONE) {
xmlCatalogEntryPtr entry;
entry = xmlNewCatalogEntry(typ, orig, replace,
XML_CATA_PREFER_NONE);
res = xmlHashAddEntry(xmlDefaultCatalog, orig, entry);
}
}
return(res);
}
/**
* xmlCatalogRemove:
* @value: the value to remove
*
* Remove an entry from the catalog
*
* Returns 0 if successful, -1 otherwise
*/
int
xmlCatalogRemove(const xmlChar *value) {
int res = -1;
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
if (xmlDefaultXMLCatalogList != NULL) {
res = xmlDelXMLCatalog(xmlDefaultXMLCatalogList, value);
} else if (xmlDefaultCatalog != NULL) {
TODO
}
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();
if (xmlDebugCatalogs) {
xmlGenericError(xmlGenericErrorContext,
"Converting SGML catalog to XML\n");
}
if (xmlDefaultXMLCatalogList == NULL) {
xmlDefaultXMLCatalogList = xmlNewCatalogEntry(XML_CATA_CATALOG,
NULL, BAD_CAST "NewCatalog.xml",
xmlCatalogDefaultPrefer);
}
if (xmlDefaultCatalog != NULL) {
res = 0;
xmlHashScan(xmlDefaultCatalog,
(xmlHashScanner) xmlCatalogConvertEntry, &res);
}
return(res);
}
/**
* 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:
*
* Used to set the user preference w.r.t. to what catalogs should
* be accepted
*/
void
xmlCatalogSetDefaults(xmlCatalogAllow allow) {
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
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 (!xmlCatalogInitialized)
xmlInitializeCatalog();
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);
}
/**
* xmlCatalogFreeLocal:
* @catalogs: a document's list of catalogs
*
* Free up the memory associated to the catalog list
*/
void
xmlCatalogFreeLocal(void *catalogs) {
xmlCatalogEntryPtr catal;
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,
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 ((pubID == NULL) && (sysID == NULL))
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
if (xmlDebugCatalogs) {
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
* @pubId: 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 (URI == NULL)
return(NULL);
if (!xmlCatalogInitialized)
xmlInitializeCatalog();
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);
}
#endif /* LIBXML_CATALOG_ENABLED */