applied patch from Malcolm Tredinnick fixing bug #154294 related to saving

* python/generator.py python/libxml.c python/tests/outbuf.py:
  applied patch from Malcolm Tredinnick fixing bug #154294
  related to saving to python file objects.
Daniel
diff --git a/python/generator.py b/python/generator.py
index df89c4e..04b3832 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -300,6 +300,13 @@
 #
 #######################################################################
 
+# Class methods which are written by hand in libxml.c but the Python-level
+# code is still automatically generated (so they are not in skip_function()).
+skip_impl = (
+    'xmlSaveFileTo',
+    'xmlSaveFormatFileTo',
+)
+
 def skip_function(name):
     if name[0:12] == "xmlXPathWrap":
         return 1
@@ -356,6 +363,9 @@
         return 0
     if skip_function(name) == 1:
         return 0
+    if name in skip_impl:
+	# Don't delete the function entry in the caller.
+	return 1
 
     c_call = "";
     format=""
diff --git a/python/libxml.c b/python/libxml.c
index 9e24314..99d9ac3 100644
--- a/python/libxml.c
+++ b/python/libxml.c
@@ -512,6 +512,11 @@
     if (!PyArg_ParseTuple(args, (char *)"O:xmlOutputBufferClose", &pyobj_out))
         return(NULL);
     out = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_out);
+    /* Buffer may already have been destroyed elsewhere. This is harmless. */
+    if (out == NULL) {
+	Py_INCREF(Py_None);
+	return(Py_None);
+    }
 
     c_retval = xmlOutputBufferClose(out);
     py_retval = libxml_intWrap((int) c_retval);
@@ -533,6 +538,53 @@
     py_retval = libxml_intWrap((int) c_retval);
     return(py_retval);
 }
+
+static PyObject *
+libxml_xmlSaveFileTo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
+    PyObject *py_retval;
+    int c_retval;
+    xmlOutputBufferPtr buf;
+    PyObject *pyobj_buf;
+    xmlDocPtr cur;
+    PyObject *pyobj_cur;
+    char * encoding;
+
+    if (!PyArg_ParseTuple(args, (char *)"OOz:xmlSaveFileTo", &pyobj_buf, &pyobj_cur, &encoding))
+        return(NULL);
+    buf = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_buf);
+    cur = (xmlDocPtr) PyxmlNode_Get(pyobj_cur);
+
+    c_retval = xmlSaveFileTo(buf, cur, encoding);
+	/* xmlSaveTo() freed the memory pointed to by buf, so record that in the
+	 * Python object. */
+    ((PyoutputBuffer_Object *)(pyobj_buf))->obj = NULL;
+    py_retval = libxml_intWrap((int) c_retval);
+    return(py_retval);
+}
+
+static PyObject *
+libxml_xmlSaveFormatFileTo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) {
+    PyObject *py_retval;
+    int c_retval;
+    xmlOutputBufferPtr buf;
+    PyObject *pyobj_buf;
+    xmlDocPtr cur;
+    PyObject *pyobj_cur;
+    char * encoding;
+    int format;
+
+    if (!PyArg_ParseTuple(args, (char *)"OOzi:xmlSaveFormatFileTo", &pyobj_buf, &pyobj_cur, &encoding, &format))
+        return(NULL);
+    buf = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_buf);
+    cur = (xmlDocPtr) PyxmlNode_Get(pyobj_cur);
+
+    c_retval = xmlSaveFormatFileTo(buf, cur, encoding, format);
+	/* xmlSaveFormatFileTo() freed the memory pointed to by buf, so record that
+	 * in the Python object */
+	((PyoutputBuffer_Object *)(pyobj_buf))->obj = NULL;
+    py_retval = libxml_intWrap((int) c_retval);
+    return(py_retval);
+}
 #endif /* LIBXML_OUTPUT_ENABLED */
 
 
@@ -3446,6 +3498,8 @@
     {(char *) "outputBufferGetPythonFile", libxml_outputBufferGetPythonFile, METH_VARARGS, NULL},
     {(char *) "xmlOutputBufferClose", libxml_xmlOutputBufferClose, METH_VARARGS, NULL},
     { (char *)"xmlOutputBufferFlush", libxml_xmlOutputBufferFlush, METH_VARARGS, NULL },
+    { (char *)"xmlSaveFileTo", libxml_xmlSaveFileTo, METH_VARARGS, NULL },
+    { (char *)"xmlSaveFormatFileTo", libxml_xmlSaveFormatFileTo, METH_VARARGS, NULL },
 #endif /* LIBXML_OUTPUT_ENABLED */
     {(char *) "inputBufferCreate", libxml_xmlCreateInputBuffer, METH_VARARGS, NULL},
     {(char *) "setEntityLoader", libxml_xmlSetEntityLoader, METH_VARARGS, NULL},
diff --git a/python/tests/outbuf.py b/python/tests/outbuf.py
index 4213159..09cd9b5 100755
--- a/python/tests/outbuf.py
+++ b/python/tests/outbuf.py
@@ -3,31 +3,103 @@
 import libxml2
 import StringIO
 
-#print "Skipped"
-#sys.exit(1)
+def testSimpleBufferWrites():
+    f = StringIO.StringIO()
+    buf = libxml2.createOutputBuffer(f, "ISO-8859-1")
+    buf.write(3, "foo")
+    buf.writeString("bar")
+    buf.close()
+    
+    if f.getvalue() != "foobar":
+        print "Failed to save to StringIO"
+        sys.exit(1)
 
-# Memory debug specific
-libxml2.debugMemory(1)
+def testSaveDocToBuffer():
+    """
+    Regression test for bug #154294.
+    """
+    input = '<foo>Hello</foo>'
+    expected = '''\
+<?xml version="1.0" encoding="UTF-8"?>
+<foo>Hello</foo>
+'''
+    f = StringIO.StringIO()
+    buf = libxml2.createOutputBuffer(f, 'UTF-8')
+    doc = libxml2.parseDoc(input)
+    doc.saveFileTo(buf, 'UTF-8')
+    doc.freeDoc()
+    if f.getvalue() != expected:
+        print 'xmlDoc.saveFileTo() call failed.'
+        print '     got: %s' % repr(f.getvalue())
+        print 'expected: %s' % repr(expected)
+        sys.exit(1)
 
-#f = open('res', 'w')
-f = StringIO.StringIO()
-buf = libxml2.createOutputBuffer(f, "ISO-8859-1")
-buf.write(3, "foo")
-buf.writeString("bar")
-buf.close()
+def testSaveFormattedDocToBuffer():
+    input = '<outer><inner>Some text</inner><inner/></outer>'
+    # The formatted and non-formatted versions of the output.
+    expected = ('''\
+<?xml version="1.0" encoding="UTF-8"?>
+<outer><inner>Some text</inner><inner/></outer>
+''', '''\
+<?xml version="1.0" encoding="UTF-8"?>
+<outer>
+  <inner>Some text</inner>
+  <inner/>
+</outer>
+''')
+    doc = libxml2.parseDoc(input)
+    for i in (0, 1):
+        f = StringIO.StringIO()
+        buf = libxml2.createOutputBuffer(f, 'UTF-8')
+        doc.saveFormatFileTo(buf, 'UTF-8', i)
+        if f.getvalue() != expected[i]:
+            print 'xmlDoc.saveFormatFileTo() call failed.'
+            print '     got: %s' % repr(f.getvalue())
+            print 'expected: %s' % repr(expected[i])
+            sys.exit(1)
+    doc.freeDoc()
 
-if f.getvalue() != "foobar":
-    print "Failed to save to StringIO"
-    sys.exit(1)
+def testSaveIntoOutputBuffer():
+    """
+    Similar to the previous two tests, except this time we invoke the save
+    methods on the output buffer object and pass in an XML node object.
+    """
+    input = '<foo>Hello</foo>'
+    expected = '''\
+<?xml version="1.0" encoding="UTF-8"?>
+<foo>Hello</foo>
+'''
+    f = StringIO.StringIO()
+    doc = libxml2.parseDoc(input)
+    buf = libxml2.createOutputBuffer(f, 'UTF-8')
+    buf.saveFileTo(doc, 'UTF-8')
+    if f.getvalue() != expected:
+        print 'outputBuffer.saveFileTo() call failed.'
+        print '     got: %s' % repr(f.getvalue())
+        print 'expected: %s' % repr(expected)
+        sys.exit(1)
+    f = StringIO.StringIO()
+    buf = libxml2.createOutputBuffer(f, 'UTF-8')
+    buf.saveFormatFileTo(doc, 'UTF-8', 1)
+    if f.getvalue() != expected:
+        print 'outputBuffer.saveFormatFileTo() call failed.'
+        print '     got: %s' % repr(f.getvalue())
+        print 'expected: %s' % repr(expected)
+        sys.exit(1)
+    doc.freeDoc()
 
-del buf
-del f
+if __name__ == '__main__':
+    # Memory debug specific
+    libxml2.debugMemory(1)
 
-# Memory debug specific
-libxml2.cleanupParser()
-if libxml2.debugMemory(1) == 0:
-    print "OK"
-else:
-    print "Memory leak %d bytes" % (libxml2.debugMemory(1))
-    libxml2.dumpMemory()
+    testSimpleBufferWrites()
+    testSaveDocToBuffer()
+    testSaveFormattedDocToBuffer()
+    testSaveIntoOutputBuffer()
 
+    libxml2.cleanupParser()
+    if libxml2.debugMemory(1) == 0:
+        print "OK"
+    else:
+        print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+        libxml2.dumpMemory()