CVS patch #477161:  New "access" keyword for mmap, from Jay T Miller.
This gives mmap() on Windows the ability to create read-only, write-
through and copy-on-write mmaps.  A new keyword argument is introduced
because the mmap() signatures diverged between Windows and Unix, so
while they (now) both support this functionality, there wasn't a way to
spell it in a common way without introducing a new spelling gimmick.
The old spellings are still accepted, so there isn't a backward-
compatibility issue here.
diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c
index 8d57e90..a4ec2d0 100644
--- a/Modules/mmapmodule.c
+++ b/Modules/mmapmodule.c
@@ -29,9 +29,9 @@
 static int
 my_getpagesize(void)
 {
-    SYSTEM_INFO si;
-    GetSystemInfo(&si);
-    return si.dwPageSize;
+	SYSTEM_INFO si;
+	GetSystemInfo(&si);
+	return si.dwPageSize;
 }
 #endif
 
@@ -49,7 +49,7 @@
 static int
 my_getpagesize(void)
 {
-    return sysconf(_SC_PAGESIZE);
+	return sysconf(_SC_PAGESIZE);
 }
 #else
 #define my_getpagesize getpagesize
@@ -62,6 +62,14 @@
 
 static PyObject *mmap_module_error;
 
+typedef enum
+{
+	ACCESS_DEFAULT,
+	ACCESS_READ,
+	ACCESS_WRITE,
+	ACCESS_COPY
+} access_mode;
+
 typedef struct {
 	PyObject_HEAD
 	char *	data;
@@ -77,8 +85,11 @@
 #ifdef UNIX
         int fd;
 #endif
+
+        access_mode access;
 } mmap_object;
 
+
 static void
 mmap_object_dealloc(mmap_object *m_obj)
 {
@@ -178,7 +189,7 @@
 
 static PyObject *
 mmap_read_line_method(mmap_object *self,
-		     PyObject *args)
+		      PyObject *args)
 {
 	char *start = self->data+self->pos;
 	char *eof = self->data+self->size;
@@ -236,11 +247,11 @@
 		char *e = self->data + self->size;
 
                 if (start < 0)
-                    start += self->size;
+			start += self->size;
                 if (start < 0)
-                    start = 0;
+			start = 0;
                 else if ((size_t)start > self->size)
-                    start = self->size;
+			start = self->size;
                 p = self->data + start;
 
 		while (p < e) {
@@ -260,6 +271,26 @@
 	}
 }
 
+static int 
+is_writeable(mmap_object *self)
+{
+	if (self->access != ACCESS_READ)
+		return 1; 
+	PyErr_Format(PyExc_TypeError, "mmap can't modify a readonly memory map.");
+	return 0;
+}
+
+static int 
+is_resizeable(mmap_object *self)
+{
+	if ((self->access == ACCESS_WRITE) || (self->access == ACCESS_DEFAULT))
+		return 1; 
+	PyErr_Format(PyExc_TypeError, 
+		     "mmap can't resize a readonly or copy-on-write memory map.");
+	return 0;
+}
+
+
 static PyObject *
 mmap_write_method(mmap_object *self,
 		  PyObject *args)
@@ -271,6 +302,9 @@
 	if (!PyArg_ParseTuple (args, "s#:write", &data, &length))
 		return(NULL);
 
+	if (!is_writeable(self))
+		return NULL;
+
 	if ((self->pos + length) > self->size) {
 		PyErr_SetString (PyExc_ValueError, "data out of range");
 		return NULL;
@@ -291,12 +325,14 @@
 	if (!PyArg_ParseTuple (args, "c:write_byte", &value))
 		return(NULL);
 
+	if (!is_writeable(self))
+		return NULL;
 	*(self->data+self->pos) = value;
 	self->pos += 1;
 	Py_INCREF (Py_None);
 	return (Py_None);
 }
-
+ 
 static PyObject *
 mmap_size_method(mmap_object *self,
 		 PyObject *args)
@@ -342,7 +378,8 @@
 {
 	unsigned long new_size;
 	CHECK_VALID(NULL);
-	if (!PyArg_ParseTuple (args, "l:resize", &new_size)) {
+	if (!PyArg_ParseTuple (args, "l:resize", &new_size) || 
+	    !is_resizeable(self)) {
 		return NULL;
 #ifdef MS_WIN32
 	} else { 
@@ -386,31 +423,31 @@
 
 #ifdef UNIX
 #ifndef HAVE_MREMAP 
-} else {
-	PyErr_SetString(PyExc_SystemError,
-			"mmap: resizing not available--no mremap()");
-	return NULL;
+	} else {
+		PyErr_SetString(PyExc_SystemError,
+				"mmap: resizing not available--no mremap()");
+		return NULL;
 #else
-} else {
-	void *newmap;
+	} else {
+		void *newmap;
 
 #ifdef MREMAP_MAYMOVE
-	newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
+		newmap = mremap(self->data, self->size, new_size, MREMAP_MAYMOVE);
 #else
-	newmap = mremap(self->data, self->size, new_size, 0);
+		newmap = mremap(self->data, self->size, new_size, 0);
 #endif
-	if (newmap == (void *)-1) 
-	{
-		PyErr_SetFromErrno(mmap_module_error);
-		return NULL;
-	}
-	self->data = newmap;
-	self->size = new_size;
-	Py_INCREF(Py_None);
-	return Py_None;
+		if (newmap == (void *)-1) 
+		{
+			PyErr_SetFromErrno(mmap_module_error);
+			return NULL;
+		}
+		self->data = newmap;
+		self->size = new_size;
+		Py_INCREF(Py_None);
+		return Py_None;
 #endif /* HAVE_MREMAP */
 #endif /* UNIX */
-}
+	}
 }
 
 static PyObject *
@@ -491,7 +528,7 @@
 		return (Py_None);
 	}
 
-onoutofrange:
+  onoutofrange:
 	PyErr_SetString (PyExc_ValueError, "seek out of range");
 	return NULL;
 }
@@ -501,7 +538,8 @@
 {
 	unsigned long dest, src, count;
 	CHECK_VALID(NULL);
-	if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count)) {
+	if (!PyArg_ParseTuple (args, "iii:move", &dest, &src, &count) ||
+	    !is_writeable(self)) {
 		return NULL;
 	} else {
 		/* bounds check the values */
@@ -561,6 +599,8 @@
 				"Accessing non-existent mmap segment");
 		return -1;
 	}
+	if (!is_writeable(self))
+		return -1;
 	*ptr = self->data;
 	return self->size;
 }
@@ -665,7 +705,7 @@
     
 	if (v == NULL) {
 		PyErr_SetString(PyExc_TypeError,
-			"mmap object doesn't support slice deletion");
+				"mmap object doesn't support slice deletion");
 		return -1;
 	}
 	if (! (PyString_Check(v)) ) {
@@ -678,6 +718,8 @@
 				"mmap slice assignment is wrong size");
 		return -1;
 	}
+	if (!is_writeable(self))
+		return -1;
 	buf = PyString_AsString(v);
 	memcpy(self->data + ilow, buf, ihigh-ilow);
 	return 0;
@@ -695,14 +737,16 @@
 	}
 	if (v == NULL) {
 		PyErr_SetString(PyExc_TypeError,
-			"mmap object doesn't support item deletion");
+				"mmap object doesn't support item deletion");
 		return -1;
 	}
 	if (! (PyString_Check(v) && PyString_Size(v)==1) ) {
 		PyErr_SetString(PyExc_IndexError, 
-			"mmap assignment must be single-character string");
+				"mmap assignment must be single-character string");
 		return -1;
 	}
+	if (!is_writeable(self))
+		return -1;
 	buf = PyString_AsString(v);
 	self->data[i] = buf[0];
 	return 0;
@@ -792,18 +836,18 @@
 	}
 	else {
 		PyErr_SetString(PyExc_TypeError,
-			"map size must be an integral value");
+				"map size must be an integral value");
 		return -1;
 	}
 
-onnegoverflow:
+  onnegoverflow:
 	PyErr_SetString(PyExc_OverflowError,
-		"memory mapped size must be positive");
+			"memory mapped size must be positive");
 	return -1;
 
-onposoverflow:
+  onposoverflow:
 	PyErr_SetString(PyExc_OverflowError,
-		"memory mapped size is too large (limited by C int)");
+			"memory mapped size is too large (limited by C int)");
 	return -1;
 }
 
@@ -815,16 +859,42 @@
 	PyObject *map_size_obj = NULL;
 	int map_size;
 	int fd, flags = MAP_SHARED, prot = PROT_WRITE | PROT_READ;
-	char *keywords[] = {"file", "size", "flags", "prot", NULL};
+	access_mode access = ACCESS_DEFAULT;
+	char *keywords[] = {"fileno", "length", 
+			    "flags", "prot", 
+			    "access", NULL};
 
-	if (!PyArg_ParseTupleAndKeywords(args, kwdict, 
-					 "iO|ii", keywords, 
-					 &fd, &map_size_obj, &flags, &prot)
-		)
+	if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|iii", keywords, 
+					 &fd, &map_size_obj, &flags, &prot, &access))
 		return NULL;
 	map_size = _GetMapSize(map_size_obj);
 	if (map_size < 0)
 		return NULL;
+
+	if ((access != ACCESS_DEFAULT) && 
+	    ((flags != MAP_SHARED) || ( prot != (PROT_WRITE | PROT_READ))))
+		return PyErr_Format(PyExc_ValueError, 
+				    "mmap can't specify both access and flags, prot.");
+	switch(access) {
+	case ACCESS_READ:
+		flags = MAP_SHARED;
+		prot = PROT_READ;
+		break;
+	case ACCESS_WRITE:
+		flags = MAP_SHARED;
+		prot = PROT_READ | PROT_WRITE;
+		break;
+	case ACCESS_COPY:
+		flags = MAP_PRIVATE;
+		prot = PROT_READ | PROT_WRITE;
+		break;
+	case ACCESS_DEFAULT: 
+		/* use the specified or default values of flags and prot */
+		break;
+	default:
+		return PyErr_Format(PyExc_ValueError, 
+				    "mmap invalid access parameter.");
+	}
 	
 	m_obj = PyObject_New (mmap_object, &mmap_object_type);
 	if (m_obj == NULL) {return NULL;}
@@ -834,37 +904,57 @@
 	m_obj->data = mmap(NULL, map_size, 
 			   prot, flags,
 			   fd, 0);
-	if (m_obj->data == (char *)-1)
-	{
+	if (m_obj->data == (char *)-1) {
 		Py_DECREF(m_obj);
 		PyErr_SetFromErrno(mmap_module_error);
 		return NULL;
 	}
+	m_obj->access = access;
 	return (PyObject *)m_obj;
 }
 #endif /* UNIX */
 
 #ifdef MS_WIN32
 static PyObject *
-new_mmap_object(PyObject *self, PyObject *args)
+new_mmap_object(PyObject *self, PyObject *args, PyObject *kwdict)
 {
 	mmap_object *m_obj;
 	PyObject *map_size_obj = NULL;
 	int map_size;
 	char *tagname = "";
-
 	DWORD dwErr = 0;
 	int fileno;
 	HANDLE fh = 0;
+	access_mode   access = ACCESS_DEFAULT;
+	DWORD flProtect, dwDesiredAccess;
+	char *keywords[] = { "fileno", "length", 
+			     "tagname", 
+			     "access", NULL };
 
-	if (!PyArg_ParseTuple(args,
-			  "iO|z",
-			  &fileno,
-			  &map_size_obj,
-			  &tagname)
-		)
+	if (!PyArg_ParseTupleAndKeywords(args, kwdict, "iO|zi", keywords,
+					 &fileno, &map_size_obj, 
+					 &tagname, &access)) {
 		return NULL;
-  
+	}
+
+	switch(access) {
+	case ACCESS_READ:
+		flProtect = PAGE_READONLY;
+		dwDesiredAccess = FILE_MAP_READ;
+		break;
+	case ACCESS_DEFAULT:  case ACCESS_WRITE:
+		flProtect = PAGE_READWRITE;
+		dwDesiredAccess = FILE_MAP_WRITE;
+		break;
+	case ACCESS_COPY:
+		flProtect = PAGE_WRITECOPY;
+		dwDesiredAccess = FILE_MAP_COPY;
+		break;
+	default:
+		return PyErr_Format(PyExc_ValueError, 
+				    "mmap invalid access parameter.");
+	}
+
 	map_size = _GetMapSize(map_size_obj);
 	if (map_size < 0)
 		return NULL;
@@ -873,8 +963,8 @@
 	if (fileno != 0) {
 		fh = (HANDLE)_get_osfhandle(fileno);
 		if (fh==(HANDLE)-1) {
-		    PyErr_SetFromErrno(mmap_module_error);
-		    return NULL;
+			PyErr_SetFromErrno(mmap_module_error);
+			return NULL;
 		}
 		/* Win9x appears to need us seeked to zero */
 		fseek(&_iob[fileno], 0, SEEK_SET);
@@ -894,13 +984,13 @@
 		/* It is necessary to duplicate the handle, so the
 		   Python code can close it on us */
 		if (!DuplicateHandle(
-			    GetCurrentProcess(), /* source process handle */
-			    fh, /* handle to be duplicated */
-			    GetCurrentProcess(), /* target proc handle */
-			    (LPHANDLE)&m_obj->file_handle, /* result */
-			    0, /* access - ignored due to options value */
-			    FALSE, /* inherited by child processes? */
-			    DUPLICATE_SAME_ACCESS)) { /* options */
+			GetCurrentProcess(), /* source process handle */
+			fh, /* handle to be duplicated */
+			GetCurrentProcess(), /* target proc handle */
+			(LPHANDLE)&m_obj->file_handle, /* result */
+			0, /* access - ignored due to options value */
+			FALSE, /* inherited by child processes? */
+			DUPLICATE_SAME_ACCESS)) { /* options */
 			dwErr = GetLastError();
 			Py_DECREF(m_obj);
 			PyErr_SetFromWindowsErr(dwErr);
@@ -932,22 +1022,23 @@
 	else
 		m_obj->tagname = NULL;
 
+	m_obj->access = access;
 	m_obj->map_handle = CreateFileMapping (m_obj->file_handle,
 					       NULL,
-					       PAGE_READWRITE,
+					       flProtect,
 					       0,
 					       m_obj->size,
 					       m_obj->tagname);
 	if (m_obj->map_handle != NULL) {
 		m_obj->data = (char *) MapViewOfFile (m_obj->map_handle,
-						      FILE_MAP_WRITE,
+						      dwDesiredAccess,
 						      0,
 						      0,
 						      0);
 		if (m_obj->data != NULL) {
 			return ((PyObject *) m_obj);
 		} else {
-		    dwErr = GetLastError();
+			dwErr = GetLastError();
 		}
 	} else {
 		dwErr = GetLastError();
@@ -966,7 +1057,7 @@
 };
 
 DL_EXPORT(void)
-initmmap(void)
+	initmmap(void)
 {
 	PyObject *dict, *module;
 
@@ -1011,5 +1102,11 @@
 
 	PyDict_SetItemString (dict, "PAGESIZE",
 			      PyInt_FromLong( (long)my_getpagesize() ) );
-}
 
+	PyDict_SetItemString (dict, "ACCESS_READ",	
+			      PyInt_FromLong(ACCESS_READ));
+	PyDict_SetItemString (dict, "ACCESS_WRITE", 
+			      PyInt_FromLong(ACCESS_WRITE));
+	PyDict_SetItemString (dict, "ACCESS_COPY",	
+			      PyInt_FromLong(ACCESS_COPY));
+}