- improved os.popen support for windows, based on win32pipe
  by Bill Tutt.

  note: to run this on Windows 95/98, you need to have the
  w9xpopen.exe helper in the same directory as the python DLL.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 5b61193..c40060d 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -222,6 +222,7 @@
 #include <direct.h>
 #include <io.h>
 #include <process.h>
+#define WINDOWS_LEAN_AND_MEAN
 #include <windows.h>
 #ifdef MS_WIN32
 #define popen	_popen
@@ -353,6 +354,18 @@
 	return PyErr_SetFromErrnoWithFilename(PyExc_OSError, name);
 }
 
+#ifdef MS_WIN32
+static PyObject *
+win32_error(char* function, char* filename)
+{
+	/* XXX this could be improved */
+	errno = GetLastError();
+	if (filename)
+		return PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
+	else
+		return PyErr_SetFromErrno(PyExc_OSError);
+}
+#endif
 
 #if defined(PYOS_OS2)
 /**********************************************************************
@@ -845,7 +858,7 @@
 		errno = GetLastError();
 		if (errno == ERROR_FILE_NOT_FOUND)
 			return PyList_New(0);
-		return posix_error_with_filename(name);
+		return win32_error("FindFirstFile", name);
 	}
 	do {
 		if (FileData.cFileName[0] == '.' &&
@@ -868,10 +881,8 @@
 		Py_DECREF(v);
 	} while (FindNextFile(hFindFile, &FileData) == TRUE);
 
-	if (FindClose(hFindFile) == FALSE) {
-		errno = GetLastError();
-		return posix_error_with_filename(name);
-	}
+	if (FindClose(hFindFile) == FALSE)
+		return win32_error("FindClose", name);
 
 	return d;
 
@@ -2108,6 +2119,446 @@
 	return f;
 }
 
+#elif defined(MS_WIN32)
+
+/*
+ * Portable 'popen' replacement for Win32.
+ *
+ * Written by Bill Tutt <billtut@microsoft.com>.  Minor tweaks
+ * and 2.0 integration by Fredrik Lundh <fredrik@pythonware.com>
+ */
+
+#include <malloc.h>
+#include <io.h>
+#include <fcntl.h>
+
+/* These tell _PyPopen() wether to return 1, 2, or 3 file objects. */
+#define POPEN_1 1
+#define POPEN_2 2
+#define POPEN_3 3
+#define POPEN_4 4
+
+static PyObject *_PyPopen(char *, int, int);
+
+/* popen that works from a GUI.
+ *
+ * The result of this function is a pipe (file) connected to the
+ * processes stdin or stdout, depending on the requested mode.
+ */
+
+static PyObject *
+posix_popen(PyObject *self, PyObject *args)
+{
+	int bufsize = -1;
+	PyObject *f, *s;
+	int tm = 0;
+  
+	char *cmdstring;
+	char *mode = "r";
+	if (!PyArg_ParseTuple(args, "s|s:popen", &cmdstring, &mode))
+		return NULL;
+
+	s = PyTuple_New(0);
+	  
+	if (*mode == 'r')
+		tm = _O_RDONLY;
+	else if (*mode != 'w') {
+		PyErr_SetString(PyExc_ValueError, "mode must be 'r' or 'w'");
+		return NULL;
+	} else
+		tm = _O_WRONLY;
+	 
+	if (*(mode+1) == 't')
+		f = _PyPopen(cmdstring, tm | _O_TEXT , POPEN_1);
+	else if (*(mode+1) == 'b')
+		f = _PyPopen(cmdstring, tm | _O_BINARY , POPEN_1);
+	else
+		f = _PyPopen(cmdstring, tm | _O_TEXT, POPEN_1);
+
+	return f;
+}
+
+/* Variation on win32pipe.popen
+ *
+ * The result of this function is a pipe (file) connected to the
+ * process's stdin, and a pipe connected to the process's stdout.
+ */
+
+static PyObject *
+win32_popen2(PyObject *self, PyObject  *args)
+{
+	PyObject *f;
+	int tm=0;
+  
+	char *cmdstring;
+	char *mode = "t";
+	if (!PyArg_ParseTuple(args, "s|s:popen2", &cmdstring, &mode))
+		return NULL;
+  
+	if (*mode == 't')
+		tm = _O_TEXT;
+	else if (*mode != 'b') {
+		PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'");
+		return NULL;
+	} else
+		tm = _O_BINARY;
+  
+	f = _PyPopen(cmdstring, tm , POPEN_2);
+  
+	return f;
+}
+
+/*
+ * Variation on <om win32pipe.popen>
+ * The result of this function is 3 pipes - the process's stdin,
+ * stdout and stderr
+ *
+ */
+
+static PyObject *
+win32_popen3(PyObject *self, PyObject *args)
+{
+	PyObject *f;
+	int tm = 0;
+  
+	char *cmdstring;
+	char *mode = "t";
+	if (!PyArg_ParseTuple(args, "s|s:Popen3", &cmdstring, &mode))
+		return NULL;
+  
+	if (*mode == 't')
+		tm = _O_TEXT;
+	else if (*mode != 'b') {
+		PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'");
+		return NULL;
+	} else
+		tm = _O_BINARY;
+  
+	f = _PyPopen(cmdstring, tm, POPEN_3);
+  
+	return f;
+}
+
+/*
+ * Variation on win32pipe.popen
+ *
+ * The result of this function is 2 pipes - the processes stdin, 
+ * and stdout+stderr combined as a single pipe.
+ */
+
+static PyObject *
+win32_popen4(PyObject *self, PyObject  *args)
+{
+	PyObject *f;
+	int tm = 0;
+  
+	char *cmdstring;
+	char *mode = "t";
+	if (!PyArg_ParseTuple(args, "s|s:popen4", &cmdstring, &mode))
+		return NULL;
+  
+	if (*mode == 't')
+		tm = _O_TEXT;
+	else if (*mode != 'b') {
+		PyErr_SetString(PyExc_ValueError, "mode must be 't' or 'b'");
+		return NULL;
+	} else
+		tm = _O_BINARY;
+  
+	f = _PyPopen(cmdstring, tm , POPEN_4);
+  
+	return f;
+}
+
+static int
+_PyPopenCreateProcess(char *cmdstring,
+					  HANDLE hStdin,
+					  HANDLE hStdout,
+					  HANDLE hStderr)
+{
+	PROCESS_INFORMATION piProcInfo;
+	STARTUPINFO siStartInfo;
+	char *s1,*s2, *s3 = " /c ";
+	const char *szConsoleSpawn = "w9xpopen.exe \"";
+	int i;
+	int x;
+
+	if (i = GetEnvironmentVariable("COMSPEC",NULL,0)) {
+		s1 = (char *)_alloca(i);
+		if (!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
+			return x;
+		if (GetVersion() < 0x80000000) {
+			/*
+			 * NT/2000
+			 */
+			x = i + strlen(s3) + strlen(cmdstring) + 1;
+			s2 = (char *)_alloca(x);
+			ZeroMemory(s2, x);
+			sprintf(s2, "%s%s%s", s1, s3, cmdstring);
+		}
+		else {
+			/*
+			 * Oh gag, we're on Win9x. Use the workaround listed in
+			 * KB: Q150956
+			 */
+			char modulepath[256];
+			GetModuleFileName(NULL, modulepath, sizeof(modulepath));
+			for (i = x = 0; modulepath[i]; i++)
+				if (modulepath[i] == '\\')
+					x = i+1;
+			modulepath[x] = '\0';
+			x = i + strlen(s3) + strlen(cmdstring) + 1 +
+				strlen(modulepath) + 
+				strlen(szConsoleSpawn) + 1;
+			s2 = (char *)_alloca(x);
+			ZeroMemory(s2, x);
+			sprintf(
+				s2,
+				"%s%s%s%s%s\"",
+				modulepath,
+				szConsoleSpawn,
+				s1,
+				s3,
+				cmdstring);
+		}
+	}
+
+	/* Could be an else here to try cmd.exe / command.com in the path
+	   Now we'll just error out.. */
+	else
+		return -1;
+  
+	ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+	siStartInfo.cb = sizeof(STARTUPINFO);
+	siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+	siStartInfo.hStdInput = hStdin;
+	siStartInfo.hStdOutput = hStdout;
+	siStartInfo.hStdError = hStderr;
+	siStartInfo.wShowWindow = SW_HIDE;
+
+	if (CreateProcess(NULL,
+					  s2,
+					  NULL,
+					  NULL,
+					  TRUE,
+					  CREATE_NEW_CONSOLE,
+					  NULL,
+					  NULL,
+					  &siStartInfo,
+					  &piProcInfo) ) {
+		/* Close the handles now so anyone waiting is woken. */
+		CloseHandle(piProcInfo.hProcess);
+		CloseHandle(piProcInfo.hThread);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/* The following code is based off of KB: Q190351 */
+
+static PyObject *
+_PyPopen(char *cmdstring, int mode, int n)
+{
+	HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
+		hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
+		hChildStderrRdDup; /* hChildStdoutWrDup; */
+	  
+	SECURITY_ATTRIBUTES saAttr;
+	BOOL fSuccess;
+	int fd1, fd2, fd3;
+	FILE *f1, *f2, *f3;
+	PyObject *f;
+
+	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+	saAttr.bInheritHandle = TRUE;
+	saAttr.lpSecurityDescriptor = NULL;
+
+	if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+		return win32_error("CreatePipe", NULL);
+
+	/* Create new output read handle and the input write handle. Set
+	 * the inheritance properties to FALSE. Otherwise, the child inherits
+	 * the these handles; resulting in non-closeable handles to the pipes
+	 * being created. */
+	 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+								GetCurrentProcess(), &hChildStdinWrDup, 0,
+								FALSE,
+								DUPLICATE_SAME_ACCESS);
+	 if (!fSuccess)
+		 return win32_error("DuplicateHandle", NULL);
+
+	 /* Close the inheritable version of ChildStdin
+	that we're using. */
+	 CloseHandle(hChildStdinWr);
+
+	 if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+		 return win32_error("CreatePipe", NULL);
+
+	 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+								GetCurrentProcess(), &hChildStdoutRdDup, 0,
+								FALSE,
+								DUPLICATE_SAME_ACCESS);
+	 if (!fSuccess)
+		 return win32_error("DuplicateHandle", NULL);
+
+	 /* Close the inheritable version of ChildStdout
+		that we're using. */
+	 CloseHandle(hChildStdoutRd);
+
+	 if (n != POPEN_4) {
+		 if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
+			 return win32_error("CreatePipe", NULL);
+		 fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStderrRd,
+									GetCurrentProcess(), &hChildStderrRdDup, 0,
+									FALSE,
+									DUPLICATE_SAME_ACCESS);
+		 if (!fSuccess)
+			 return win32_error("DuplicateHandle", NULL);
+		 /* Close the inheritable version of ChildStdErr that we're using. */
+		 CloseHandle(hChildStderrRd);
+	 }
+	  
+	 switch (n) {
+	 case POPEN_1:
+		 switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY)) {
+		 case _O_WRONLY | _O_TEXT:
+			 /* Case for writing to child Stdin in text mode. */
+			 fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+			 f1 = _fdopen(fd1, "w");
+			 f = PyFile_FromFile(f1, cmdstring, "w", fclose);
+			 PyFile_SetBufSize(f, 0);
+			 /* We don't care about these pipes anymore, so close them. */
+			 CloseHandle(hChildStdoutRdDup);
+			 CloseHandle(hChildStderrRdDup);
+			 break;
+
+		 case _O_RDONLY | _O_TEXT:
+			 /* Case for reading from child Stdout in text mode. */
+			 fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+			 f1 = _fdopen(fd1, "r");
+			 f = PyFile_FromFile(f1, cmdstring, "r", fclose);
+			 PyFile_SetBufSize(f, 0);
+			 /* We don't care about these pipes anymore, so close them. */
+			 CloseHandle(hChildStdinWrDup);
+			 CloseHandle(hChildStderrRdDup);
+			 break;
+
+		 case _O_RDONLY | _O_BINARY:
+			 /* Case for readinig from child Stdout in binary mode. */
+			 fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+			 f1 = _fdopen(fd1, "rb");
+			 f = PyFile_FromFile(f1, cmdstring, "rb", fclose);
+			 PyFile_SetBufSize(f, 0);
+			 /* We don't care about these pipes anymore, so close them. */
+			 CloseHandle(hChildStdinWrDup);
+			 CloseHandle(hChildStderrRdDup);
+			 break;
+
+		 case _O_WRONLY | _O_BINARY:
+			 /* Case for writing to child Stdin in binary mode. */
+			 fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+			 f1 = _fdopen(fd1, "wb");
+			 f = PyFile_FromFile(f1, cmdstring, "wb", fclose);
+			 PyFile_SetBufSize(f, 0);
+			 /* We don't care about these pipes anymore, so close them. */
+			 CloseHandle(hChildStdoutRdDup);
+			 CloseHandle(hChildStderrRdDup);
+			 break;
+		 }
+		 break;
+	
+	 case POPEN_2:
+	 case POPEN_4:
+	 {
+		 char *m1, *m2;
+		 PyObject *p1, *p2;
+		
+		 if (mode && _O_TEXT) {
+			 m1 = "r";
+			 m2 = "w";
+		 } else {
+			 m1 = "rb";
+			 m2 = "wb";
+		 }
+
+		 fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+		 f1 = _fdopen(fd1, m2);
+		 fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+		 f2 = _fdopen(fd2, m1);
+		 p1 = PyFile_FromFile(f1, cmdstring, m2, fclose);
+		 PyFile_SetBufSize(p1, 0);
+		 p2 = PyFile_FromFile(f2, cmdstring, m1, fclose);
+		 PyFile_SetBufSize(p2, 0);
+
+		 if (n != 4)
+			 CloseHandle(hChildStderrRdDup);
+
+		 f = Py_BuildValue("OO",p1,p2);
+		 break;
+	 }
+	
+	 case POPEN_3:
+	 {
+		 char *m1, *m2;
+		 PyObject *p1, *p2, *p3;
+		
+		 if (mode && _O_TEXT) {
+			 m1 = "r";
+			 m2 = "w";
+		 } else {
+			 m1 = "rb";
+			 m2 = "wb";
+		 }
+
+		 fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+		 f1 = _fdopen(fd1, m2);
+		 fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+		 f2 = _fdopen(fd2, m1);
+		 fd3 = _open_osfhandle((long)hChildStderrRdDup, mode);
+		 f3 = _fdopen(fd3, m1);
+		 p1 = PyFile_FromFile(f1, cmdstring, m2, fclose);
+		 p2 = PyFile_FromFile(f2, cmdstring, m1, fclose);
+		 p3 = PyFile_FromFile(f3, cmdstring, m1, fclose);
+		 PyFile_SetBufSize(p1, 0);
+		 PyFile_SetBufSize(p2, 0);
+		 PyFile_SetBufSize(p3, 0);
+		 f = Py_BuildValue("OOO",p1,p2,p3);
+		 break;
+	 }
+	 }
+
+	 if (n == POPEN_4) {
+		 if (!_PyPopenCreateProcess(cmdstring,
+									hChildStdinRd,
+									hChildStdoutWr,
+									hChildStdoutWr))
+			 return win32_error("CreateProcess", NULL);
+	 }
+	 else {
+		 if (!_PyPopenCreateProcess(cmdstring,
+									hChildStdinRd,
+									hChildStdoutWr,
+									hChildStderrWr))
+			 return win32_error("CreateProcess", NULL);
+	 }
+
+	 /* Child is launched. Close the parents copy of those pipe
+	  * handles that only the child should have open.  You need to
+	  * make sure that no handles to the write end of the output pipe
+	  * are maintained in this process or else the pipe will not close
+	  * when the child process exits and the ReadFile will hang. */
+
+	 if (!CloseHandle(hChildStdinRd))
+		 return win32_error("CloseHandle", NULL);
+	  
+	 if (!CloseHandle(hChildStdoutWr))
+		 return win32_error("CloseHandle", NULL);
+	  
+	 if ((n != 4) && (!CloseHandle(hChildStderrWr)))
+		 return win32_error("CloseHandle", NULL);
+
+	 return f;
+}
 #else
 static PyObject *
 posix_popen(PyObject *self, PyObject *args)
@@ -2728,7 +3179,7 @@
 	ok = CreatePipe(&read, &write, NULL, 0);
 	Py_END_ALLOW_THREADS
 	if (!ok)
-		return posix_error();
+		return win32_error("CreatePipe", NULL);
 	read_fd = _open_osfhandle((intptr_t)read, 0);
 	write_fd = _open_osfhandle((intptr_t)write, 1);
 	return Py_BuildValue("(ii)", read_fd, write_fd);
@@ -4423,6 +4874,11 @@
 #endif /* HAVE_PLOCK */
 #ifdef HAVE_POPEN
 	{"popen",	posix_popen, METH_VARARGS, posix_popen__doc__},
+#ifdef MS_WIN32
+	{"popen2",	win32_popen2, METH_VARARGS},
+	{"popen3",	win32_popen3, METH_VARARGS},
+	{"popen4",	win32_popen4, METH_VARARGS},
+#endif
 #endif /* HAVE_POPEN */
 #ifdef HAVE_SETUID
 	{"setuid",	posix_setuid, METH_VARARGS, posix_setuid__doc__},