summaryrefslogtreecommitdiffstats
path: root/Modules/posixmodule.c
diff options
context:
space:
mode:
authorFredrik Lundh <fredrik@pythonware.com>2000-07-09 14:49:51 (GMT)
committerFredrik Lundh <fredrik@pythonware.com>2000-07-09 14:49:51 (GMT)
commitffb9c770f8992a4692e3794ee10bc0d92c6a45ff (patch)
tree7f002f4b40ad2fae9c21059b4815f7a27bef7ccc /Modules/posixmodule.c
parentce81d59c0cb1be048c279f0acf5205da77a82e23 (diff)
downloadcpython-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/posixmodule.c')
-rw-r--r--Modules/posixmodule.c468
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__},