renaming testCatalog as xmlcatalog, making it an installed app adding a

* Makefile.am xmlcatalog.c libxml.spec.in: renaming
  testCatalog as xmlcatalog, making it an installed app
  adding a shell, and preparing it to be a /etc/xml/catalog
  management tool, though not ready yet
* catalog.c include/libxml/catalog.h: adding support for
  XML Catalogs http://www.oasis-open.org/committees/entity/
  not finished, there is some interesting tradeoffs and a
  few open questions left.
Daniel
diff --git a/ChangeLog b/ChangeLog
index c8706fb..1ae75ed 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Mon Aug 20 02:04:13 CEST 2001 Daniel Veillard <daniel@veillard.com>
+
+	* Makefile.am xmlcatalog.c libxml.spec.in: renaming 
+	  testCatalog as xmlcatalog, making it an installed app
+	  adding a shell, and preparing it to be a /etc/xml/catalog
+	  management tool, though not ready yet
+	* catalog.c include/libxml/catalog.h: adding support for
+	  XML Catalogs http://www.oasis-open.org/committees/entity/
+	  not finished, there is some interesting tradeoffs and a
+	  few open questions left.
+
 Sun Aug 19 14:59:56 CEST 2001 Daniel Veillard <daniel@veillard.com>
 
 	* xmllint.c: fixed a line formatting problem
diff --git a/Makefile.am b/Makefile.am
index 4331d65..2e1d622 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,9 +7,9 @@
 
 INCLUDES = -I@srcdir@/include -I$(top_builddir)/include @Z_CFLAGS@ 
 
-noinst_PROGRAMS=testSAX testHTML testXPath testURI testDocbook testCatalog
+noinst_PROGRAMS=testSAX testHTML testXPath testURI testDocbook
 
-bin_PROGRAMS = xmllint
+bin_PROGRAMS = xmllint xmlcatalog
 
 bin_SCRIPTS=xml2-config
 
@@ -62,10 +62,10 @@
 testDocbook_DEPENDENCIES = $(DEPS)
 testDocbook_LDADD= $(LDADDS)
 
-testCatalog_SOURCES=testCatalog.c
-testCatalog_LDFLAGS = 
-testCatalog_DEPENDENCIES = $(DEPS)
-testCatalog_LDADD= $(LDADDS)
+xmlcatalog_SOURCES=xmlcatalog.c
+xmlcatalog_LDFLAGS = 
+xmlcatalog_DEPENDENCIES = $(DEPS)
+xmlcatalog_LDADD= $(LDADDS)
 
 testXPath_SOURCES=testXPath.c
 testXPath_LDFLAGS = 
diff --git a/catalog.c b/catalog.c
index 9664c4f..a5e0631 100644
--- a/catalog.c
+++ b/catalog.c
@@ -4,6 +4,9 @@
  * 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
@@ -32,6 +35,17 @@
 #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__);
+
+
 /************************************************************************
  *									*
  *			Types, all private				*
@@ -39,36 +53,57 @@
  ************************************************************************/
 
 typedef enum {
+    XML_CATA_PREFER_PUBLIC = 1,
+    XML_CATA_PREFER_SYSTEM
+} xmlCatalogPrefer;
+
+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
+    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;
 };
 
 static xmlHashTablePtr xmlDefaultCatalog;
+static xmlCatalogEntryPtr xmlDefaultXMLCatalogList = NULL;
 
 /* 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				*
@@ -76,7 +111,8 @@
  ************************************************************************/
 
 static xmlCatalogEntryPtr
-xmlNewCatalogEntry(int type, xmlChar *name, xmlChar *value) {
+xmlNewCatalogEntry(xmlCatalogEntryType type, const xmlChar *name,
+	           const xmlChar *value) {
     xmlCatalogEntryPtr ret;
 
     ret = (xmlCatalogEntryPtr) xmlMalloc(sizeof(xmlCatalogEntry));
@@ -85,16 +121,30 @@
 		"malloc of %d byte failed\n", sizeof(xmlCatalogEntry));
 	return(NULL);
     }
+    ret->next = NULL;
+    ret->parent = NULL;
+    ret->children = NULL;
     ret->type = type;
-    ret->name = xmlStrdup(name);
-    ret->value = xmlStrdup(value);
+    if (name != NULL)
+	ret->name = xmlStrdup(name);
+    else
+	ret->name = NULL;
+    if (value != NULL)
+	ret->value = xmlStrdup(value);
+    else
+	ret->value = NULL;
     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)
@@ -102,6 +152,17 @@
     xmlFree(ret);
 }
 
+static void
+xmlFreeCatalogEntryList(xmlCatalogEntryPtr ret) {
+    xmlCatalogEntryPtr next;
+
+    while (ret != NULL) {
+	next = ret->next;
+	xmlFreeCatalogEntry(ret);
+	ret = next;
+    }
+}
+
 /**
  * xmlCatalogDumpEntry:
  * @entry:  the 
@@ -114,60 +175,60 @@
     if ((entry == NULL) || (out == NULL))
 	return;
     switch (entry->type) {
-	case XML_CATA_ENTITY:
+	case SGML_CATA_ENTITY:
 	    fprintf(out, "ENTITY "); break;
-	case XML_CATA_PENTITY:
+	case SGML_CATA_PENTITY:
 	    fprintf(out, "ENTITY %%"); break;
-	case XML_CATA_DOCTYPE:
+	case SGML_CATA_DOCTYPE:
 	    fprintf(out, "DOCTYPE "); break;
-	case XML_CATA_LINKTYPE:
+	case SGML_CATA_LINKTYPE:
 	    fprintf(out, "LINKTYPE "); break;
-	case XML_CATA_NOTATION:
+	case SGML_CATA_NOTATION:
 	    fprintf(out, "NOTATION "); break;
-	case XML_CATA_PUBLIC:
+	case SGML_CATA_PUBLIC:
 	    fprintf(out, "PUBLIC "); break;
-	case XML_CATA_SYSTEM:
+	case SGML_CATA_SYSTEM:
 	    fprintf(out, "SYSTEM "); break;
-	case XML_CATA_DELEGATE:
+	case SGML_CATA_DELEGATE:
 	    fprintf(out, "DELEGATE "); break;
-	case XML_CATA_BASE:
+	case SGML_CATA_BASE:
 	    fprintf(out, "BASE "); break;
-	case XML_CATA_CATALOG:
+	case SGML_CATA_CATALOG:
 	    fprintf(out, "CATALOG "); break;
-	case XML_CATA_DOCUMENT:
+	case SGML_CATA_DOCUMENT:
 	    fprintf(out, "DOCUMENT "); break;
-	case XML_CATA_SGMLDECL:
+	case SGML_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:
+	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 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:
+	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 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:
+	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;
@@ -177,7 +238,498 @@
 
 /************************************************************************
  *									*
- *			The parser					*
+ *			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 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) {
+    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) {
+	    if (nameValue != NULL)
+		printf("Found %s: '%s' '%s'\n", name, nameValue, URL);
+	    else
+		printf("Found %s: '%s'\n", name, URL);
+	}
+	ret = xmlNewCatalogEntry(type, nameValue, URL);
+    } 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");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "system")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_SYSTEM,
+		BAD_CAST "system", BAD_CAST "systemId", BAD_CAST "uri");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteSystem")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_SYSTEM,
+		BAD_CAST "rewriteSystem", BAD_CAST "systemIdStartString",
+		BAD_CAST "rewritePrefix");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
+		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
+		BAD_CAST "catalog");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
+		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
+		BAD_CAST "catalog");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
+		BAD_CAST "uri", BAD_CAST "name",
+		BAD_CAST "uri");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
+		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
+		BAD_CAST "rewritePrefix");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
+		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
+		BAD_CAST "catalog");
+    } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
+	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
+		BAD_CAST "nextCatalog", NULL,
+		BAD_CAST "catalog");
+    }
+    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);
+
+    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))) {
+
+	parent = xmlNewCatalogEntry(XML_CATA_CATALOG,
+		                    (const xmlChar *)file, NULL);
+        if (parent == NULL) {
+	    xmlFreeDoc(doc);
+	    return(NULL);
+	}
+
+	prop = xmlGetProp(cur, BAD_CAST "prefer");
+	if (prop != NULL) {
+	    if (xmlStrEqual(prop, BAD_CAST "system")) {
+		prefer = XML_CATA_PREFER_SYSTEM;
+	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
+		prefer = XML_CATA_PREFER_PUBLIC;
+	    } else {
+		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", 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) 
+	return(NULL);
+
+    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,
+		                    (const xmlChar *)filename, NULL);
+        if (parent == NULL) {
+	    xmlFreeDoc(doc);
+	    return(NULL);
+	}
+
+	prop = xmlGetProp(cur, BAD_CAST "prefer");
+	if (prop != NULL) {
+	    if (xmlStrEqual(prop, BAD_CAST "system")) {
+		prefer = XML_CATA_PREFER_SYSTEM;
+	    } else if (xmlStrEqual(prop, BAD_CAST "public")) {
+		prefer = XML_CATA_PREFER_PUBLIC;
+	    } else {
+		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);
+}
+
+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 
+ * @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)
+	return(-1);
+
+    cur = catal->children;
+    /*
+     * Might be a simple "update in place"
+     */
+    if (cur != NULL) {
+	while (cur != NULL) {
+	    cur = cur->next;
+	    if ((cur->type == typ) && (xmlStrEqual(orig, cur->name))) {
+		if (cur->value != NULL)
+		    xmlFree(cur->value);
+		cur->value = xmlStrdup(replace);
+		return(1);
+	    }
+	    if (cur->next == NULL)
+		break;
+	    cur = cur->next;
+	}
+    }
+    if (cur == NULL)
+	catal->children = xmlNewCatalogEntry(typ, orig, replace);
+    else
+	cur->next = xmlNewCatalogEntry(typ, orig, replace);
+    return(1);
+}
+
+/************************************************************************
+ *									*
+ *			The SGML Catalog parser				*
  *									*
  ************************************************************************/
 
@@ -290,6 +842,36 @@
     return(cur);
 }
 
+static xmlCatalogEntryType
+xmlGetCatalogEntryType(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
 xmlParseCatalog(const xmlChar *value, const char *file) {
     const xmlChar *cur = value;
@@ -324,29 +906,29 @@
 	    }
 	    SKIP_BLANKS;
 	    if (xmlStrEqual(name, (const xmlChar *) "SYSTEM"))
-                type = XML_CATA_SYSTEM;
+                type = SGML_CATA_SYSTEM;
 	    else if (xmlStrEqual(name, (const xmlChar *) "PUBLIC"))
-                type = XML_CATA_PUBLIC;
+                type = SGML_CATA_PUBLIC;
 	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
-                type = XML_CATA_DELEGATE;
+                type = SGML_CATA_DELEGATE;
 	    else if (xmlStrEqual(name, (const xmlChar *) "ENTITY"))
-                type = XML_CATA_ENTITY;
+                type = SGML_CATA_ENTITY;
 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCTYPE"))
-                type = XML_CATA_DOCTYPE;
+                type = SGML_CATA_DOCTYPE;
 	    else if (xmlStrEqual(name, (const xmlChar *) "LINKTYPE"))
-                type = XML_CATA_LINKTYPE;
+                type = SGML_CATA_LINKTYPE;
 	    else if (xmlStrEqual(name, (const xmlChar *) "NOTATION"))
-                type = XML_CATA_NOTATION;
+                type = SGML_CATA_NOTATION;
 	    else if (xmlStrEqual(name, (const xmlChar *) "SGMLDECL"))
-                type = XML_CATA_SGMLDECL;
+                type = SGML_CATA_SGMLDECL;
 	    else if (xmlStrEqual(name, (const xmlChar *) "DOCUMENT"))
-                type = XML_CATA_DOCUMENT;
+                type = SGML_CATA_DOCUMENT;
 	    else if (xmlStrEqual(name, (const xmlChar *) "CATALOG"))
-                type = XML_CATA_CATALOG;
+                type = SGML_CATA_CATALOG;
 	    else if (xmlStrEqual(name, (const xmlChar *) "BASE"))
-                type = XML_CATA_BASE;
+                type = SGML_CATA_BASE;
 	    else if (xmlStrEqual(name, (const xmlChar *) "DELEGATE"))
-                type = XML_CATA_DELEGATE;
+                type = SGML_CATA_DELEGATE;
 	    else if (xmlStrEqual(name, (const xmlChar *) "OVERRIDE")) {
 		xmlFree(name);
 		cur = xmlParseCatalogName(cur, &name);
@@ -361,13 +943,13 @@
 	    name = NULL;
 
 	    switch(type) {
-		case XML_CATA_ENTITY:
+		case SGML_CATA_ENTITY:
 		    if (*cur == '%')
-			type = XML_CATA_PENTITY;
-		case XML_CATA_PENTITY:
-		case XML_CATA_DOCTYPE:
-		case XML_CATA_LINKTYPE:
-		case XML_CATA_NOTATION:
+			type = SGML_CATA_PENTITY;
+		case SGML_CATA_PENTITY:
+		case SGML_CATA_DOCTYPE:
+		case SGML_CATA_LINKTYPE:
+		case SGML_CATA_NOTATION:
 		    cur = xmlParseCatalogName(cur, &name);
 		    if (cur == NULL) {
 			/* error */
@@ -384,9 +966,9 @@
 			break;
 		    }
 		    break;
-		case XML_CATA_PUBLIC:
-		case XML_CATA_SYSTEM:
-		case XML_CATA_DELEGATE:
+		case SGML_CATA_PUBLIC:
+		case SGML_CATA_SYSTEM:
+		case SGML_CATA_DELEGATE:
 		    cur = xmlParseCatalogPubid(cur, &name);
 		    if (cur == NULL) {
 			/* error */
@@ -403,10 +985,10 @@
 			break;
 		    }
 		    break;
-		case XML_CATA_BASE:
-		case XML_CATA_CATALOG:
-		case XML_CATA_DOCUMENT:
-		case XML_CATA_SGMLDECL:
+		case SGML_CATA_BASE:
+		case SGML_CATA_CATALOG:
+		case SGML_CATA_DOCUMENT:
+		case SGML_CATA_SGMLDECL:
 		    cur = xmlParseCatalogPubid(cur, &sysid);
 		    if (cur == NULL) {
 			/* error */
@@ -422,12 +1004,12 @@
 		if (sysid != NULL)
 		    xmlFree(sysid);
 		break;
-	    } else if (type == XML_CATA_BASE) {
+	    } else if (type == SGML_CATA_BASE) {
 		if (base != NULL)
 		    xmlFree(base);
 		base = xmlStrdup(sysid);
-	    } else if ((type == XML_CATA_PUBLIC) ||
-		       (type == XML_CATA_SYSTEM)) {
+	    } else if ((type == SGML_CATA_PUBLIC) ||
+		       (type == SGML_CATA_SYSTEM)) {
 		xmlChar *filename;
 
 		filename = xmlBuildURI(sysid, base);
@@ -442,7 +1024,7 @@
 		    xmlFree(filename);
 		}
 
-	    } else if (type == XML_CATA_CATALOG) {
+	    } else if (type == SGML_CATA_CATALOG) {
 		xmlChar *filename;
 
 		filename = xmlBuildURI(sysid, base);
@@ -542,7 +1124,27 @@
     content[len] = 0;
     close(fd);
 
-    ret = xmlParseCatalog(content, filename);
+    if ((content[0] == ' ') || (content[0] == '-') ||
+	((content[0] >= 'A') && (content[0] <= 'Z')) ||
+	((content[0] >= 'a') && (content[0] <= 'z')))
+	ret = xmlParseCatalog(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);
@@ -611,7 +1213,92 @@
     entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, sysID);
     if (entry == NULL)
 	return(NULL);
-    if (entry->type == XML_CATA_SYSTEM)
+    if (entry->type == SGML_CATA_SYSTEM)
+	return(entry->value);
+    return(NULL);
+}
+
+/**
+ * xmlCatalogGetXMLPublic:
+ * @catal:  an XML catalog
+ * @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 *
+xmlCatalogGetXMLPublic(xmlCatalogEntryPtr catal, const xmlChar *pubID) {
+    const xmlChar *ret;
+    while (catal != NULL) {
+	switch (catal->type) {
+            case XML_CATA_CATALOG:
+		if (catal->children == NULL) {
+		    TODO /* fetch and fill */
+		}
+		ret = xmlCatalogGetXMLPublic(catal->children, pubID);
+		if (ret != NULL)
+		    return(ret);
+		break;
+            case XML_CATA_NEXT_CATALOG:
+		if (catal->children == NULL) {
+		    TODO /* fetch and fill */
+		}
+            case XML_CATA_PUBLIC:
+		if (xmlStrEqual(pubID, catal->name))
+		    return(catal->value);
+		break;
+            case XML_CATA_SYSTEM:
+            case XML_CATA_REWRITE_SYSTEM:
+            case XML_CATA_DELEGATE_PUBLIC:
+            case XML_CATA_DELEGATE_SYSTEM:
+            case XML_CATA_URI:
+            case XML_CATA_REWRITE_URI:
+            case XML_CATA_DELEGATE_URI:
+		TODO;
+		break;
+
+            case XML_CATA_NONE:
+            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:
+		/* Ignored entries */
+		break;
+	}
+	catal = catal->next;
+    }
+    return(NULL);
+}
+
+/**
+ * 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);
 }
@@ -626,17 +1313,27 @@
  */
 const xmlChar *
 xmlCatalogGetPublic(const xmlChar *pubID) {
-    xmlCatalogEntryPtr entry;
+    xmlCatalogEntryPtr catal;
+    const xmlChar *ret;
 
-    if ((pubID == NULL) || (xmlDefaultCatalog == NULL))
+    if (pubID == NULL)
 	return(NULL);
-    entry = (xmlCatalogEntryPtr) xmlHashLookup(xmlDefaultCatalog, pubID);
-    if (entry == NULL)
-	return(NULL);
-    if (entry->type == XML_CATA_PUBLIC)
-	return(entry->value);
+    
+    /*
+     * Check first the XML catalogs
+     */
+    catal = xmlDefaultXMLCatalogList;
+    if (catal != NULL) {
+	ret = xmlCatalogGetXMLPublic(catal, pubID);
+	if (ret != NULL)
+	    return(ret);
+    }
+
+    if (xmlDefaultCatalog != NULL)
+	return(xmlCatalogGetSGMLPublic(xmlDefaultCatalog, pubID));
     return(NULL);
 }
+
 /**
  * xmlCatalogDump:
  * @out:  the file.
@@ -647,9 +1344,74 @@
 xmlCatalogDump(FILE *out) {
     if (out == NULL)
 	return;
-    if (xmlDefaultCatalog != NULL) {
+
+    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) {
+	res = xmlAddXMLCatalog(xmlDefaultXMLCatalogList, type, orig, replace);
+    } else if (xmlDefaultCatalog != NULL) {
+	xmlCatalogEntryType typ;
+
+	typ = xmlGetCatalogEntryType(type);
+	if (type != XML_CATA_NONE) {
+	    xmlCatalogEntryPtr entry;
+	    entry = xmlNewCatalogEntry(typ, orig, replace);
+	    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) {
+}
+
+/**
+ * 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);
 }
 #endif /* LIBXML_CATALOG_ENABLED */
diff --git a/include/libxml/catalog.h b/include/libxml/catalog.h
index 504b2e6..1a62bb2 100644
--- a/include/libxml/catalog.h
+++ b/include/libxml/catalog.h
@@ -42,6 +42,13 @@
 void		xmlCatalogDump		(FILE *out);
 const xmlChar *	xmlCatalogGetSystem	(const xmlChar *sysID);
 const xmlChar *	xmlCatalogGetPublic	(const xmlChar *pubID);
+const xmlChar *	xmlCatalogResolve	(const xmlChar *pubID,
+	                                 const xmlChar *sysID);
+int		xmlCatalogAdd		(const xmlChar *type,
+					 const xmlChar *orig,
+					 const xmlChar *replace);
+int		xmlCatalogRemove	(const xmlChar *value);
+int		xmlCatalogSetDebug	(int level);
 
 #ifdef __cplusplus
 }
diff --git a/libxml.spec.in b/libxml.spec.in
index 56aadad..efef779 100644
--- a/libxml.spec.in
+++ b/libxml.spec.in
@@ -122,6 +122,7 @@
 
 %{prefix}/lib/lib*.so.*
 %{prefix}/bin/xmllint
+%{prefix}/bin/xmlcatalog
 
 %files devel
 %defattr(-, root, root)
diff --git a/testCatalog.c b/testCatalog.c
deleted file mode 100644
index d7bc4c7..0000000
--- a/testCatalog.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * testCatalog.c : a small tester program for Catalog loading
- *
- * See Copyright for the status of this software.
- *
- * daniel@veillard.com
- */
-
-#include "libxml.h"
-
-#include <string.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-#include <libxml/xmlmemory.h>
-#include <libxml/uri.h>
-#include <libxml/catalog.h>
-#include <libxml/parser.h>
-
-int main(int argc, char **argv) {
-#ifdef LIBXML_CATALOG_ENABLED
-    int i;
-
-    for (i = 1; i < argc; i++)
-	xmlLoadCatalog(argv[i]);
-
-    xmlCatalogDump(stdout);
-    xmlCatalogCleanup();
-    xmlCleanupParser();
-    xmlMemoryDump();
-#endif
-    return(0);
-}
diff --git a/xmlIO.c b/xmlIO.c
index 6360d04..981528f 100644
--- a/xmlIO.c
+++ b/xmlIO.c
@@ -3,7 +3,7 @@
  *
  * See Copyright for the status of this software.
  *
- * Daniel.Veillard@w3.org
+ * daniel@veillard.com
  *
  * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char
  */
diff --git a/xmlcatalog.c b/xmlcatalog.c
new file mode 100644
index 0000000..0063bca
--- /dev/null
+++ b/xmlcatalog.c
@@ -0,0 +1,216 @@
+/*
+ * xmlcatalog.c : a small utility program to handle XML catalogs
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#include "libxml.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <libxml/xmlmemory.h>
+#include <libxml/uri.h>
+#include <libxml/catalog.h>
+#include <libxml/parser.h>
+
+static int shell = 0;
+static int noout = 0;
+static int verbose = 0;
+
+#ifdef LIBXML_CATALOG_ENABLED
+/************************************************************************
+ * 									*
+ * 			Shell Interface					*
+ * 									*
+ ************************************************************************/
+/**
+ * xmlShellReadline:
+ * @prompt:  the prompt value
+ *
+ * Read a string
+ * 
+ * Returns a pointer to it or NULL on EOF the caller is expected to
+ *     free the returned string.
+ */
+static char *
+xmlShellReadline(const char *prompt) {
+#ifdef HAVE_LIBREADLINE
+    char *line_read;
+
+    /* Get a line from the user. */
+    line_read = readline (prompt);
+
+    /* If the line has any text in it, save it on the history. */
+    if (line_read && *line_read)
+	add_history (line_read);
+
+    return (line_read);
+#else
+    char line_read[501];
+
+    if (prompt != NULL)
+	fprintf(stdout, "%s", prompt);
+    if (!fgets(line_read, 500, stdin))
+        return(NULL);
+    line_read[500] = 0;
+    return(strdup(line_read));
+#endif
+}
+
+
+static void usershell(void) {
+    char *cmdline = NULL, *cur;
+    int nbargs;
+    char command[100];
+    char arg[400];
+    int i;
+    const xmlChar *answer;
+
+    while (1) {
+	cmdline = xmlShellReadline("> ");
+	if (cmdline == NULL)
+	    return;
+
+	/*
+	 * Parse the command itself
+	 */
+	cur = cmdline;
+	nbargs = 0;
+	while ((*cur == ' ') || (*cur == '\t')) cur++;
+	i = 0;
+	while ((*cur != ' ') && (*cur != '\t') &&
+	       (*cur != '\n') && (*cur != '\r')) {
+	    if (*cur == 0)
+		break;
+	    command[i++] = *cur++;
+	}
+	command[i] = 0;
+	if (i == 0) continue;
+	nbargs++;
+
+	/*
+	 * Parse the argument
+	 */
+	while ((*cur == ' ') || (*cur == '\t')) cur++;
+	i = 0;
+	while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
+	    if (*cur == 0)
+		break;
+	    arg[i++] = *cur++;
+	}
+	arg[i] = 0;
+	if (i != 0) 
+	    nbargs++;
+
+	/*
+	 * start interpreting the command
+	 */
+        if (!strcmp(command, "exit"))
+	    break;
+        if (!strcmp(command, "quit"))
+	    break;
+        if (!strcmp(command, "bye"))
+	    break;
+	if (!strcmp(command, "public")) {
+	    answer = xmlCatalogGetPublic((const xmlChar *) arg);
+	    if (answer == NULL) {
+		printf("No entry for PUBLIC %s\n", arg);
+	    } else {
+		printf("%s\n", answer);
+	    }
+	} else if (!strcmp(command, "system")) {
+	    answer = xmlCatalogGetSystem((const xmlChar *) arg);
+	    if (answer == NULL) {
+		printf("No entry for SYSTEM %s\n", arg);
+	    } else {
+		printf("%s\n", answer);
+	    }
+	} else if (!strcmp(command, "dump")) {
+	    xmlCatalogDump(stdout);
+	} else {
+	    if (strcmp(command, "help")) {
+		printf("Unrecognized command %s\n", command);
+	    }
+	    printf("Commands available:\n");
+	    printf("\tpublic PublicID: make a PUBLIC identifier lookup\n");
+	    printf("\tsystem SystemID: make a SYSTEM identifier lookup\n");
+	    printf("\tdump: print the current catalog state\n");
+	    printf("\texit:  quit the shell\n");
+	} 
+	free(cmdline); /* not xmlFree here ! */
+    }
+}
+
+/************************************************************************
+ * 									*
+ * 			Main						*
+ * 									*
+ ************************************************************************/
+static void usage(const char *name) {
+    printf("Usage : %s [options] catalogfile ...\n", name);
+    printf("\tParse the catalog file(s) and output the result of the parsing\n");
+    printf("\t--shell : run a shell allowing interactive queries\n");
+    printf("\t-v --verbose : provide debug informations\n");
+}
+int main(int argc, char **argv) {
+    int i;
+
+    if (argc <= 1) {
+	usage(argv[0]);
+	return(1);
+    }
+
+    LIBXML_TEST_VERSION
+    for (i = 1; i < argc ; i++) {
+	if (!strcmp(argv[i], "-"))
+	    break;
+
+	if (argv[i][0] != '-')
+	    continue;
+	if ((!strcmp(argv[i], "-verbose")) ||
+	    (!strcmp(argv[i], "-v")) ||
+	    (!strcmp(argv[i], "--verbose"))) {
+	    verbose++;
+	    xmlCatalogSetDebug(verbose);
+	} else if ((!strcmp(argv[i], "-shell")) ||
+	    (!strcmp(argv[i], "--shell"))) {
+	    shell++;
+            noout = 1;
+	} else {
+	    fprintf(stderr, "Unknown option %s\n", argv[i]);
+	    usage(argv[0]);
+	    return(1);
+	}
+    }
+
+    for (i = 1; i < argc; i++) {
+	if (argv[i][0] == '-')
+	    continue;
+	xmlLoadCatalog(argv[i]);
+    }
+
+    if (shell) {
+	usershell();
+    }
+    if (!noout) {
+	xmlCatalogDump(stdout);
+    }
+
+    /*
+     * Cleanup and check for memory leaks
+     */
+    xmlCatalogCleanup();
+    xmlCleanupParser();
+    xmlMemoryDump();
+    return(0);
+}
+#else
+int main(int argc, char **argv) {
+    fprintf(stderr, "libxml was not compiled with catalog support\n");
+    return(1);
+}
+#endif