Applied patch from Brent Hendricks adding support for late DTD validation.

* python/generator.py python/libxml.c python/libxml2class.txt
  python/libxml_wrap.h python/types.c: Applied patch from Brent
  Hendricks adding support for late DTD validation.
* python/tests/Makefile.am python/tests/dtdvalid.py
  python/tests/test.dtd: integrated the provided regression test
Daniel
diff --git a/python/generator.py b/python/generator.py
index 04b3832..6032065 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -270,6 +270,7 @@
     'xmlParserCtxt *': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
     'htmlParserCtxtPtr': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
     'htmlParserCtxt *': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"),
+    'xmlValidCtxtPtr': ('O', "ValidCtxt", "xmlValidCtxtPtr", "xmlValidCtxtPtr"),
     'xmlCatalogPtr': ('O', "catalog", "xmlCatalogPtr", "xmlCatalogPtr"),
     'FILE *': ('O', "File", "FILEPtr", "FILE *"),
     'xmlURIPtr': ('O', "URI", "xmlURIPtr", "xmlURIPtr"),
@@ -345,6 +346,14 @@
         return 1
     if name == "xmlErrMemory":
         return 1
+
+    if name == "xmlValidBuildContentModel":
+        return 1
+    if name == "xmlValidateElementDecl":
+        return 1
+    if name == "xmlValidateAttributeDecl":
+        return 1
+
     return 0
 
 def print_function_wrapper(name, output, export, include):
@@ -668,6 +677,7 @@
     "xmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
     "htmlParserCtxtPtr": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
     "htmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
+    "xmlValidCtxtPtr": ("._o", "ValidCtxt(_obj=%s)", "ValidCtxt"),
     "xmlCatalogPtr": ("._o", "catalog(_obj=%s)", "catalog"),
     "xmlURIPtr": ("._o", "URI(_obj=%s)", "URI"),
     "xmlErrorPtr": ("._o", "Error(_obj=%s)", "Error"),
@@ -718,6 +728,7 @@
 	"Schema": "xmlSchemaFree",
 	"SchemaParserCtxt": "xmlSchemaFreeParserCtxt",
 	"SchemaValidCtxt": "xmlSchemaFreeValidCtxt",
+        "ValidCtxt": "xmlFreeValidCtxt",
 }
 
 functions_noexcept = {
diff --git a/python/libxml.c b/python/libxml.c
index 9d33c77..ae1a0f4 100644
--- a/python/libxml.c
+++ b/python/libxml.c
@@ -1554,7 +1554,7 @@
     xmlParserCtxtPyCtxtPtr pyCtxt;
     
 #ifdef DEBUG_ERROR
-    printf("libxml_xmlParserCtxtGenericErrorFuncHandler(%p, %s, ...) called\n", ctx, msg);
+    printf("libxml_xmlParserCtxtGenericErrorFuncHandler(%p, %s, ...) called\n", ctx, str);
 #endif
 
     ctxt = (xmlParserCtxtPtr)ctx;
@@ -1723,6 +1723,141 @@
     return(Py_None);
 }
 
+/***
+ * xmlValidCtxt stuff
+ */
+
+typedef struct 
+{
+    PyObject *warn;
+    PyObject *error;
+    PyObject *arg;
+} xmlValidCtxtPyCtxt;
+typedef xmlValidCtxtPyCtxt *xmlValidCtxtPyCtxtPtr;
+
+static void
+libxml_xmlValidCtxtGenericErrorFuncHandler(void *ctx, int severity, char *str) 
+{
+    PyObject *list;
+    PyObject *result;
+    xmlValidCtxtPyCtxtPtr pyCtxt;
+    
+#ifdef DEBUG_ERROR
+    printf("libxml_xmlValidCtxtGenericErrorFuncHandler(%p, %d, %s, ...) called\n", ctx, severity, str);
+#endif
+
+    pyCtxt = (xmlValidCtxtPyCtxtPtr)ctx;
+    
+    list = PyTuple_New(2);
+    PyTuple_SetItem(list, 0, libxml_charPtrWrap(str));
+    PyTuple_SetItem(list, 1, pyCtxt->arg);
+    Py_XINCREF(pyCtxt->arg);
+    result = PyEval_CallObject(pyCtxt->error, list);
+    if (result == NULL) 
+    {
+	/* TODO: manage for the exception to be propagated... */
+	PyErr_Print();
+    }
+    Py_XDECREF(list);
+    Py_XDECREF(result);
+}
+
+static void
+libxml_xmlValidCtxtGenericWarningFuncHandler(void *ctx, int severity, char *str) 
+{
+    PyObject *list;
+    PyObject *result;
+    xmlValidCtxtPyCtxtPtr pyCtxt;
+    
+#ifdef DEBUG_ERROR
+    printf("libxml_xmlValidCtxtGenericWarningFuncHandler(%p, %d, %s, ...) called\n", ctx, severity, str);
+#endif
+
+    pyCtxt = (xmlValidCtxtPyCtxtPtr)ctx;
+
+    list = PyTuple_New(2);
+    PyTuple_SetItem(list, 0, libxml_charPtrWrap(str));
+    PyTuple_SetItem(list, 1, pyCtxt->arg);
+    Py_XINCREF(pyCtxt->arg);
+    result = PyEval_CallObject(pyCtxt->warn, list);
+    if (result == NULL) 
+    {
+	/* TODO: manage for the exception to be propagated... */
+	PyErr_Print();
+    }
+    Py_XDECREF(list);
+    Py_XDECREF(result);
+}
+
+static void 
+libxml_xmlValidCtxtErrorFuncHandler(void *ctx, const char *msg, ...) 
+{
+    va_list ap;
+
+    va_start(ap, msg);
+    libxml_xmlValidCtxtGenericErrorFuncHandler(ctx,XML_PARSER_SEVERITY_VALIDITY_ERROR,libxml_buildMessage(msg,ap));
+    va_end(ap);
+}
+
+static void 
+libxml_xmlValidCtxtWarningFuncHandler(void *ctx, const char *msg, ...) 
+{
+    va_list ap;
+
+    va_start(ap, msg);
+    libxml_xmlValidCtxtGenericWarningFuncHandler(ctx,XML_PARSER_SEVERITY_VALIDITY_WARNING,libxml_buildMessage(msg,ap));
+    va_end(ap);
+}
+
+static PyObject *
+libxml_xmlSetValidErrors(ATTRIBUTE_UNUSED PyObject * self, PyObject * args)
+{
+    PyObject *py_retval;
+    PyObject *pyobj_error;
+    PyObject *pyobj_warn;
+    PyObject *pyobj_ctx;
+    PyObject *pyobj_arg = Py_None;
+    xmlValidCtxtPtr ctxt;
+    xmlValidCtxtPyCtxtPtr pyCtxt;
+
+    if (!PyArg_ParseTuple
+        (args, (char *) "OOO|O:xmlSetValidErrors", &pyobj_ctx, &pyobj_error, &pyobj_warn, &pyobj_arg))
+        return (NULL);
+
+#ifdef DEBUG_ERROR
+    printf("libxml_xmlSetValidErrors(%p, %p, %p) called\n", pyobj_ctx, pyobj_error, pyobj_warn);
+#endif
+
+    ctxt = PyValidCtxt_Get(pyobj_ctx);
+    pyCtxt = xmlMalloc(sizeof(xmlValidCtxtPyCtxt));
+    if (pyCtxt == NULL) {
+            py_retval = libxml_intWrap(-1);
+            return(py_retval);
+    }
+    memset(pyCtxt, 0, sizeof(xmlValidCtxtPyCtxt));
+
+    
+    /* TODO: check warn and error is a function ! */
+    Py_XDECREF(pyCtxt->error);
+    Py_XINCREF(pyobj_error);
+    pyCtxt->error = pyobj_error;
+    
+    Py_XDECREF(pyCtxt->warn);
+    Py_XINCREF(pyobj_warn);
+    pyCtxt->warn = pyobj_warn;
+    
+    Py_XDECREF(pyCtxt->arg);
+    Py_XINCREF(pyobj_arg);
+    pyCtxt->arg = pyobj_arg;
+
+    ctxt->error = libxml_xmlValidCtxtErrorFuncHandler;
+    ctxt->warning = libxml_xmlValidCtxtWarningFuncHandler;
+    ctxt->userData = pyCtxt;
+
+    py_retval = libxml_intWrap(1);
+    return (py_retval);
+}
+
 /************************************************************************
  *									*
  *                      Per xmlTextReader error handler                 *
@@ -3482,6 +3617,7 @@
     {(char *) "type", libxml_type, METH_VARARGS, NULL},
     {(char *) "doc", libxml_doc, METH_VARARGS, NULL},
     {(char *) "xmlNewNode", libxml_xmlNewNode, METH_VARARGS, NULL},
+    {(char *)"xmlSetValidErrors", libxml_xmlSetValidErrors, METH_VARARGS, NULL},
 #ifdef LIBXML_OUTPUT_ENABLED
     {(char *) "serializeNode", libxml_serializeNode, METH_VARARGS, NULL},
     {(char *) "saveNodeTo", libxml_saveNodeTo, METH_VARARGS, NULL},
diff --git a/python/libxml2class.txt b/python/libxml2class.txt
index 972074c..3d61714 100644
--- a/python/libxml2class.txt
+++ b/python/libxml2class.txt
@@ -187,6 +187,7 @@
 parseURI()
 
 # functions from module valid
+newValidCtxt()
 validateNameValue()
 validateNamesValue()
 validateNmtokenValue()
@@ -632,7 +633,20 @@
     isRef()
     removeID()
     removeRef()
+    validCtxtNormalizeAttributeValue()
     validNormalizeAttributeValue()
+    validateDocument()
+    validateDocumentFinal()
+    validateDtd()
+    validateDtdFinal()
+    validateElement()
+    validateNotationUse()
+    validateOneAttribute()
+    validateOneElement()
+    validateOneNamespace()
+    validatePopElement()
+    validatePushElement()
+    validateRoot()
 
     # functions from module xinclude
     xincludeProcess()
@@ -687,18 +701,23 @@
 
 
 Class xmlAttribute(xmlNode)
-Class catalog()
+Class ValidCtxt()
 
-    # functions from module catalog
-    add()
-    catalogIsEmpty()
-    convertSGMLCatalog()
-    dump()
-    remove()
-    resolve()
-    resolvePublic()
-    resolveSystem()
-    resolveURI()
+    # functions from module valid
+    validCtxtNormalizeAttributeValue()
+    validateDocument()
+    validateDocumentFinal()
+    validateDtd()
+    validateDtdFinal()
+    validateElement()
+    validateNotationUse()
+    validateOneAttribute()
+    validateOneElement()
+    validateOneNamespace()
+    validatePopElement()
+    validatePushCData()
+    validatePushElement()
+    validateRoot()
 
 
 Class xmlElement(xmlNode)
@@ -786,6 +805,18 @@
     regexpExec()
     regexpIsDeterminist()
     regexpPrint()
+Class catalog()
+
+    # functions from module catalog
+    add()
+    catalogIsEmpty()
+    convertSGMLCatalog()
+    dump()
+    remove()
+    resolve()
+    resolvePublic()
+    resolveSystem()
+    resolveURI()
 
 
 Class xmlEntity(xmlNode)
diff --git a/python/libxml_wrap.h b/python/libxml_wrap.h
index e821075..2758e99 100644
--- a/python/libxml_wrap.h
+++ b/python/libxml_wrap.h
@@ -73,6 +73,14 @@
     xmlParserCtxtPtr obj;
 } PyparserCtxt_Object;
 
+#define PyValidCtxt_Get(v) (((v) == Py_None) ? NULL : \
+	(((PyValidCtxt_Object *)(v))->obj))
+
+typedef struct {
+	PyObject_HEAD
+	xmlValidCtxtPtr obj;
+} PyValidCtxt_Object;
+
 #define Pycatalog_Get(v) (((v) == Py_None) ? NULL : \
         (((Pycatalog_Object *)(v))->obj))
 
@@ -213,6 +221,7 @@
 PyObject * libxml_xmlParserCtxtPtrWrap(xmlParserCtxtPtr ctxt);
 PyObject * libxml_xmlXPathParserContextPtrWrap(xmlXPathParserContextPtr ctxt);
 PyObject * libxml_xmlXPathObjectPtrWrap(xmlXPathObjectPtr obj);
+PyObject * libxml_xmlValidCtxtPtrWrap(xmlValidCtxtPtr valid);
 PyObject * libxml_xmlCatalogPtrWrap(xmlCatalogPtr obj);
 PyObject * libxml_xmlURIPtrWrap(xmlURIPtr uri);
 PyObject * libxml_xmlOutputBufferPtrWrap(xmlOutputBufferPtr buffer);
diff --git a/python/tests/Makefile.am b/python/tests/Makefile.am
index 722e0ec..1ae9b6c 100644
--- a/python/tests/Makefile.am
+++ b/python/tests/Makefile.am
@@ -36,12 +36,14 @@
     thread2.py \
     sync.py \
     tstLastError.py \
-    indexes.py
+    indexes.py \
+    dtdvalid.py
 
 XMLS=		\
     tst.xml	\
     valid.xml	\
-    invalid.xml
+    invalid.xml	\
+    test.dtd
 
 EXTRA_DIST = $(PYTESTS) $(XMLS)
 
diff --git a/python/tests/dtdvalid.py b/python/tests/dtdvalid.py
new file mode 100755
index 0000000..2011f2c
--- /dev/null
+++ b/python/tests/dtdvalid.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python -u
+import libxml2
+import sys
+
+# Memory debug specific
+libxml2.debugMemory(1)
+
+dtd="""<!ELEMENT foo EMPTY>"""
+instance="""<?xml version="1.0"?>
+<foo></foo>"""
+
+dtd = libxml2.parseDTD(None, 'test.dtd')
+ctxt = libxml2.newValidCtxt()
+doc = libxml2.parseDoc(instance)
+ret = doc.validateDtd(ctxt, dtd)
+if ret != 1:
+    print "error doing DTD validation"
+    sys.exit(1)
+
+doc.freeDoc()
+dtd.freeDtd()
+del dtd
+del ctxt
+
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+    print "OK"
+else:
+    print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+    libxml2.dumpMemory()
+
diff --git a/python/tests/test.dtd b/python/tests/test.dtd
new file mode 100644
index 0000000..b61b438
--- /dev/null
+++ b/python/tests/test.dtd
@@ -0,0 +1 @@
+<!ELEMENT foo EMPTY>
diff --git a/python/types.c b/python/types.c
index fa91a65..329d270 100644
--- a/python/types.c
+++ b/python/types.c
@@ -478,6 +478,26 @@
 }
 
 PyObject *
+libxml_xmlValidCtxtPtrWrap(xmlValidCtxtPtr valid)
+{
+	PyObject *ret;
+	
+#ifdef DEBUG
+	printf("libxml_xmlValidCtxtPtrWrap: valid = %p\n", valid);
+#endif
+	if (valid == NULL) {
+		Py_INCREF(Py_None);
+		return (Py_None);
+	}
+
+	ret = 
+		PyCObject_FromVoidPtrAndDesc((void *) valid,
+									 (char *) "xmlValidCtxtPtr", NULL);
+
+	return (ret);
+}
+
+PyObject *
 libxml_xmlCatalogPtrWrap(xmlCatalogPtr catal)
 {
     PyObject *ret;