mirror of
https://github.com/GNOME/libxml2.git
synced 2025-02-23 18:29:14 +08:00
2440 lines
62 KiB
C
2440 lines
62 KiB
C
/**
|
|
* catalog.c: set of generic Catalog related routines
|
|
*
|
|
* Reference: SGML Open Technical Resolution TR9401:1997.
|
|
* http://www.jclark.com/sp/catalog.htm
|
|
*
|
|
* XML Catalogs Working Draft 06 August 2001
|
|
* http://www.oasis-open.org/committees/entity/spec-2001-08-06.html
|
|
*
|
|
* See Copyright for the status of this software.
|
|
*
|
|
* Daniel.Veillard@imag.fr
|
|
*/
|
|
|
|
#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>
|
|
|
|
/**
|
|
* 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)
|
|
#define XML_DEFAULT_CATALOG "/etc/xml/catalog"
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Types, all private *
|
|
* *
|
|
************************************************************************/
|
|
|
|
typedef enum {
|
|
XML_CATA_NONE = 0,
|
|
XML_CATA_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;
|
|
};
|
|
|
|
static xmlCatalogAllow xmlCatalogDefaultAllow = XML_CATA_ALLOW_ALL;
|
|
static xmlCatalogPrefer xmlCatalogDefaultPrefer = XML_CATA_PREFER_SYSTEM;
|
|
static xmlHashTablePtr xmlDefaultCatalog;
|
|
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;
|
|
return(ret);
|
|
}
|
|
|
|
static void
|
|
xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret);
|
|
|
|
static void
|
|
xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
|
|
if (ret == NULL)
|
|
return;
|
|
if (ret->children != NULL)
|
|
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");
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* 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));
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* 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 = xmlParseFile((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;
|
|
|
|
if (catal == NULL)
|
|
return(-1);
|
|
if (catal->value == NULL)
|
|
return(-1);
|
|
if (catal->children != NULL)
|
|
return(-1);
|
|
|
|
/*
|
|
* Fetch and parse
|
|
*/
|
|
children = xmlParseXMLCatalogFile(catal->prefer, catal->value);
|
|
if (children == NULL)
|
|
return(-1);
|
|
|
|
/*
|
|
* Where a real test and set would be needed !
|
|
*/
|
|
if (catal->children == NULL) {
|
|
catal->children = 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_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))
|
|
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))
|
|
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) {
|
|
/*
|
|
* 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)))) {
|
|
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) {
|
|
/*
|
|
* 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)))) {
|
|
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) {
|
|
/*
|
|
* 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)))) {
|
|
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 recuse in 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;
|
|
|
|
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 (xmlDefaultXMLCatalogList != NULL)
|
|
xmlFreeCatalogEntryList(xmlDefaultXMLCatalogList);
|
|
xmlDefaultXMLCatalogList = NULL;
|
|
if (xmlDefaultCatalog != NULL)
|
|
xmlHashFree(xmlDefaultCatalog,
|
|
(xmlHashDeallocator) xmlFreeCatalogEntry);
|
|
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 (!xmlCatalogInitialized)
|
|
xmlInitializeCatalog();
|
|
|
|
if (xmlDefaultXMLCatalogList != NULL) {
|
|
return(xmlCatalogListXMLResolve(xmlDefaultXMLCatalogList, pubID, sysID));
|
|
} 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 (xmlDefaultXMLCatalogList != NULL) {
|
|
return(xmlCatalogListXMLResolveURI(xmlDefaultXMLCatalogList, URI));
|
|
} 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);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
if (!xmlCatalogInitialized)
|
|
xmlInitializeCatalog();
|
|
catal = (xmlCatalogEntryPtr) catalogs;
|
|
if (catal == NULL)
|
|
return(NULL);
|
|
return(xmlCatalogListXMLResolve(catal, pubID, sysID));
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
if (!xmlCatalogInitialized)
|
|
xmlInitializeCatalog();
|
|
catal = (xmlCatalogEntryPtr) catalogs;
|
|
if (catal == NULL)
|
|
return(NULL);
|
|
return(xmlCatalogListXMLResolveURI(catal, URI));
|
|
}
|
|
|
|
#endif /* LIBXML_CATALOG_ENABLED */
|