Merge branch 'master' of ssh://darcs.haskell.org/srv/darcs/ghc
[ghc-hetmet.git] / includes / shell-tools.c
1 #include <stdio.h>
2 #include <stdarg.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include <ghcplatform.h>
7
8 #ifdef mingw32_HOST_OS
9 #include <windows.h>
10 #include <process.h>
11 #include <malloc.h>
12 #include <signal.h>
13 #include <io.h>
14 #endif
15
16 void error(const char *fmt, ...) {
17     va_list argp;
18     va_start(argp, fmt);
19     vfprintf(stderr, fmt, argp);
20     va_end(argp);
21     fflush(stderr);
22 }
23
24 #ifndef mingw32_HOST_OS
25 int run(char *this, char *program, int argc, char** argv) {
26     execv(program, argv);
27     error("%s: Unable to start %s: ", this, program);
28     perror("");
29     return 1; /* Not reached */
30 }
31 #else
32 int run(char *this, char *program, int argc, char** argv) {
33     TCHAR  programShort[MAX_PATH+1];
34     DWORD  dwSize;
35     DWORD  dwExitCode;
36     int    i;
37     char*  new_cmdline;
38     char   *ptr;
39     char   *src;
40     unsigned int cmdline_len;
41
42     STARTUPINFO si;
43     PROCESS_INFORMATION pi;
44   
45     ZeroMemory(&si, sizeof(STARTUPINFO));
46     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
47
48     si.cb = sizeof(STARTUPINFO);
49
50     dwSize = MAX_PATH;
51     /* Turn the path into short form - LFN form causes problems
52        when passed in argv[0]. */
53     if ( !(GetShortPathName(program, programShort, dwSize)) ) {
54         error("%s: Unable to locate %s\n", this, program);
55         return 1;
56     }
57   
58     /* Compute length of the flattened 'argv', including spaces! */
59     cmdline_len = 0;
60     for(i = 0; i < argc; i++) {
61         /* Note: play it safe and quote all argv strings */
62         /* In the worst case we have to escape every character with a \ */
63         cmdline_len += 1 + 2 * strlen(argv[i]) + 2;
64     }
65     new_cmdline = (char*)malloc(sizeof(char) * (cmdline_len + 1));
66     if (!new_cmdline) {
67         error("%s: failed to start up ghc.exe; insufficient memory\n", this);
68         return 1;
69     }
70
71     ptr = new_cmdline;
72     for(i = 0; i < argc; i++) {
73         *ptr++ = ' ';
74         *ptr++ = '"';
75         src = argv[i];
76         while(*src) {
77             /* Escape any \ and " characters */
78             if ((*src == '\\') || (*src == '"')) {
79                 *ptr++ = '\\';
80             }
81             *ptr++ = *src++;
82         }
83         *ptr++ = '"';
84     }
85     *ptr = '\0';
86     new_cmdline = new_cmdline + 1; /* Skip the leading space */
87
88     /* Note: Used to use _spawnv(_P_WAIT, ...) here, but it suffered
89        from the parent intercepting console events such as Ctrl-C,
90        which it shouldn't. Installing an ignore-all console handler
91        didn't do the trick either.
92
93        Irrespective of this issue, using CreateProcess() is preferable,
94        as it makes this wrapper work on both mingw and cygwin.
95     */
96 #if 0
97     fprintf(stderr, "Invoking ghc: %s %s\n", programShort, new_cmdline);
98     fflush(stderr);
99 #endif
100     if (!CreateProcess(programShort,
101                        new_cmdline,
102                        NULL,
103                        NULL,
104                        TRUE,
105                        0, /* dwCreationFlags */
106                        NULL, /* lpEnvironment */
107                        NULL, /* lpCurrentDirectory */
108                        &si,  /* lpStartupInfo */
109                        &pi) ) {
110         error("%s: Unable to start ghc.exe (error code: %lu)\n",
111               this, GetLastError());
112         return 1;
113     }
114     /* Disable handling of console events in the parent by dropping its
115      * connection to the console. This has the (minor) downside of not being
116      * able to subsequently emit any error messages to the console.
117      */
118     FreeConsole();
119
120     switch (WaitForSingleObject(pi.hProcess, INFINITE) ) {
121         case WAIT_OBJECT_0:
122             if (GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
123                 return dwExitCode;
124             }
125             else {
126                 return 1;
127             }
128         case WAIT_ABANDONED:
129         case WAIT_FAILED:
130             /* in the event we get any hard errors, bring the child
131                to a halt. */
132             TerminateProcess(pi.hProcess, 1);
133             return 1;
134         default:
135             return 1;
136     }
137 }
138 #endif
139