Checkin of Jack's buffer mods.
Not really checked, but didn't fail any tests either...
diff --git a/Include/object.h b/Include/object.h
index 54b5481..45bb41e 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -146,6 +146,9 @@
 typedef int(*intobjargproc) Py_PROTO((PyObject *, int, PyObject *));
 typedef int(*intintobjargproc) Py_PROTO((PyObject *, int, int, PyObject *));
 typedef int(*objobjargproc) Py_PROTO((PyObject *, PyObject *, PyObject *));
+typedef int (*getreadbufferproc) Py_PROTO((PyObject *, int, void **));
+typedef int (*getwritebufferproc) Py_PROTO((PyObject *, int, void **));
+typedef int (*getsegcountproc) Py_PROTO((PyObject *, int *));
 
 typedef struct {
 	binaryfunc nb_add;
@@ -189,6 +192,13 @@
 	objobjargproc mp_ass_subscript;
 } PyMappingMethods;
 
+typedef struct {
+	getreadbufferproc bf_getreadbuffer;
+	getwritebufferproc bf_getwritebuffer;
+	getsegcountproc bf_getsegcount;
+} PyBufferProcs;
+	
+
 typedef void (*destructor) Py_PROTO((PyObject *));
 typedef int (*printfunc) Py_PROTO((PyObject *, FILE *, int));
 typedef PyObject *(*getattrfunc) Py_PROTO((PyObject *, char *));
@@ -227,8 +237,10 @@
 	getattrofunc tp_getattro;
 	setattrofunc tp_setattro;
 
+	/* Functions to access object as input/output buffer */
+	PyBufferProcs *tp_as_buffer;
+	
 	/* Space for future expansion */
-	long tp_xxx3;
 	long tp_xxx4;
 
 	char *tp_doc; /* Documentation string */
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index e732f39..50cadd0 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -1169,6 +1169,44 @@
 	return s;
 }
 
+static int
+array_buffer_getreadbuf(self, index, ptr)
+	arrayobject *self;
+	int index;
+	const void **ptr;
+{
+	if ( index != 0 ) {
+		PyErr_SetString(PyExc_SystemError, "Accessing non-existent array segment");
+		return -1;
+	}
+	*ptr = (void *)self->ob_item;
+	return self->ob_size*self->ob_descr->itemsize;
+}
+
+static int
+array_buffer_getwritebuf(self, index, ptr)
+	arrayobject *self;
+	int index;
+	const void **ptr;
+{
+	if ( index != 0 ) {
+		PyErr_SetString(PyExc_SystemError, "Accessing non-existent array segment");
+		return -1;
+	}
+	*ptr = (void *)self->ob_item;
+	return self->ob_size*self->ob_descr->itemsize;
+}
+
+static int
+array_buffer_getsegcount(self, lenp)
+	arrayobject *self;
+	int *lenp;
+{
+	if ( lenp )
+		*lenp = self->ob_size*self->ob_descr->itemsize;
+	return 1;
+}
+
 static PySequenceMethods array_as_sequence = {
 	(inquiry)array_length,		        /*sq_length*/
 	(binaryfunc)array_concat,               /*sq_concat*/
@@ -1179,6 +1217,13 @@
 	(intintobjargproc)array_ass_slice,	/*sq_ass_slice*/
 };
 
+static PyBufferProcs array_as_buffer = {
+	(getreadbufferproc)array_buffer_getreadbuf,
+	(getwritebufferproc)array_buffer_getwritebuf,
+	(getsegcountproc)array_buffer_getsegcount,
+};
+
+
 statichere PyTypeObject Arraytype = {
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
@@ -1194,6 +1239,14 @@
 	0,				/*tp_as_number*/
 	&array_as_sequence,		/*tp_as_sequence*/
 	0,				/*tp_as_mapping*/
+	0, 				/*tp_hash*/
+	0,				/*tp_call*/
+	0,				/*tp_str*/
+	0,				/*tp_getattro*/
+	0,				/*tp_setattro*/
+	&array_as_buffer,		/*tp_as_buffer*/
+	0,				/*tp_xxx4*/
+	0,				/*tp_doc*/
 };
 
 
diff --git a/Objects/fileobject.c b/Objects/fileobject.c
index cee41a1..a4b1d4b 100644
--- a/Objects/fileobject.c
+++ b/Objects/fileobject.c
@@ -419,6 +419,41 @@
 	return v;
 }
 
+static PyObject *
+file_readinto(f, args)
+	PyFileObject *f;
+	PyObject *args;
+{
+	char *ptr;
+	int ntodo, ndone, nnow;
+	
+	if (f->f_fp == NULL)
+		return err_closed();
+	if (!PyArg_Parse(args, "w#", &ptr, &ntodo))
+		return NULL;
+	ndone = 0;
+	/* 
+	** XXXX Is this correct? Other threads may see partially-completed
+	** reads if they look at the object we're reading into...
+	*/
+	Py_BEGIN_ALLOW_THREADS
+	while(ntodo > 0) {
+		nnow = fread(ptr+ndone, 1, ntodo, f->f_fp);
+		if (nnow < 0 ) {
+			PyErr_SetFromErrno(PyExc_IOError);
+			clearerr(f->f_fp);
+			return NULL;
+		}
+		if (nnow == 0)
+			break;
+		ndone += nnow;
+		ntodo -= nnow;
+	}
+	Py_END_ALLOW_THREADS
+	return PyInt_FromLong(ndone);
+}
+
+
 /* Internal routine to get a line.
    Size argument interpretation:
    > 0: max length;
@@ -688,6 +723,7 @@
 	{"tell",	(PyCFunction)file_tell, 0},
 	{"write",	(PyCFunction)file_write, 0},
 	{"writelines",	(PyCFunction)file_writelines, 0},
+	{"readinto",	(PyCFunction)file_readinto, 0},
 	{NULL,		NULL}		/* sentinel */
 };
 
diff --git a/Objects/stringobject.c b/Objects/stringobject.c
index 1f95aa1..dbcb1a9 100644
--- a/Objects/stringobject.c
+++ b/Objects/stringobject.c
@@ -456,6 +456,40 @@
 	return x;
 }
 
+static int
+string_buffer_getreadbuf(self, index, ptr)
+	PyStringObject *self;
+	int index;
+	const void **ptr;
+{
+	if ( index != 0 ) {
+		PyErr_SetString(PyExc_SystemError, "Accessing non-existent string segment");
+		return -1;
+	}
+	*ptr = (void *)self->ob_sval;
+	return self->ob_size;
+}
+
+static int
+string_buffer_getwritebuf(self, index, ptr)
+	PyStringObject *self;
+	int index;
+	const void **ptr;
+{
+	PyErr_SetString(PyExc_TypeError, "Cannot use string as modifyable buffer");
+	return -1;
+}
+
+static int
+string_buffer_getsegcount(self, lenp)
+	PyStringObject *self;
+	int *lenp;
+{
+	if ( lenp )
+		*lenp = self->ob_size;
+	return 1;
+}
+
 static PySequenceMethods string_as_sequence = {
 	(inquiry)string_length, /*sq_length*/
 	(binaryfunc)string_concat, /*sq_concat*/
@@ -466,6 +500,12 @@
 	0,		/*sq_ass_slice*/
 };
 
+static PyBufferProcs string_as_buffer = {
+	(getreadbufferproc)string_buffer_getreadbuf,
+	(getwritebufferproc)string_buffer_getwritebuf,
+	(getsegcountproc)string_buffer_getsegcount,
+};
+
 PyTypeObject PyString_Type = {
 	PyObject_HEAD_INIT(&PyType_Type)
 	0,
@@ -486,7 +526,7 @@
 	0,		/*tp_str*/
 	0,		/*tp_getattro*/
 	0,		/*tp_setattro*/
-	0,		/*tp_xxx3*/
+	&string_as_buffer,	/*tp_as_buffer*/
 	0,		/*tp_xxx4*/
 	0,		/*tp_doc*/
 };
diff --git a/Python/getargs.c b/Python/getargs.c
index a2555ce..f166921 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -539,41 +539,81 @@
 	
 	case 's': /* string */
 		{
-			char **p = va_arg(*p_va, char **);
-			if (PyString_Check(arg))
-				*p = PyString_AsString(arg);
-			else
-				return "string";
-			if (*format == '#') {
+			if (*format == '#') { /* any buffer-like object */
+			        void **p = (void **)va_arg(*p_va, char **);
+			        PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
 				int *q = va_arg(*p_va, int *);
-				*q = PyString_Size(arg);
+				int count;
+
+				if ( pb == NULL ||
+				     pb->bf_getreadbuffer == NULL ||
+				     pb->bf_getsegcount == NULL )
+				  return "read-only buffer";
+				if ( (*pb->bf_getsegcount)(arg, NULL) != 1 )
+				  return "single-segment read-only buffer";
+				if ( (count =
+				      (*pb->bf_getreadbuffer)(arg, 0, p)) < 0 )
+				  return "(unspecified)";
+				*q = count;
 				format++;
+			} else {
+			        char **p = va_arg(*p_va, char **);
+			
+			        if (PyString_Check(arg))
+				  *p = PyString_AsString(arg);
+				else
+				  return "string";
+				if ((int)strlen(*p) != PyString_Size(arg))
+				  return "string without null bytes";
 			}
-			else if ((int)strlen(*p) != PyString_Size(arg))
-				return "string without null bytes";
 			break;
 		}
-	
+
 	case 'z': /* string, may be NULL (None) */
 		{
-			char **p = va_arg(*p_va, char **);
-			if (arg == Py_None)
-				*p = 0;
-			else if (PyString_Check(arg))
-				*p = PyString_AsString(arg);
-			else
-				return "None or string";
-			if (*format == '#') {
+			if (*format == '#') { /* any buffer-like object */
+			        void **p = (void **)va_arg(*p_va, char **);
+			        PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
 				int *q = va_arg(*p_va, int *);
-				if (arg == Py_None)
-					*q = 0;
-				else
-					*q = PyString_Size(arg);
+				int count;
+
+				if (arg == Py_None) {
+				  *p = 0;
+				  *q = 0;
+				} else {
+				  if ( pb == NULL ||
+				       pb->bf_getreadbuffer == NULL ||
+				       pb->bf_getsegcount == NULL )
+				    return "read-only buffer";
+				  if ( (*pb->bf_getsegcount)(arg, NULL) != 1 )
+				  return "single-segment read-only buffer";
+				  if ( (count = (*pb->bf_getreadbuffer)
+					                    (arg, 0, p)) < 0 )
+				    return "(unspecified)";
+				  *q = count;
+				}
 				format++;
+			} else {
+			        char **p = va_arg(*p_va, char **);
+			
+			        if (arg == Py_None)
+				  *p = 0;
+				else if (PyString_Check(arg))
+				  *p = PyString_AsString(arg);
+				else
+				  return "None or string";
+				if (*format == '#') {
+				  int *q = va_arg(*p_va, int *);
+				  if (arg == Py_None)
+				    *q = 0;
+				  else
+				    *q = PyString_Size(arg);
+				  format++;
+				}
+				else if (*p != NULL &&
+					 (int)strlen(*p) != PyString_Size(arg))
+				  return "None or string without null bytes";
 			}
-			else if (*p != NULL &&
-				 (int)strlen(*p) != PyString_Size(arg))
-				return "None or string without null bytes";
 			break;
 		}
 	
@@ -624,6 +664,30 @@
 			}
 			break;
 		}
+		
+		
+	case 'w': /* memory buffer, read-write access */
+		{
+			void **p = va_arg(*p_va, void **);
+			PyBufferProcs *pb = arg->ob_type->tp_as_buffer;
+			int count;
+			
+			if ( pb == NULL || pb->bf_getwritebuffer == NULL ||
+					pb->bf_getsegcount == NULL )
+				return "read-write buffer";
+			if ( (*pb->bf_getsegcount)(arg, NULL) != 1 )
+				return "single-segment read-write buffer";
+			if ( (count = pb->bf_getwritebuffer(arg, 0, p)) < 0 )
+				return "(unspecified)";
+			if (*format == '#') {
+				int *q = va_arg(*p_va, int *);
+				
+				*q = count;
+				format++;
+			}
+			break;
+		}
+		
 	
 	default:
 		return "impossible<bad format char>";