]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
- improved os.popen support for windows, based on win32pipe
authorFredrik Lundh <fredrik@pythonware.com>
Sun, 9 Jul 2000 14:49:51 +0000 (14:49 +0000)
committerFredrik Lundh <fredrik@pythonware.com>
Sun, 9 Jul 2000 14:49:51 +0000 (14:49 +0000)
  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.

Modules/posixmodule.c

index 5b6119317fed7b7eabee085ece09fe83e8ed4798..c40060d689743dfce5d66beb73d74ba2ac1c58b6 100644 (file)
@@ -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__},