I spent some time debugging M-x shell in XEmacs on 32-bit Cygwin. Here's what I found out.
In the child after fork() but before exec(), the setsid() call in disconnect_controlling_terminal() is causing the subprocess not to function after it gets spawned. Here is a patch which works around the problem, enabling M-x shell to work for bash and zsh (at least): diff -r 00f2705e2cb3 src/sysdep.c --- a/src/sysdep.c Mon Jan 26 08:53:07 2015 -0500 +++ b/src/sysdep.c Tue Jan 27 22:15:16 2015 -0500 @@ -1319,7 +1319,7 @@ void disconnect_controlling_terminal (void) { -# ifdef HAVE_SETSID +# if defined(HAVE_SETSID) && !defined(CYGWIN) /* Controlling terminals are attached to a session. Create a new session for us; it will have no controlling terminal. This also, of course, puts us in our own HOWEVER - I don't understand why this should be necessary. I here reproduce all of disconnect_controlling_terminal() for your reading pleasure. void disconnect_controlling_terminal (void) { # if defined(HAVE_SETSID) && !defined(CYGWIN) /* Controlling terminals are attached to a session. Create a new session for us; it will have no controlling terminal. This also, of course, puts us in our own process group. */ setsid (); # else /* Put us in our own process group. */ EMACS_SEPARATE_PROCESS_GROUP (); # if defined (TIOCNOTTY) /* This is the older way of disconnecting the controlling terminal, on 4.3 BSD. We must open /dev/tty; using filedesc 0 is not sufficient because it could be something else (e.g. our stdin was redirected to another terminal). */ { int j = open ("/dev/tty", O_RDWR, 0); ioctl (j, TIOCNOTTY, 0); close (j); } # endif /* TIOCNOTTY */ /* On systems without TIOCNOTTY and without setsid(), we don't need to do anything more to disconnect our controlling terminal. Here is what the man page for termio(7) from a SYSV 3.2 system says: "The first terminal file opened by the process group leader of a terminal file not already associated with a process group becomes the control terminal for that process group. The control terminal plays a special role in handling quit and interrupt signals, as discussed below. The control terminal is inherited by a child process during a fork(2). A process can break this association by changing its process group using setpgrp(2)." */ # endif /* not HAVE_SETSID */ } Since Cygwin doesn't seem to have TIOCNOTTY, commenting out the setsid() call reduces disconnect_controlling_terminal to: void disconnect_controlling_terminal (void) { # 1330 "sysdep.c" setpgid (0, 0); # 1362 "sysdep.c" } Incidentally, that setpgid() call causes bash to complain at startup: bash: cannot set terminal process group (-1): Inappropriate ioctl for device bash: no job control in this shell Thanks for any insight you can offer. - Vin