[Python-checkins] CVS: python/dist/src/Modules posixmodule.c,2.144,2.145

Fredrik Lundh python-dev@python.org
Sun, 9 Jul 2000 07:49:53 -0700


Update of /cvsroot/python/python/dist/src/Modules
In directory slayer.i.sourceforge.net:/tmp/cvs-serv7152/Modules

Modified Files:
	posixmodule.c 
Log Message:


- 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.

Index: posixmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v
retrieving revision 2.144
retrieving revision 2.145
diff -C2 -r2.144 -r2.145
*** posixmodule.c	2000/07/09 13:10:40	2.144
--- posixmodule.c	2000/07/09 14:49:51	2.145
***************
*** 223,226 ****
--- 223,227 ----
  #include <io.h>
  #include <process.h>
+ #define WINDOWS_LEAN_AND_MEAN
  #include <windows.h>
  #ifdef MS_WIN32
***************
*** 354,357 ****
--- 355,370 ----
  }
  
+ #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)
***************
*** 846,850 ****
  		if (errno == ERROR_FILE_NOT_FOUND)
  			return PyList_New(0);
! 		return posix_error_with_filename(name);
  	}
  	do {
--- 859,863 ----
  		if (errno == ERROR_FILE_NOT_FOUND)
  			return PyList_New(0);
! 		return win32_error("FindFirstFile", name);
  	}
  	do {
***************
*** 869,876 ****
  	} while (FindNextFile(hFindFile, &FileData) == TRUE);
  
! 	if (FindClose(hFindFile) == FALSE) {
! 		errno = GetLastError();
! 		return posix_error_with_filename(name);
! 	}
  
  	return d;
--- 882,887 ----
  	} while (FindNextFile(hFindFile, &FileData) == TRUE);
  
! 	if (FindClose(hFindFile) == FALSE)
! 		return win32_error("FindClose", name);
  
  	return d;
***************
*** 2109,2112 ****
--- 2120,2563 ----
  }
  
+ #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 *
***************
*** 2729,2733 ****
  	Py_END_ALLOW_THREADS
  	if (!ok)
! 		return posix_error();
  	read_fd = _open_osfhandle((intptr_t)read, 0);
  	write_fd = _open_osfhandle((intptr_t)write, 1);
--- 3180,3184 ----
  	Py_END_ALLOW_THREADS
  	if (!ok)
! 		return win32_error("CreatePipe", NULL);
  	read_fd = _open_osfhandle((intptr_t)read, 0);
  	write_fd = _open_osfhandle((intptr_t)write, 1);
***************
*** 4424,4427 ****
--- 4875,4883 ----
  #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