Escape arguments for Windows in 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         /* 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 = 1; 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
87     /* Note: Used to use _spawnv(_P_WAIT, ...) here, but it suffered
88        from the parent intercepting console events such as Ctrl-C,
89        which it shouldn't. Installing an ignore-all console handler
90        didn't do the trick either.
91
92        Irrespective of this issue, using CreateProcess() is preferable,
93        as it makes this wrapper work on both mingw and cygwin.
94     */
95 #if 0
96     fprintf(stderr, "Invoking ghc: %s %s\n", programShort, new_cmdline);
97     fflush(stderr);
98 #endif
99     if (!CreateProcess(programShort,
100                        new_cmdline,
101                        NULL,
102                        NULL,
103                        TRUE,
104                        0, /* dwCreationFlags */
105                        NULL, /* lpEnvironment */
106                        NULL, /* lpCurrentDirectory */
107                        &si,  /* lpStartupInfo */
108                        &pi) ) {
109         error("%s: Unable to start ghc.exe (error code: %lu)\n",
110               this, GetLastError());
111         return 1;
112     }
113     /* Disable handling of console events in the parent by dropping its
114      * connection to the console. This has the (minor) downside of not being
115      * able to subsequently emit any error messages to the console.
116      */
117     FreeConsole();
118
119     switch (WaitForSingleObject(pi.hProcess, INFINITE) ) {
120         case WAIT_OBJECT_0:
121             if (GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
122                 return dwExitCode;
123             }
124             else {
125                 return 1;
126             }
127         case WAIT_ABANDONED:
128         case WAIT_FAILED:
129             /* in the event we get any hard errors, bring the child
130                to a halt. */
131             TerminateProcess(pi.hProcess, 1);
132             return 1;
133         default:
134             return 1;
135     }
136 }
137 #endif
138