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

Andrew I MacIntyre aimacintyre@users.sourceforge.net
Sat, 02 Mar 2002 19:07:10 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory usw-pr-cvs1:/tmp/cvs-serv1868

Modified Files:
	posixmodule.c 
Log Message:
OS/2 EMX port changes (Modules part of patch #450267):
  Modules/
    posixmodule.c

- use SEP,ALTSEP #defines instead of hard coded path separator chars
- use EMX specific variants of chdir2(),getcwd() that support drive letters
- OS/2+EMX spawnv(),spawnve() support
- EMX specific popen[234]() derived from Win32 popen[234]() code


Index: posixmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/posixmodule.c,v
retrieving revision 2.222
retrieving revision 2.223
diff -C2 -d -r2.222 -r2.223
*** posixmodule.c	16 Feb 2002 23:33:23 -0000	2.222
--- posixmodule.c	3 Mar 2002 03:07:07 -0000	2.223
***************
*** 2,11 ****
  /* POSIX module implementation */
  
! /* This file is also used for Windows NT and MS-Win.  In that case the module
!    actually calls itself 'nt', not 'posix', and a few functions are
!    either unimplemented or implemented differently.  The source
     assumes that for Windows NT, the macro 'MS_WIN32' is defined independent
     of the compiler used.  Different compilers define their own feature
!    test macro, e.g. '__BORLANDC__' or '_MSC_VER'. */
  
  /* See also ../Dos/dosmodule.c */
--- 2,14 ----
  /* POSIX module implementation */
  
! /* This file is also used for Windows NT/MS-Win and OS/2.  In that case the
!    module actually calls itself 'nt' or 'os2', not 'posix', and a few
!    functions are either unimplemented or implemented differently.  The source
     assumes that for Windows NT, the macro 'MS_WIN32' is defined independent
     of the compiler used.  Different compilers define their own feature
!    test macro, e.g. '__BORLANDC__' or '_MSC_VER'.  For OS/2, the compiler
!    independent macro PYOS_OS2 should be defined.  On OS/2 the default
!    compiler is assumed to be IBM's VisualAge C++ (VACPP).  PYCC_GCC is used
!    as the compiler specific macro for the EMX port of gcc to OS/2. */
  
  /* See also ../Dos/dosmodule.c */
***************
*** 26,29 ****
--- 29,39 ----
  #define  INCL_NOPMAPI
  #include <os2.h>
+ #if defined(PYCC_GCC)
+ #include <ctype.h>
+ #include <io.h>
+ #include <stdio.h>
+ #include <process.h>
+ #include "osdefs.h"
+ #endif
  #endif
  
***************
*** 82,85 ****
--- 92,98 ----
  #else /* 16-bit Windows */
  #endif /* !MS_WIN32 */
+ #else
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ /* Everything needed is defined in PC/os2emx/pyconfig.h */
  #else			/* all other compilers */
  /* Unix functions that the configure script doesn't check for */
***************
*** 102,105 ****
--- 115,119 ----
  #define HAVE_WAIT       1
  #define HAVE_TTYNAME	1
+ #endif  /* PYOS_OS2 && PYCC_GCC */
  #endif  /* _MSC_VER */
  #endif  /* __BORLANDC__ */
***************
*** 802,806 ****
--- 816,824 ----
  posix_chdir(PyObject *self, PyObject *args)
  {
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ 	return posix_1str(args, "et:chdir", _chdir2);
+ #else
  	return posix_1str(args, "et:chdir", chdir);
+ #endif
  }
  
***************
*** 913,917 ****
--- 931,939 ----
  		return NULL;
  	Py_BEGIN_ALLOW_THREADS
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ 	res = _getcwd2(buf, sizeof buf);
+ #else
  	res = getcwd(buf, sizeof buf);
+ #endif
  	Py_END_ALLOW_THREADS
  	if (res == NULL)
***************
*** 964,968 ****
  		return NULL;
  	ch = namebuf[len-1];
! 	if (ch != '/' && ch != '\\' && ch != ':')
  		namebuf[len++] = '/';
  	strcpy(namebuf + len, "*.*");
--- 986,990 ----
  		return NULL;
  	ch = namebuf[len-1];
! 	if (ch != SEP && ch != ALTSEP && ch != ':')
  		namebuf[len++] = '/';
  	strcpy(namebuf + len, "*.*");
***************
*** 1023,1030 ****
  	strcpy(namebuf, name);
  	for (pt = namebuf; *pt; pt++)
! 		if (*pt == '/')
! 			*pt = '\\';
! 	if (namebuf[len-1] != '\\')
! 		namebuf[len++] = '\\';
  	strcpy(namebuf + len, "*.*");
  
--- 1045,1052 ----
  	strcpy(namebuf, name);
  	for (pt = namebuf; *pt; pt++)
! 		if (*pt == ALTSEP)
! 			*pt = SEP;
! 	if (namebuf[len-1] != SEP)
! 		namebuf[len++] = SEP;
  	strcpy(namebuf + len, "*.*");
  
***************
*** 1087,1094 ****
      strcpy(namebuf, name);
      for (pt = namebuf; *pt; pt++)
!         if (*pt == '/')
!             *pt = '\\';
!     if (namebuf[len-1] != '\\')
!         namebuf[len++] = '\\';
      strcpy(namebuf + len, "*.*");
  
--- 1109,1116 ----
      strcpy(namebuf, name);
      for (pt = namebuf; *pt; pt++)
!         if (*pt == ALTSEP)
!             *pt = SEP;
!     if (namebuf[len-1] != SEP)
!         namebuf[len++] = SEP;
      strcpy(namebuf + len, "*.*");
  
***************
*** 1111,1115 ****
          do {
              if (ep.achName[0] == '.'
!             && (ep.achName[1] == '\0' || ep.achName[1] == '.' && ep.achName[2] == '\0'))
                  continue; /* Skip Over "." and ".." Names */
  
--- 1133,1137 ----
          do {
              if (ep.achName[0] == '.'
!             && (ep.achName[1] == '\0' || (ep.achName[1] == '.' && ep.achName[2] == '\0')))
                  continue; /* Skip Over "." and ".." Names */
  
***************
*** 1705,1708 ****
--- 1727,1735 ----
  	argvlist[argc] = NULL;
  
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ 	Py_BEGIN_ALLOW_THREADS
+ 	spawnval = spawnv(mode, path, argvlist);
+ 	Py_END_ALLOW_THREADS
+ #else
  	if (mode == _OLD_P_OVERLAY)
  		mode = _P_OVERLAY;
***************
*** 1711,1714 ****
--- 1738,1742 ----
  	spawnval = _spawnv(mode, path, argvlist);
  	Py_END_ALLOW_THREADS
+ #endif
  
  	PyMem_DEL(argvlist);
***************
*** 1821,1824 ****
--- 1849,1857 ----
  	envlist[envc] = 0;
  
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ 	Py_BEGIN_ALLOW_THREADS
+ 	spawnval = spawnve(mode, path, argvlist, envlist);
+ 	Py_END_ALLOW_THREADS
+ #else
  	if (mode == _OLD_P_OVERLAY)
  		mode = _P_OVERLAY;
***************
*** 1827,1830 ****
--- 1860,1864 ----
  	spawnval = _spawnve(mode, path, argvlist, envlist);
  	Py_END_ALLOW_THREADS
+ #endif
  
  	if (spawnval == -1)
***************
*** 2178,2182 ****
  	if (!PyArg_ParseTuple(args, "ii:kill", &pid, &sig))
  		return NULL;
! #if defined(PYOS_OS2)
      if (sig == XCPT_SIGNAL_INTR || sig == XCPT_SIGNAL_BREAK) {
          APIRET rc;
--- 2212,2216 ----
  	if (!PyArg_ParseTuple(args, "ii:kill", &pid, &sig))
  		return NULL;
! #if defined(PYOS_OS2) && !defined(PYCC_GCC)
      if (sig == XCPT_SIGNAL_INTR || sig == XCPT_SIGNAL_BREAK) {
          APIRET rc;
***************
*** 2248,2251 ****
--- 2282,2286 ----
  
  #if defined(PYOS_OS2)
+ #if defined(PYCC_VACPP)
  static int
  async_system(const char *command)
***************
*** 2355,2358 ****
--- 2390,3014 ----
  }
  
+ #elif defined(PYCC_GCC)
+ 
+ /* standard posix version of popen() support */
+ static PyObject *
+ posix_popen(PyObject *self, PyObject *args)
+ {
+ 	char *name;
+ 	char *mode = "r";
+ 	int bufsize = -1;
+ 	FILE *fp;
+ 	PyObject *f;
+ 	if (!PyArg_ParseTuple(args, "s|si:popen", &name, &mode, &bufsize))
+ 		return NULL;
+ 	Py_BEGIN_ALLOW_THREADS
+ 	fp = popen(name, mode);
+ 	Py_END_ALLOW_THREADS
+ 	if (fp == NULL)
+ 		return posix_error();
+ 	f = PyFile_FromFile(fp, name, mode, pclose);
+ 	if (f != NULL)
+ 		PyFile_SetBufSize(f, bufsize);
+ 	return f;
+ }
+ 
+ /* fork() under OS/2 has lots'o'warts
+  * EMX supports pipe() and spawn*() so we can synthesize popen[234]()
+  * most of this code is a ripoff of the win32 code, but using the
+  * capabilities of EMX's C library routines
+  */
+ 
+ /* These tell _PyPopen() whether 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, int);
+ static int _PyPclose(FILE *file);
+ 
+ /*
+  * Internal dictionary mapping popen* file pointers to process handles,
+  * for use when retrieving the process exit code.  See _PyPclose() below
+  * for more information on this dictionary's use.
+  */
+ static PyObject *_PyPopenProcs = NULL;
+ 
+ /* os2emx version of popen2()
+  *
+  * 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 *
+ os2emx_popen2(PyObject *self, PyObject  *args)
+ {
+ 	PyObject *f;
+ 	int tm=0;
+ 
+ 	char *cmdstring;
+ 	char *mode = "t";
+ 	int bufsize = -1;
+ 	if (!PyArg_ParseTuple(args, "s|si:popen2", &cmdstring, &mode, &bufsize))
+ 		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, bufsize);
+ 
+ 	return f;
+ }
+ 
+ /*
+  * Variation on os2emx.popen2
+  *
+  * The result of this function is 3 pipes - the process's stdin,
+  * stdout and stderr
+  */
+ 
+ static PyObject *
+ os2emx_popen3(PyObject *self, PyObject *args)
+ {
+ 	PyObject *f;
+ 	int tm = 0;
+ 
+ 	char *cmdstring;
+ 	char *mode = "t";
+ 	int bufsize = -1;
+ 	if (!PyArg_ParseTuple(args, "s|si:popen3", &cmdstring, &mode, &bufsize))
+ 		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, bufsize);
+ 
+ 	return f;
+ }
+ 
+ /*
+  * Variation on os2emx.popen2
+  *
+  * The result of this function is 2 pipes - the processes stdin, 
+  * and stdout+stderr combined as a single pipe.
+  */
+ 
+ static PyObject *
+ os2emx_popen4(PyObject *self, PyObject  *args)
+ {
+ 	PyObject *f;
+ 	int tm = 0;
+ 
+ 	char *cmdstring;
+ 	char *mode = "t";
+ 	int bufsize = -1;
+ 	if (!PyArg_ParseTuple(args, "s|si:popen4", &cmdstring, &mode, &bufsize))
+ 		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, bufsize);
+ 
+ 	return f;
+ }
+ 
+ /* a couple of structures for convenient handling of multiple
+  * file handles and pipes
+  */
+ struct file_ref
+ {
+ 	int handle;
+ 	int flags;
+ };
+ 
+ struct pipe_ref
+ {
+ 	int rd;
+ 	int wr;
+ };
+ 
+ /* The following code is derived from the win32 code */
+ 
+ static PyObject *
+ _PyPopen(char *cmdstring, int mode, int n, int bufsize)
+ {
+ 	struct file_ref stdio[3];
+ 	struct pipe_ref p_fd[3];
+ 	FILE *p_s[3];
+ 	int file_count, i, pipe_err, pipe_pid;
+ 	char *shell, *sh_name, *opt, *rd_mode, *wr_mode;
+ 	PyObject *f, *p_f[3];
+ 
+ 	/* file modes for subsequent fdopen's on pipe handles */
+ 	if (mode == O_TEXT)
+ 	{
+ 		rd_mode = "rt";
+ 		wr_mode = "wt";
+ 	}
+ 	else
+ 	{
+ 		rd_mode = "rb";
+ 		wr_mode = "wb";
+ 	}
+ 
+ 	/* prepare shell references */
+ 	if ((shell = getenv("EMXSHELL")) == NULL)
+ 		if ((shell = getenv("COMSPEC")) == NULL)
+ 		{
+ 			errno = ENOENT;
+ 			return posix_error();
+ 		}
+ 
+ 	sh_name = _getname(shell);
+ 	if (stricmp(sh_name, "cmd.exe") == 0 || stricmp(sh_name, "4os2.exe") == 0)
+ 		opt = "/c";
+ 	else
+ 		opt = "-c";
+ 
+ 	/* save current stdio fds + their flags, and set not inheritable */
+ 	i = pipe_err = 0;
+ 	while (pipe_err >= 0 && i < 3)
+ 	{
+ 		pipe_err = stdio[i].handle = dup(i);
+ 		stdio[i].flags = fcntl(i, F_GETFD, 0);
+ 		fcntl(stdio[i].handle, F_SETFD, stdio[i].flags | FD_CLOEXEC);
+ 		i++;
+ 	}
+ 	if (pipe_err < 0)
+ 	{
+ 		/* didn't get them all saved - clean up and bail out */
+ 		int saved_err = errno;
+ 		while (i-- > 0)
+ 		{
+ 			close(stdio[i].handle);
+ 		}
+ 		errno = saved_err;
+ 		return posix_error();
+ 	}
+ 
+ 	/* create pipe ends */
+ 	file_count = 2;
+ 	if (n == POPEN_3)
+ 		file_count = 3;
+ 	i = pipe_err = 0;
+ 	while ((pipe_err == 0) && (i < file_count))
+ 		pipe_err = pipe((int *)&p_fd[i++]);
+ 	if (pipe_err < 0)
+ 	{
+ 		/* didn't get them all made - clean up and bail out */
+ 		while (i-- > 0)
+ 		{
+ 			close(p_fd[i].wr);
+ 			close(p_fd[i].rd);
+ 		}
+ 		errno = EPIPE;
+ 		return posix_error();
+ 	}
+ 
+ 	/* change the actual standard IO streams over temporarily,
+ 	 * making the retained pipe ends non-inheritable
+ 	 */
+ 	pipe_err = 0;
+ 
+ 	/* - stdin */
+ 	if (dup2(p_fd[0].rd, 0) == 0)
+ 	{
+ 		close(p_fd[0].rd);
+ 		i = fcntl(p_fd[0].wr, F_GETFD, 0);
+ 		fcntl(p_fd[0].wr, F_SETFD, i | FD_CLOEXEC);
+ 		if ((p_s[0] = fdopen(p_fd[0].wr, wr_mode)) == NULL)
+ 		{
+ 			close(p_fd[0].wr);
+ 			pipe_err = -1;
+ 		}
+ 	}
+ 	else
+ 	{
+ 		pipe_err = -1;
+ 	}
+ 
+ 	/* - stdout */
+ 	if (pipe_err == 0)
+ 	{
+ 		if (dup2(p_fd[1].wr, 1) == 1)
+ 		{
+ 			close(p_fd[1].wr);
+ 			i = fcntl(p_fd[1].rd, F_GETFD, 0);
+ 			fcntl(p_fd[1].rd, F_SETFD, i | FD_CLOEXEC);
+ 			if ((p_s[1] = fdopen(p_fd[1].rd, rd_mode)) == NULL)
+ 			{
+ 				close(p_fd[1].rd);
+ 				pipe_err = -1;
+ 			}
+ 		}
+ 		else
+ 		{
+ 			pipe_err = -1;
+ 		}
+ 	}
+ 
+ 	/* - stderr, as required */
+ 	if (pipe_err == 0)
+ 		switch (n)
+ 		{
+ 			case POPEN_3:
+ 			{
+ 				if (dup2(p_fd[2].wr, 2) == 2)
+ 				{
+ 					close(p_fd[2].wr);
+ 					i = fcntl(p_fd[2].rd, F_GETFD, 0);
+ 					fcntl(p_fd[2].rd, F_SETFD, i | FD_CLOEXEC);
+ 					if ((p_s[2] = fdopen(p_fd[2].rd, rd_mode)) == NULL)
+ 					{
+ 						close(p_fd[2].rd);
+ 						pipe_err = -1;
+ 					}
+ 				}
+ 				else
+ 				{
+ 					pipe_err = -1;
+ 				}
+ 				break;
+ 			}
+ 
+ 			case POPEN_4:
+ 			{
+ 				if (dup2(1, 2) != 2)
+ 				{
+ 					pipe_err = -1;
+ 				}
+ 				break;
+ 			}
+ 		}
+ 
+ 	/* spawn the child process */
+ 	if (pipe_err == 0)
+ 	{
+ 		pipe_pid = spawnlp(P_NOWAIT, shell, shell, opt, cmdstring, (char *)0);
+ 		if (pipe_pid == -1)
+ 		{
+ 			pipe_err = -1;
+ 		}
+ 		else
+ 		{
+ 			/* save the PID into the FILE structure
+ 			 * NOTE: this implementation doesn't actually
+ 			 * take advantage of this, but do it for
+ 			 * completeness - AIM Apr01
+ 			 */
+ 			for (i = 0; i < file_count; i++)
+ 				p_s[i]->_pid = pipe_pid;
+ 		}
+ 	}
+ 
+ 	/* reset standard IO to normal */
+ 	for (i = 0; i < 3; i++)
+ 	{
+ 		dup2(stdio[i].handle, i);
+ 		fcntl(i, F_SETFD, stdio[i].flags);
+ 		close(stdio[i].handle);
+ 	}
+ 
+ 	/* if any remnant problems, clean up and bail out */
+ 	if (pipe_err < 0)
+ 	{
+ 		for (i = 0; i < 3; i++)
+ 		{
+ 			close(p_fd[i].rd);
+ 			close(p_fd[i].wr);
+ 		}
+ 		errno = EPIPE;
+ 		return posix_error_with_filename(cmdstring);
+ 	}
+ 
+ 	/* build tuple of file objects to return */
+ 	if ((p_f[0] = PyFile_FromFile(p_s[0], cmdstring, wr_mode, _PyPclose)) != NULL)
+ 		PyFile_SetBufSize(p_f[0], bufsize);
+ 	if ((p_f[1] = PyFile_FromFile(p_s[1], cmdstring, rd_mode, _PyPclose)) != NULL)
+ 		PyFile_SetBufSize(p_f[1], bufsize);
+ 	if (n == POPEN_3)
+ 	{
+ 		if ((p_f[2] = PyFile_FromFile(p_s[2], cmdstring, rd_mode, _PyPclose)) != NULL)
+ 			PyFile_SetBufSize(p_f[0], bufsize);
+ 		f = Py_BuildValue("OOO", p_f[0], p_f[1], p_f[2]);
+ 	}
+ 	else
+ 		f = Py_BuildValue("OO", p_f[0], p_f[1]);
+ 
+ 	/*
+ 	 * Insert the files we've created into the process dictionary
+ 	 * all referencing the list with the process handle and the
+ 	 * initial number of files (see description below in _PyPclose).
+ 	 * Since if _PyPclose later tried to wait on a process when all
+ 	 * handles weren't closed, it could create a deadlock with the
+ 	 * child, we spend some energy here to try to ensure that we
+ 	 * either insert all file handles into the dictionary or none
+ 	 * at all.  It's a little clumsy with the various popen modes
+ 	 * and variable number of files involved.
+ 	 */
+ 	if (!_PyPopenProcs)
+ 	{
+ 		_PyPopenProcs = PyDict_New();
+ 	}
+ 
+ 	if (_PyPopenProcs)
+ 	{
+ 		PyObject *procObj, *pidObj, *intObj, *fileObj[3];
+ 		int ins_rc[3];
+ 
+ 		fileObj[0] = fileObj[1] = fileObj[2] = NULL;
+ 		ins_rc[0]  = ins_rc[1]  = ins_rc[2]  = 0;
+ 
+ 		procObj = PyList_New(2);
+ 		pidObj = PyInt_FromLong((long) pipe_pid);
+ 		intObj = PyInt_FromLong((long) file_count);
+ 
+ 		if (procObj && pidObj && intObj)
+ 		{
+ 			PyList_SetItem(procObj, 0, pidObj);
+ 			PyList_SetItem(procObj, 1, intObj);
+ 
+ 			fileObj[0] = PyLong_FromVoidPtr(p_s[0]);
+ 			if (fileObj[0])
+ 			{
+ 			    ins_rc[0] = PyDict_SetItem(_PyPopenProcs,
+ 						       fileObj[0],
+ 						       procObj);
+ 			}
+ 			fileObj[1] = PyLong_FromVoidPtr(p_s[1]);
+ 			if (fileObj[1])
+ 			{
+ 			    ins_rc[1] = PyDict_SetItem(_PyPopenProcs,
+ 						       fileObj[1],
+ 						       procObj);
+ 			}
+ 			if (file_count >= 3)
+ 			{
+ 				fileObj[2] = PyLong_FromVoidPtr(p_s[2]);
+ 				if (fileObj[2])
+ 				{
+ 				    ins_rc[2] = PyDict_SetItem(_PyPopenProcs,
+ 							       fileObj[2],
+ 							       procObj);
+ 				}
+ 			}
+ 
+ 			if (ins_rc[0] < 0 || !fileObj[0] ||
+ 			    ins_rc[1] < 0 || (file_count > 1 && !fileObj[1]) ||
+ 			    ins_rc[2] < 0 || (file_count > 2 && !fileObj[2]))
+ 			{
+ 				/* Something failed - remove any dictionary
+ 				 * entries that did make it.
+ 				 */
+ 				if (!ins_rc[0] && fileObj[0])
+ 				{
+ 					PyDict_DelItem(_PyPopenProcs,
+ 							fileObj[0]);
+ 				}
+ 				if (!ins_rc[1] && fileObj[1])
+ 				{
+ 					PyDict_DelItem(_PyPopenProcs,
+ 							fileObj[1]);
+ 				}
+ 				if (!ins_rc[2] && fileObj[2])
+ 				{
+ 					PyDict_DelItem(_PyPopenProcs,
+ 							fileObj[2]);
+ 				}
+ 			}
+ 		}
+ 		     
+ 		/*
+ 		 * Clean up our localized references for the dictionary keys
+ 		 * and value since PyDict_SetItem will Py_INCREF any copies
+ 		 * that got placed in the dictionary.
+ 		 */
+ 		Py_XDECREF(procObj);
+ 		Py_XDECREF(fileObj[0]);
+ 		Py_XDECREF(fileObj[1]);
+ 		Py_XDECREF(fileObj[2]);
+ 	}
+ 
+ 	/* Child is launched. */
+ 	return f;
+ }
+ 
+ /*
+  * Wrapper for fclose() to use for popen* files, so we can retrieve the
+  * exit code for the child process and return as a result of the close.
+  *
+  * This function uses the _PyPopenProcs dictionary in order to map the
+  * input file pointer to information about the process that was
+  * originally created by the popen* call that created the file pointer.
+  * The dictionary uses the file pointer as a key (with one entry
+  * inserted for each file returned by the original popen* call) and a
+  * single list object as the value for all files from a single call.
+  * The list object contains the Win32 process handle at [0], and a file
+  * count at [1], which is initialized to the total number of file
+  * handles using that list.
+  *
+  * This function closes whichever handle it is passed, and decrements
+  * the file count in the dictionary for the process handle pointed to
+  * by this file.  On the last close (when the file count reaches zero),
+  * this function will wait for the child process and then return its
+  * exit code as the result of the close() operation.  This permits the
+  * files to be closed in any order - it is always the close() of the
+  * final handle that will return the exit code.
+  */
+ 
+  /* RED_FLAG 31-Aug-2000 Tim
+   * This is always called (today!) between a pair of
+   * Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
+   * macros.  So the thread running this has no valid thread state, as
+   * far as Python is concerned.  However, this calls some Python API
+   * functions that cannot be called safely without a valid thread
+   * state, in particular PyDict_GetItem.
+   * As a temporary hack (although it may last for years ...), we
+   * *rely* on not having a valid thread state in this function, in
+   * order to create our own "from scratch".
+   * This will deadlock if _PyPclose is ever called by a thread
+   * holding the global lock.
+   * (The OS/2 EMX thread support appears to cover the case where the
+   *  lock is already held - AIM Apr01)
+   */
+ 
+ static int _PyPclose(FILE *file)
+ {
+ 	int result;
+ 	int exit_code;
+ 	int pipe_pid;
+ 	PyObject *procObj, *pidObj, *intObj, *fileObj;
+ 	int file_count;
+ #ifdef WITH_THREAD
+ 	PyInterpreterState* pInterpreterState;
+ 	PyThreadState* pThreadState;
+ #endif
+ 
+ 	/* Close the file handle first, to ensure it can't block the
+ 	 * child from exiting if it's the last handle.
+ 	 */
+ 	result = fclose(file);
+ 
+ #ifdef WITH_THREAD
+ 	/* Bootstrap a valid thread state into existence. */
+ 	pInterpreterState = PyInterpreterState_New();
+ 	if (!pInterpreterState) {
+ 		/* Well, we're hosed now!  We don't have a thread
+ 		 * state, so can't call a nice error routine, or raise
+ 		 * an exception.  Just die.
+ 		 */
+ 		 Py_FatalError("unable to allocate interpreter state "
+ 		 	       "when closing popen object.");
+ 		 return -1;  /* unreachable */
+ 	}
+ 	pThreadState = PyThreadState_New(pInterpreterState);
+ 	if (!pThreadState) {
+ 		 Py_FatalError("unable to allocate thread state "
+ 		 	       "when closing popen object.");
+ 		 return -1;  /* unreachable */
+ 	}
+ 	/* Grab the global lock.  Note that this will deadlock if the
+ 	 * current thread already has the lock! (see RED_FLAG comments
+ 	 * before this function)
+ 	 */
+ 	PyEval_RestoreThread(pThreadState);
+ #endif
+ 
+ 	if (_PyPopenProcs)
+ 	{
+ 		if ((fileObj = PyLong_FromVoidPtr(file)) != NULL &&
+ 		    (procObj = PyDict_GetItem(_PyPopenProcs,
+ 					      fileObj)) != NULL &&
+ 		    (pidObj = PyList_GetItem(procObj,0)) != NULL &&
+ 		    (intObj = PyList_GetItem(procObj,1)) != NULL)
+ 		{
+ 			pipe_pid = (int) PyInt_AsLong(pidObj);
+ 			file_count = (int) PyInt_AsLong(intObj);
+ 
+ 			if (file_count > 1)
+ 			{
+ 				/* Still other files referencing process */
+ 				file_count--;
+ 				PyList_SetItem(procObj,1,
+ 					       PyInt_FromLong((long) file_count));
+ 			}
+ 			else
+ 			{
+ 				/* Last file for this process */
+ 				if (result != EOF &&
+ 				    waitpid(pipe_pid, &exit_code, 0) == pipe_pid)
+ 				{
+ 					/* extract exit status */
+ 					if (WIFEXITED(exit_code))
+ 					{
+ 						result = WEXITSTATUS(exit_code);
+ 					}
+ 					else
+ 					{
+ 						errno = EPIPE;
+ 						result = -1;
+ 					}
+ 				}
+ 				else
+ 				{
+ 					/* Indicate failure - this will cause the file object
+ 					 * to raise an I/O error and translate the last
+ 					 * error code from errno.  We do have a problem with
+ 					 * last errors that overlap the normal errno table,
+ 					 * but that's a consistent problem with the file object.
+ 					 */
+ 					result = -1;
+ 				}
+ 			}
+ 
+ 			/* Remove this file pointer from dictionary */
+ 			PyDict_DelItem(_PyPopenProcs, fileObj);
+ 
+ 			if (PyDict_Size(_PyPopenProcs) == 0)
+ 			{
+ 				Py_DECREF(_PyPopenProcs);
+ 				_PyPopenProcs = NULL;
+ 			}
+ 
+ 		} /* if object retrieval ok */
+ 
+ 		Py_XDECREF(fileObj);
+ 	} /* if _PyPopenProcs */
+ 
+ #ifdef WITH_THREAD
+ 	/* Tear down the thread & interpreter states.
+ 	 * Note that interpreter state clear & delete functions automatically
+ 	 * call the thread clear & delete functions, and indeed insist on
+ 	 * doing that themselves.  The lock must be held during the clear, but
+ 	 * need not be held during the delete.
+ 	 */
+ 	PyInterpreterState_Clear(pInterpreterState);
+ 	PyEval_ReleaseThread(pThreadState);
+ 	PyInterpreterState_Delete(pInterpreterState);
+ #endif
+ 
+ 	return result;
+ }
+ 
+ #endif /* PYCC_??? */
+ 
  #elif defined(MS_WIN32)
  
***************
*** 2585,2589 ****
  			GetModuleFileName(NULL, modulepath, sizeof(modulepath));
  			for (i = x = 0; modulepath[i]; i++)
! 				if (modulepath[i] == '\\')
  					x = i+1;
  			modulepath[x] = '\0';
--- 3241,3245 ----
  			GetModuleFileName(NULL, modulepath, sizeof(modulepath));
  			for (i = x = 0; modulepath[i]; i++)
! 				if (modulepath[i] == SEP)
  					x = i+1;
  			modulepath[x] = '\0';
***************
*** 3149,3154 ****
  	return f;
  }
- #endif
  
  #endif /* HAVE_POPEN */
  
--- 3805,3810 ----
  	return f;
  }
  
+ #endif /* PYOS_??? */
  #endif /* HAVE_POPEN */
  
***************
*** 5605,5608 ****
--- 6261,6270 ----
  	{"popen4",	win32_popen4, METH_VARARGS},
  	{"startfile",	win32_startfile, METH_VARARGS, win32_startfile__doc__},
+ #else
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ 	{"popen2",	os2emx_popen2, METH_VARARGS},
+ 	{"popen3",	os2emx_popen3, METH_VARARGS},
+ 	{"popen4",	os2emx_popen4, METH_VARARGS},
+ #endif
  #endif
  #endif /* HAVE_POPEN */
***************
*** 5920,5923 ****
--- 6582,6607 ----
  
  #ifdef HAVE_SPAWNV
+ #if defined(PYOS_OS2) && defined(PYCC_GCC)
+ 	if (ins(d, "P_WAIT", (long)P_WAIT)) return -1;
+ 	if (ins(d, "P_NOWAIT", (long)P_NOWAIT)) return -1;
+ 	if (ins(d, "P_OVERLAY", (long)P_OVERLAY)) return -1;
+ 	if (ins(d, "P_DEBUG", (long)P_DEBUG)) return -1;
+ 	if (ins(d, "P_SESSION", (long)P_SESSION)) return -1;
+ 	if (ins(d, "P_DETACH", (long)P_DETACH)) return -1;
+ 	if (ins(d, "P_PM", (long)P_PM)) return -1;
+ 	if (ins(d, "P_DEFAULT", (long)P_DEFAULT)) return -1;
+ 	if (ins(d, "P_MINIMIZE", (long)P_MINIMIZE)) return -1;
+ 	if (ins(d, "P_MAXIMIZE", (long)P_MAXIMIZE)) return -1;
+ 	if (ins(d, "P_FULLSCREEN", (long)P_FULLSCREEN)) return -1;
+ 	if (ins(d, "P_WINDOWED", (long)P_WINDOWED)) return -1;
+ 	if (ins(d, "P_FOREGROUND", (long)P_FOREGROUND)) return -1;
+ 	if (ins(d, "P_BACKGROUND", (long)P_BACKGROUND)) return -1;
+ 	if (ins(d, "P_NOCLOSE", (long)P_NOCLOSE)) return -1;
+ 	if (ins(d, "P_NOSESSION", (long)P_NOSESSION)) return -1;
+ 	if (ins(d, "P_QUOTE", (long)P_QUOTE)) return -1;
+ 	if (ins(d, "P_TILDE", (long)P_TILDE)) return -1;
+ 	if (ins(d, "P_UNRELATED", (long)P_UNRELATED)) return -1;
+ 	if (ins(d, "P_DEBUGDESC", (long)P_DEBUGDESC)) return -1;
+ #else
          if (ins(d, "P_WAIT", (long)_P_WAIT)) return -1;
          if (ins(d, "P_NOWAIT", (long)_P_NOWAIT)) return -1;
***************
*** 5925,5928 ****
--- 6609,6613 ----
          if (ins(d, "P_NOWAITO", (long)_P_NOWAITO)) return -1;
          if (ins(d, "P_DETACH", (long)_P_DETACH)) return -1;
+ #endif
  #endif