diff options
author | Fredrik Lundh <fredrik@pythonware.com> | 2000-07-09 14:49:51 (GMT) |
---|---|---|
committer | Fredrik Lundh <fredrik@pythonware.com> | 2000-07-09 14:49:51 (GMT) |
commit | ffb9c770f8992a4692e3794ee10bc0d92c6a45ff (patch) | |
tree | 7f002f4b40ad2fae9c21059b4815f7a27bef7ccc /Modules | |
parent | ce81d59c0cb1be048c279f0acf5205da77a82e23 (diff) | |
download | cpython-ffb9c770f8992a4692e3794ee10bc0d92c6a45ff.zip cpython-ffb9c770f8992a4692e3794ee10bc0d92c6a45ff.tar.gz cpython-ffb9c770f8992a4692e3794ee10bc0d92c6a45ff.tar.bz2 |
- 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.
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/posixmodule.c | 468 |
1 files changed, 462 insertions, 6 deletions
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 @@ extern int lstat(const char *, struct stat *); #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 @@ posix_error_with_filename(char* name) 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 @@ posix_listdir(PyObject *self, PyObject *args) 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 @@ posix_listdir(PyObject *self, PyObject *args) 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 @@ posix_popen(PyObject *self, PyObject *args) 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 @@ posix_pipe(PyObject *self, PyObject *args) 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 @@ static PyMethodDef posix_methods[] = { #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__}, |