Refactor cross-plattform process spawning from ghc-inplace into shell-tools.c
[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 = 1; i < argc; i++) {
61         /* Note: play it safe and quote all argv strings */
62         cmdline_len += 1 + strlen(argv[i]) + 2;
63     }
64     new_cmdline = (char*)malloc(sizeof(char) * (cmdline_len + 1));
65     if (!new_cmdline) {
66         error("%s: failed to start up ghc.exe; insufficient memory\n", this);
67         return 1;
68     }
69
70     ptr = new_cmdline;
71     for(i = 1; i < argc; i++) {
72         *ptr++ = ' ';
73         *ptr++ = '"';
74         src = argv[i];
75         while(*src) {
76             *ptr++ = *src++;
77         }
78         *ptr++ = '"';
79     }
80     *ptr = '\0';
81
82     /* Note: Used to use _spawnv(_P_WAIT, ...) here, but it suffered
83        from the parent intercepting console events such as Ctrl-C,
84        which it shouldn't. Installing an ignore-all console handler
85        didn't do the trick either.
86
87        Irrespective of this issue, using CreateProcess() is preferable,
88        as it makes this wrapper work on both mingw and cygwin.
89     */
90 #if 0
91     fprintf(stderr, "Invoking ghc: %s %s\n", programShort, new_cmdline);
92     fflush(stderr);
93 #endif
94     if (!CreateProcess(programShort,
95                        new_cmdline,
96                        NULL,
97                        NULL,
98                        TRUE,
99                        0, /* dwCreationFlags */
100                        NULL, /* lpEnvironment */
101                        NULL, /* lpCurrentDirectory */
102                        &si,  /* lpStartupInfo */
103                        &pi) ) {
104         error("%s: Unable to start ghc.exe (error code: %lu)\n",
105               this, GetLastError());
106         return 1;
107     }
108     /* Disable handling of console events in the parent by dropping its
109      * connection to the console. This has the (minor) downside of not being
110      * able to subsequently emit any error messages to the console.
111      */
112     FreeConsole();
113
114     switch (WaitForSingleObject(pi.hProcess, INFINITE) ) {
115         case WAIT_OBJECT_0:
116             if (GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
117                 return dwExitCode;
118             }
119             else {
120                 return 1;
121             }
122         case WAIT_ABANDONED:
123         case WAIT_FAILED:
124             /* in the event we get any hard errors, bring the child
125                to a halt. */
126             TerminateProcess(pi.hProcess, 1);
127             return 1;
128         default:
129             return 1;
130     }
131 }
132 #endif
133