Commit c3b7d57d authored by sof's avatar sof
Browse files

[project @ 2005-04-22 17:15:51 by sof]

Switch away from using _spawnv() to CreateProcess(); the former has the
annoying 'feature' that it quits upon Ctrl-C, leaving its child in the
background. Use CreateProcess() instead and avoid the Ctrl-C issue by
having the wrapper let go of its console before waiting for the sub-process
running GHCi to exit.

This still doesn't fix the issue of Ctrl-C handling when the 'ghci' wrapper
is invoked from a cygwin-based bash. cmd.exe users will hopefully see
an improvement in behaviour though.

Merge to STABLE.
parent ecb1a539
* $Id: ghci.c,v 1.8 2003/06/12 09:48:17 simonpj Exp $
* $Id: ghci.c,v 1.9 2005/04/22 17:15:51 sof Exp $
* ghci wrapper for Win32 only
......@@ -44,11 +44,14 @@
#include <process.h>
#include <malloc.h>
#include <stdlib.h>
#include <signal.h>
#include <io.h>
#define BINARY_NAME "ghc.exe"
#define IACTIVE_OPTION "--interactive"
#define errmsg(msg) fprintf(stderr, msg "\n"); fflush(stderr)
#define errmsg1(msg,val) fprintf(stderr, msg "\n",val); fflush(stderr)
main(int argc, char** argv)
......@@ -56,21 +59,28 @@ main(int argc, char** argv)
TCHAR binPathShort[MAX_PATH+1];
DWORD dwRes;
TCHAR* szEnd;
char** new_argv;
int i;
char* new_cmdline;
char *ptr, *src;
unsigned int cmdline_len = 0;
char **pp;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
/* Locate the binary we want to start up */
dwRes =
if (dwRes == 0) {
if ( !SearchPath(NULL,
&szEnd) ) {
errmsg("Unable to locate ghc.exe");
return 1;
......@@ -83,72 +93,70 @@ main(int argc, char** argv)
return 1;
new_argv = (char**)malloc(sizeof(char*) * (argc + 1 + 1));
if (new_argv == NULL) {
errmsg("failed to start up ghc.exe");
return 1;
/* Compute length of the flattened 'argv', including extra IACTIVE_OPTION (and spaces!) */
cmdline_len += 1 + strlen(IACTIVE_OPTION);
for(i=1;i<argc;i++) {
/* Note: play it safe and quote all argv strings */
cmdline_len += 1 + strlen(argv[i]) + 2;
new_argv[0] = binPathShort;
new_argv[1] = (char*)malloc(sizeof(char) * (strlen(IACTIVE_OPTION) + 1));
if (new_argv[1]) {
strcpy(new_argv[1], IACTIVE_OPTION);
} else {
errmsg("failed to start up ghc.exe");
return 1;
for ( i=1; i < argc; i++ ) {
int len = strlen(argv[i]);
/* to avoid quoting issues, surround each option in double quotes */
new_argv[i+1] = (char*)malloc(sizeof(char) * (len + 3));
if (new_argv[i+1] == NULL) {
errmsg("failed to start up ghc.exe");
new_cmdline = (char*)malloc(sizeof(char) * (cmdline_len + 1));
if (!new_cmdline) {
errmsg("failed to start up ghc.exe; insufficient memory");
return 1;
} else {
new_argv[i+1][0] = '"';
strcpy(1 + new_argv[i+1], argv[i]);
new_argv[i+1][len+1] = '"';
new_argv[i+1][len+2] = '\0';
new_argv[i+1] = NULL;
/* I was hoping to be able to use execv() here, but
the MS implementation of said function doesn't appear to
be quite right (the 'parent' app seems to exit without
waiting, which is not a spec-fulfilling thing to do).
Cygwin gives me the right behaviour, but does it by
implementing it in terms of spawnv(), so you pay
the cost of having to create an extra process.
Plus, of course, we aren't allowed to use Cygwin here, because
GHC does not assume Cygwin.
strcpy(new_cmdline, " " IACTIVE_OPTION);
ptr = new_cmdline + strlen(" " IACTIVE_OPTION);
for(i=1;i<argc;i++) {
*ptr++ = ' ';
*ptr++ = '"';
src = argv[i];
while(*src) {
*ptr++ = *src++;
*ptr++ = '"';
*ptr = '\0';
/* Note: Used to use _spawnv(_P_WAIT, ...) here, but it suffered
from the parent intercepting console events such as Ctrl-C,
which it shouldn't. Installing an ignore-all console handler
didn't do the trick either.
==> Just use spawnv(), which is provided by msvcrt.dll, the
Microsoft C runtime to which mingw delegates almost all
system calls
[Sigbjorn adds 12 Jun 03]
We probably ought to use CreateProcess() in ghci.c -- or better still an exec()-like
that didn't have to create a separate process from the wrapper (which is what that
code comment in there is driving at.)
CreateProcess() is a more wieldy function to invoke, which is probably why
I opted for spawnv(). spawnv() performs the equivalent of Prelude.unwords
(to look at the code itself, or at least an older version, see dospawn.c in the
vc98/crt/src/ directory of an MSVC6 installation.)
CreateProcess() is a native Win32 API though, which has the merit that it is
guaranteed to work the same with both the mingw and cygwin ports.
Irrespective of this issue, using CreateProcess() is preferable,
as it makes this wrapper work on both mingw and cygwin.
#if 0
fprintf(stderr, "Invoking ghc: ");
while (new_argv[i] != NULL) {
fprintf(stderr, "%s ", new_argv[i++]);
fprintf(stderr, "\n"); fflush(stderr);
fprintf(stderr, "Invoking ghc: %s %s\n", binPathShort, new_cmdline); fflush(stderr);
return _spawnv(_P_WAIT, binPath, new_argv);
if (!CreateProcess(binPathShort,
0, /* dwCreationFlags */
NULL, /* lpEnvironment */
NULL, /* lpCurrentDirectory */
&si, /* lpStartupInfo */
&pi) ) {
errmsg1("Unable to start ghc.exe (error code: %lu)", GetLastError());
return 1;
/* Disable handling of console events in the parent by dropping its
* connection to the console. This has the (minor) downside of not being
* able to subsequently emit any error messages to the console.
switch (WaitForSingleObject(pi.hProcess, INFINITE) ) {
return 0;
/* in the event we get any hard errors, bring the child to a halt. */
return 1;
return 1;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment