mirror of
https://github.com/GNOME/libxml2.git
synced 2025-02-23 18:29:14 +08:00
Cleanup, cleanup .. * configure.in Makefile.am: removed libxml softlink for good * include/libxml/*.h *.c doc/Makefile.am: cleanup to get 100% coverage by gtk-doc Daniel
656 lines
15 KiB
C
656 lines
15 KiB
C
/**
|
|
* catalog.c: set of generic Catalog related routines
|
|
*
|
|
* Reference: SGML Open Technical Resolution TR9401:1997.
|
|
* http://www.jclark.com/sp/catalog.htm
|
|
*
|
|
* 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>
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Types, all private *
|
|
* *
|
|
************************************************************************/
|
|
|
|
typedef enum {
|
|
XML_CATA_NONE = 0,
|
|
XML_CATA_SYSTEM,
|
|
XML_CATA_PUBLIC,
|
|
XML_CATA_ENTITY,
|
|
XML_CATA_PENTITY,
|
|
XML_CATA_DOCTYPE,
|
|
XML_CATA_LINKTYPE,
|
|
XML_CATA_NOTATION,
|
|
XML_CATA_DELEGATE,
|
|
XML_CATA_BASE,
|
|
XML_CATA_CATALOG,
|
|
XML_CATA_DOCUMENT,
|
|
XML_CATA_SGMLDECL
|
|
} xmlCatalogEntryType;
|
|
|
|
typedef struct _xmlCatalogEntry xmlCatalogEntry;
|
|
typedef xmlCatalogEntry *xmlCatalogEntryPtr;
|
|
struct _xmlCatalogEntry {
|
|
xmlCatalogEntryType type;
|
|
xmlChar *name;
|
|
xmlChar *value;
|
|
};
|
|
|
|
static xmlHashTablePtr xmlDefaultCatalog;
|
|
|
|
/* 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 */
|
|
|
|
/************************************************************************
|
|
* *
|
|
* alloc or dealloc *
|
|
* *
|
|
************************************************************************/
|
|
|
|
static xmlCatalogEntryPtr
|
|
xmlNewCatalogEntry(int type, xmlChar *name, xmlChar *value) {
|
|
xmlCatalogEntryPtr ret;
|
|
|
|
ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
|
|
if (ret == NULL) {
|
|
xmlGenericError(xmlGenericErrorContext,
|
|
"malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
|
|
return(NULL);
|
|
}
|
|
ret->type = type;
|
|
ret->name = xmlStrdup(name);
|
|
ret->value = xmlStrdup(value);
|
|
return(ret);
|
|
}
|
|
|
|
static void
|
|
xmlFreeCatalogEntry(xmlCatalogEntryPtr ret) {
|
|
if (ret == NULL)
|
|
return;
|
|
if (ret->name != NULL)
|
|
xmlFree(ret->name);
|
|
if (ret->value != NULL)
|
|
xmlFree(ret->value);
|
|
xmlFree(ret);
|
|
}
|
|
|
|
/**
|
|
* 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 XML_CATA_ENTITY:
|
|
fprintf(out, "ENTITY "); break;
|
|
case XML_CATA_PENTITY:
|
|
fprintf(out, "ENTITY %%"); break;
|
|
case XML_CATA_DOCTYPE:
|
|
fprintf(out, "DOCTYPE "); break;
|
|
case XML_CATA_LINKTYPE:
|
|
fprintf(out, "LINKTYPE "); break;
|
|
case XML_CATA_NOTATION:
|
|
fprintf(out, "NOTATION "); break;
|
|
case XML_CATA_PUBLIC:
|
|
fprintf(out, "PUBLIC "); break;
|
|
case XML_CATA_SYSTEM:
|
|
fprintf(out, "SYSTEM "); break;
|
|
case XML_CATA_DELEGATE:
|
|
fprintf(out, "DELEGATE "); break;
|
|
case XML_CATA_BASE:
|
|
fprintf(out, "BASE "); break;
|
|
case XML_CATA_CATALOG:
|
|
fprintf(out, "CATALOG "); break;
|
|
case XML_CATA_DOCUMENT:
|
|
fprintf(out, "DOCUMENT "); break;
|
|
case XML_CATA_SGMLDECL:
|
|
fprintf(out, "SGMLDECL "); break;
|
|
default:
|
|
return;
|
|
}
|
|
switch (entry->type) {
|
|
case XML_CATA_ENTITY:
|
|
case XML_CATA_PENTITY:
|
|
case XML_CATA_DOCTYPE:
|
|
case XML_CATA_LINKTYPE:
|
|
case XML_CATA_NOTATION:
|
|
fprintf(out, "%s", entry->name); break;
|
|
case XML_CATA_PUBLIC:
|
|
case XML_CATA_SYSTEM:
|
|
case XML_CATA_SGMLDECL:
|
|
case XML_CATA_DOCUMENT:
|
|
case XML_CATA_CATALOG:
|
|
case XML_CATA_BASE:
|
|
case XML_CATA_DELEGATE:
|
|
fprintf(out, "\"%s\"", entry->name); break;
|
|
default:
|
|
break;
|
|
}
|
|
switch (entry->type) {
|
|
case XML_CATA_ENTITY:
|
|
case XML_CATA_PENTITY:
|
|
case XML_CATA_DOCTYPE:
|
|
case XML_CATA_LINKTYPE:
|
|
case XML_CATA_NOTATION:
|
|
case XML_CATA_PUBLIC:
|
|
case XML_CATA_SYSTEM:
|
|
case XML_CATA_DELEGATE:
|
|
fprintf(out, " \"%s\"", entry->value); break;
|
|
default:
|
|
break;
|
|
}
|
|
fprintf(out, "\n");
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* The parser *
|
|
* *
|
|
************************************************************************/
|
|
|
|
|
|
#define RAW *cur
|
|
#define NEXT cur++;
|
|
#define SKIP(x) cur += x;
|
|
|
|
#define SKIP_BLANKS while (IS_BLANK(*cur)) NEXT;
|
|
|
|
static const xmlChar *
|
|
xmlParseCatalogComment(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 *
|
|
xmlParseCatalogPubid(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 *
|
|
xmlParseCatalogName(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 int
|
|
xmlParseCatalog(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] == '-') && (cur[1] == '-')) {
|
|
cur = xmlParseCatalogComment(cur);
|
|
if (cur == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
} else {
|
|
xmlChar *sysid = NULL;
|
|
xmlChar *name = NULL;
|
|
xmlCatalogEntryType type = XML_CATA_NONE;
|
|
|
|
cur = xmlParseCatalogName(cur, &name);
|
|
if (name == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
if (!IS_BLANK(*cur)) {
|
|
/* error */
|
|
break;
|
|
}
|
|
SKIP_BLANKS;
|
|
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 *) "DELEGATE"))
|
|
type = XML_CATA_DELEGATE;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
|
|
type = XML_CATA_ENTITY;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
|
|
type = XML_CATA_DOCTYPE;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
|
|
type = XML_CATA_LINKTYPE;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
|
|
type = XML_CATA_NOTATION;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
|
|
type = XML_CATA_SGMLDECL;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
|
|
type = XML_CATA_DOCUMENT;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
|
|
type = XML_CATA_CATALOG;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
|
|
type = XML_CATA_BASE;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
|
|
type = XML_CATA_DELEGATE;
|
|
else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
|
|
xmlFree(name);
|
|
cur = xmlParseCatalogName(cur, &name);
|
|
if (name == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
xmlFree(name);
|
|
continue;
|
|
}
|
|
xmlFree(name);
|
|
name = NULL;
|
|
|
|
switch(type) {
|
|
case XML_CATA_ENTITY:
|
|
if (*cur == '%')
|
|
type = XML_CATA_PENTITY;
|
|
case XML_CATA_PENTITY:
|
|
case XML_CATA_DOCTYPE:
|
|
case XML_CATA_LINKTYPE:
|
|
case XML_CATA_NOTATION:
|
|
cur = xmlParseCatalogName(cur, &name);
|
|
if (cur == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
if (!IS_BLANK(*cur)) {
|
|
/* error */
|
|
break;
|
|
}
|
|
SKIP_BLANKS;
|
|
cur = xmlParseCatalogPubid(cur, &sysid);
|
|
if (cur == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
break;
|
|
case XML_CATA_PUBLIC:
|
|
case XML_CATA_SYSTEM:
|
|
case XML_CATA_DELEGATE:
|
|
cur = xmlParseCatalogPubid(cur, &name);
|
|
if (cur == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
if (!IS_BLANK(*cur)) {
|
|
/* error */
|
|
break;
|
|
}
|
|
SKIP_BLANKS;
|
|
cur = xmlParseCatalogPubid(cur, &sysid);
|
|
if (cur == NULL) {
|
|
/* error */
|
|
break;
|
|
}
|
|
break;
|
|
case XML_CATA_BASE:
|
|
case XML_CATA_CATALOG:
|
|
case XML_CATA_DOCUMENT:
|
|
case XML_CATA_SGMLDECL:
|
|
cur = xmlParseCatalogPubid(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 == XML_CATA_BASE) {
|
|
if (base != NULL)
|
|
xmlFree(base);
|
|
base = xmlStrdup(sysid);
|
|
} else if ((type == XML_CATA_PUBLIC) ||
|
|
(type == XML_CATA_SYSTEM)) {
|
|
xmlChar *filename;
|
|
|
|
filename = xmlBuildURI(sysid, base);
|
|
if (filename != NULL) {
|
|
xmlCatalogEntryPtr entry;
|
|
|
|
entry = xmlNewCatalogEntry(type, name, filename);
|
|
res = xmlHashAddEntry(xmlDefaultCatalog, name, entry);
|
|
if (res < 0) {
|
|
xmlFreeCatalogEntry(entry);
|
|
}
|
|
xmlFree(filename);
|
|
}
|
|
|
|
} else if (type == XML_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);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Public interfaces *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* 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
|
|
* 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);
|
|
|
|
if (stat(filename, &info) < 0)
|
|
return(-1);
|
|
|
|
/*
|
|
* 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);
|
|
|
|
ret = xmlParseCatalog(content, filename);
|
|
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
|
|
* 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 (xmlDefaultCatalog != NULL)
|
|
xmlHashFree(xmlDefaultCatalog,
|
|
(xmlHashDeallocator) xmlFreeCatalogEntry);
|
|
xmlDefaultCatalog = NULL;
|
|
}
|
|
|
|
/**
|
|
* xmlCatalogGetSystem:
|
|
* @sysId: the system ID string
|
|
*
|
|
* Try to lookup the resource associated to a system ID
|
|
*
|
|
* Returns the resource name if found or NULL otherwise.
|
|
*/
|
|
const xmlChar *
|
|
xmlCatalogGetSystem(const xmlChar *sysID) {
|
|
xmlCatalogEntryPtr entry;
|
|
|
|
if ((sysID == NULL) || (xmlDefaultCatalog == NULL))
|
|
return(NULL);
|
|
entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, sysID);
|
|
if (entry == NULL)
|
|
return(NULL);
|
|
if (entry->type == XML_CATA_SYSTEM)
|
|
return(entry->value);
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* xmlCatalogGetPublic:
|
|
* @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.
|
|
*/
|
|
const xmlChar *
|
|
xmlCatalogGetPublic(const xmlChar *pubID) {
|
|
xmlCatalogEntryPtr entry;
|
|
|
|
if ((pubID == NULL) || (xmlDefaultCatalog == NULL))
|
|
return(NULL);
|
|
entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, pubID);
|
|
if (entry == NULL)
|
|
return(NULL);
|
|
if (entry->type == XML_CATA_PUBLIC)
|
|
return(entry->value);
|
|
return(NULL);
|
|
}
|
|
/**
|
|
* xmlCatalogDump:
|
|
* @out: the file.
|
|
*
|
|
* Free up all the memory associated with catalogs
|
|
*/
|
|
void
|
|
xmlCatalogDump(FILE *out) {
|
|
if (out == NULL)
|
|
return;
|
|
if (xmlDefaultCatalog != NULL) {
|
|
xmlHashScan(xmlDefaultCatalog,
|
|
(xmlHashScanner) xmlCatalogDumpEntry, out);
|
|
}
|
|
}
|
|
#endif /* LIBXML_CATALOG_ENABLED */
|