applied patch from Anthony Carrico providing Python bindings for the

* python/libxml.c python/libxml.py: applied patch from Anthony Carrico
  providing Python bindings for the Canonicalization C14N support.
Daniel
diff --git a/ChangeLog b/ChangeLog
index 0b3eb5a..4d9086d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Tue Mar  9 09:59:25 CET 2004 Daniel Veillard <daniel@veillard.com>
+
+	* python/libxml.c python/libxml.py: applied patch from Anthony Carrico
+	  providing Python bindings for the Canonicalization C14N support.
+
 Mon Mar  8 11:12:23 CET 2004 Hagen Moebius <hagen.moebius@starschiffchen.de>
 
 	* .cvsignore and python/.cvsignore patched
diff --git a/python/libxml.c b/python/libxml.c
index 52decf4..7a19303 100644
--- a/python/libxml.c
+++ b/python/libxml.c
@@ -22,6 +22,7 @@
 #include <libxml/xpathInternals.h>
 #include <libxml/xmlmemory.h>
 #include <libxml/xmlIO.h>
+#include <libxml/c14n.h>
 #include "libxml_wrap.h"
 #include "libxml2-py.h"
 
@@ -421,7 +422,7 @@
     return(file);
 }
 
-PyObject *
+static PyObject *
 libxml_xmlOutputBufferClose(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
     PyObject *py_retval;
     int c_retval;
@@ -437,7 +438,7 @@
     return(py_retval);
 }
 
-PyObject *
+static PyObject *
 libxml_xmlOutputBufferFlush(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
     PyObject *py_retval;
     int c_retval;
@@ -2858,6 +2859,301 @@
 
 #endif
 
+#ifdef LIBXML_C14N_ENABLED
+#ifdef LIBXML_OUTPUT_ENABLED
+
+/************************************************************************
+ *                                                                      *
+ * XML Canonicalization c14n                                            *
+ *                                                                      *
+ ************************************************************************/
+
+static int
+PyxmlNodeSet_Convert(PyObject *py_nodeset, xmlNodeSetPtr *result)
+{
+    xmlNodeSetPtr nodeSet;
+    int is_tuple = 0;
+
+    if (PyTuple_Check(py_nodeset))
+        is_tuple = 1;
+    else if (PyList_Check(py_nodeset))
+        is_tuple = 0;
+    else if (py_nodeset == Py_None) {
+        *result = NULL;
+        return 0;
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                        "must be a tuple or list of nodes.");
+        return -1;
+    }
+
+    nodeSet = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
+    if (nodeSet == NULL) {
+        PyErr_SetString(PyExc_MemoryError, "");
+        return -1;
+    }
+
+    nodeSet->nodeNr = 0;
+    nodeSet->nodeMax = (is_tuple
+                        ? PyTuple_GET_SIZE(py_nodeset)
+                        : PyList_GET_SIZE(py_nodeset));
+    nodeSet->nodeTab
+        = (xmlNodePtr *) xmlMalloc (nodeSet->nodeMax
+                                    * sizeof(xmlNodePtr));
+    if (nodeSet->nodeTab == NULL) {
+        xmlFree(nodeSet);
+        PyErr_SetString(PyExc_MemoryError, "");
+        return -1;
+    }
+    memset(nodeSet->nodeTab, 0 ,
+           nodeSet->nodeMax * sizeof(xmlNodePtr));
+
+    {
+        int idx;
+        for (idx=0; idx < nodeSet->nodeMax; ++idx) {
+            xmlNodePtr pynode =
+                PyxmlNode_Get (is_tuple
+                               ? PyTuple_GET_ITEM(py_nodeset, idx)
+                               : PyList_GET_ITEM(py_nodeset, idx));
+            if (pynode)
+                nodeSet->nodeTab[nodeSet->nodeNr++] = pynode;
+        }
+    }
+    *result = nodeSet;
+    return 0;
+}
+
+static int
+PystringSet_Convert(PyObject *py_strings, xmlChar *** result)
+{
+    /* NOTE: the array should be freed, but the strings are shared
+       with the python strings and so must not be freed. */
+
+    xmlChar ** strings;
+    int is_tuple = 0;
+    int count;
+    int init_index = 0;
+
+    if (PyTuple_Check(py_strings))
+        is_tuple = 1;
+    else if (PyList_Check(py_strings))
+        is_tuple = 0;
+    else if (py_strings == Py_None) {
+        *result = NULL;
+        return 0;
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError,
+                        "must be a tuple or list of strings.");
+        return -1;
+    }
+
+    count = (is_tuple
+             ? PyTuple_GET_SIZE(py_strings)
+             : PyList_GET_SIZE(py_strings));
+
+    strings = (xmlChar **) xmlMalloc(sizeof(xmlChar *) * count);
+
+    if (strings == NULL) {
+        PyErr_SetString(PyExc_MemoryError, "");
+        return -1;
+    }
+
+    memset(strings, 0 , sizeof(xmlChar *) * count);
+
+    {
+        int idx;
+        for (idx=0; idx < count; ++idx) {
+            char* s = PyString_AsString
+                (is_tuple
+                 ? PyTuple_GET_ITEM(py_strings, idx)
+                 : PyList_GET_ITEM(py_strings, idx));
+            if (s)
+                strings[init_index++] = (xmlChar *)s;
+            else {
+                xmlFree(strings);
+                PyErr_SetString(PyExc_TypeError,
+                                "must be a tuple or list of strings.");
+                return -1;
+            }
+        }
+    }
+
+    *result = strings;
+    return 0;
+}
+
+static PyObject *
+libxml_C14NDocDumpMemory(ATTRIBUTE_UNUSED PyObject * self,
+                         PyObject * args)
+{
+    PyObject *py_retval = NULL;
+
+    PyObject *pyobj_doc;
+    PyObject *pyobj_nodes;
+    int exclusive;
+    PyObject *pyobj_prefixes;
+    int with_comments;
+
+    xmlDocPtr doc;
+    xmlNodeSetPtr nodes;
+    xmlChar **prefixes = NULL;
+    xmlChar *doc_txt;
+
+    int result;
+
+    if (!PyArg_ParseTuple(args, (char *) "OOiOi:C14NDocDumpMemory",
+                          &pyobj_doc,
+                          &pyobj_nodes,
+                          &exclusive,
+                          &pyobj_prefixes,
+                          &with_comments))
+        return (NULL);
+
+    doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc);
+    if (!doc) {
+        PyErr_SetString(PyExc_TypeError, "bad document.");
+        return NULL;
+    }
+
+    result = PyxmlNodeSet_Convert(pyobj_nodes, &nodes);
+    if (result < 0) return NULL;
+
+    if (exclusive) {
+        result = PystringSet_Convert(pyobj_prefixes, &prefixes);
+        if (result < 0) {
+            if (nodes) {
+                xmlFree(nodes->nodeTab);
+                xmlFree(nodes);
+            }
+            return NULL;
+        }
+    }
+
+    result = xmlC14NDocDumpMemory(doc,
+                                  nodes,
+                                  exclusive,
+                                  prefixes,
+                                  with_comments,
+                                  &doc_txt);
+
+    if (nodes) {
+        xmlFree(nodes->nodeTab);
+        xmlFree(nodes);
+    }
+    if (prefixes) {
+        xmlChar ** idx = prefixes;
+        while (*idx) xmlFree(*(idx++));
+        xmlFree(prefixes);
+    }
+
+    if (result < 0) {
+        PyErr_SetString(PyExc_Exception,
+                        "libxml2 xmlC14NDocDumpMemory failure.");
+        return NULL;
+    }
+    else {
+        py_retval = PyString_FromStringAndSize((const char *) doc_txt,
+                                               result);
+        xmlFree(doc_txt);
+        return py_retval;
+    }
+}
+
+static PyObject *
+libxml_C14NDocSaveTo(ATTRIBUTE_UNUSED PyObject * self,
+                     PyObject * args)
+{
+    PyObject *pyobj_doc;
+    PyObject *py_file;
+    PyObject *pyobj_nodes;
+    int exclusive;
+    PyObject *pyobj_prefixes;
+    int with_comments;
+
+    xmlDocPtr doc;
+    xmlNodeSetPtr nodes;
+    xmlChar **prefixes = NULL;
+    FILE * output;
+    xmlOutputBufferPtr buf;
+
+    int result;
+    int len;
+
+    if (!PyArg_ParseTuple(args, (char *) "OOiOiO:C14NDocSaveTo",
+                          &pyobj_doc,
+                          &pyobj_nodes,
+                          &exclusive,
+                          &pyobj_prefixes,
+                          &with_comments,
+                          &py_file))
+        return (NULL);
+
+    doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc);
+    if (!doc) {
+        PyErr_SetString(PyExc_TypeError, "bad document.");
+        return NULL;
+    }
+
+    if ((py_file == NULL) || (!(PyFile_Check(py_file)))) {
+        PyErr_SetString(PyExc_TypeError, "bad file.");
+        return NULL;
+    }
+    output = PyFile_AsFile(py_file);
+    if (output == NULL) {
+        PyErr_SetString(PyExc_TypeError, "bad file.");
+        return NULL;
+    }
+    buf = xmlOutputBufferCreateFile(output, NULL);
+
+    result = PyxmlNodeSet_Convert(pyobj_nodes, &nodes);
+    if (result < 0) return NULL;
+
+    if (exclusive) {
+        result = PystringSet_Convert(pyobj_prefixes, &prefixes);
+        if (result < 0) {
+            if (nodes) {
+                xmlFree(nodes->nodeTab);
+                xmlFree(nodes);
+            }
+            return NULL;
+        }
+    }
+
+    result = xmlC14NDocSaveTo(doc,
+                              nodes,
+                              exclusive,
+                              prefixes,
+                              with_comments,
+                              buf);
+
+    if (nodes) {
+        xmlFree(nodes->nodeTab);
+        xmlFree(nodes);
+    }
+    if (prefixes) {
+        xmlChar ** idx = prefixes;
+        while (*idx) xmlFree(*(idx++));
+        xmlFree(prefixes);
+    }
+
+    len = xmlOutputBufferClose(buf);
+
+    if (result < 0) {
+        PyErr_SetString(PyExc_Exception,
+                        "libxml2 xmlC14NDocSaveTo failure.");
+        return NULL;
+    }
+    else
+        return PyInt_FromLong((long) len);
+}
+
+#endif
+#endif
+
+
+
 /************************************************************************
  *									*
  *			The registration stuff				*
@@ -2897,6 +3193,12 @@
     {(char *)"xmlRelaxNGSetValidErrors", libxml_xmlRelaxNGSetValidErrors, METH_VARARGS, NULL},
     {(char *)"xmlRelaxNGFreeValidCtxt", libxml_xmlRelaxNGFreeValidCtxt, METH_VARARGS, NULL},
 #endif
+#ifdef LIBXML_C14N_ENABLED
+#ifdef LIBXML_OUTPUT_ENABLED
+    {(char *)"xmlC14NDocDumpMemory", libxml_C14NDocDumpMemory, METH_VARARGS, NULL},
+    {(char *)"xmlC14NDocSaveTo", libxml_C14NDocSaveTo, METH_VARARGS, NULL},
+#endif
+#endif
     {NULL, NULL, 0, NULL}
 };
 
diff --git a/python/libxml.py b/python/libxml.py
index 4d643f9..0c3ba6c 100644
--- a/python/libxml.py
+++ b/python/libxml.py
@@ -349,6 +349,48 @@
         return libxml2mod.saveNodeTo(self._o, file, encoding, format)
             
     #
+    # Canonicalization routines:
+    #
+    #   nodes: the node set (tuple or list) to be included in the
+    #     canonized image or None if all document nodes should be
+    #     included.
+    #   exclusive: the exclusive flag (0 - non-exclusive
+    #     canonicalization; otherwise - exclusive canonicalization)
+    #   prefixes: the list of inclusive namespace prefixes (strings),
+    #     or None if there is no inclusive namespaces (only for
+    #     exclusive canonicalization, ignored otherwise)
+    #   with_comments: include comments in the result (!=0) or not
+    #     (==0)
+    def c14nMemory(self,
+                   nodes=None,
+                   exclusive=0,
+                   prefixes=None,
+                   with_comments=0):
+        if nodes:
+            nodes = map(lambda n: n._o, nodes)
+        return libxml2mod.xmlC14NDocDumpMemory(
+            self.get_doc()._o,
+            nodes,
+            exclusive != 0,
+            prefixes,
+            with_comments != 0)
+    def c14nSaveTo(self,
+                   file,
+                   nodes=None,
+                   exclusive=0,
+                   prefixes=None,
+                   with_comments=0):
+        if nodes:
+            nodes = map(lambda n: n._o, nodes)
+        return libxml2mod.xmlC14NDocSaveTo(
+            self.get_doc()._o,
+            nodes,
+            exclusive != 0,
+            prefixes,
+            with_comments != 0,
+            file)
+
+    #
     # Selecting nodes using XPath, a bit slow because the context
     # is allocated/freed every time but convenient.
     #