Remove support for Windows 9x/ME, as will be done also in Pango and GTK+.
[platform/upstream/glib.git] / glib / gspawn-win32-helper.c
1 /* gspawn-win32-helper.c - Helper program for process launching on Win32.
2  *
3  *  Copyright 2000 Red Hat, Inc.
4  *  Copyright 2000 Tor Lillqvist
5  *
6  * GLib is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * GLib is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with GLib; see the file COPYING.LIB.  If not, write
18  * to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23
24 #undef G_LOG_DOMAIN
25 #include "glib.h"
26 #define GSPAWN_HELPER
27 #include "gspawn-win32.c"       /* For shared definitions */
28
29
30 static void
31 write_err_and_exit (gint fd,
32                     gint msg)
33 {
34   gint en = errno;
35   
36   write (fd, &msg, sizeof(msg));
37   write (fd, &en, sizeof(en));
38   
39   _exit (1);
40 }
41
42 #ifdef __GNUC__
43 #  ifndef _stdcall
44 #    define _stdcall  __attribute__((stdcall))
45 #  endif
46 #endif
47
48 /* We build gspawn-win32-helper.exe as a Windows GUI application
49  * to avoid any temporarily flashing console windows in case
50  * the gspawn function is invoked by a GUI program. Thus, no main()
51  * but a WinMain(). We do, however, still use argc and argv tucked
52  * away in the global __argc and __argv by the C runtime startup code.
53  */
54
55 /* Info peeked from mingw runtime's source code. __wgetmainargs() is a
56  * function to get the program's argv in wide char format.
57  */
58
59 typedef struct {
60   int newmode;
61 } _startupinfo;
62
63 extern void __wgetmainargs(int *argc,
64                            wchar_t ***wargv,
65                            wchar_t ***wenviron,
66                            int expand_wildcards,
67                            _startupinfo *startupinfo);
68
69 /* Copy of protect_argv that handles wchar_t strings */
70
71 static gint
72 protect_wargv (wchar_t  **wargv,
73                wchar_t ***new_wargv)
74 {
75   gint i;
76   gint argc = 0;
77   
78   while (wargv[argc])
79     ++argc;
80   *new_wargv = g_new (wchar_t *, argc+1);
81
82   /* Quote each argv element if necessary, so that it will get
83    * reconstructed correctly in the C runtime startup code.  Note that
84    * the unquoting algorithm in the C runtime is really weird, and
85    * rather different than what Unix shells do. See stdargv.c in the C
86    * runtime sources (in the Platform SDK, in src/crt).
87    *
88    * Note that an new_wargv[0] constructed by this function should
89    * *not* be passed as the filename argument to a _wspawn* or _wexec*
90    * family function. That argument should be the real file name
91    * without any quoting.
92    */
93   for (i = 0; i < argc; i++)
94     {
95       wchar_t *p = wargv[i];
96       wchar_t *q;
97       gint len = 0;
98       gboolean need_dblquotes = FALSE;
99       while (*p)
100         {
101           if (*p == ' ' || *p == '\t')
102             need_dblquotes = TRUE;
103           else if (*p == '"')
104             len++;
105           else if (*p == '\\')
106             {
107               wchar_t *pp = p;
108               while (*pp && *pp == '\\')
109                 pp++;
110               if (*pp == '"')
111                 len++;
112             }
113           len++;
114           p++;
115         }
116
117       q = (*new_wargv)[i] = g_new (wchar_t, len + need_dblquotes*2 + 1);
118       p = wargv[i];
119
120       if (need_dblquotes)
121         *q++ = '"';
122
123       while (*p)
124         {
125           if (*p == '"')
126             *q++ = '\\';
127           else if (*p == '\\')
128             {
129               wchar_t *pp = p;
130               while (*pp && *pp == '\\')
131                 pp++;
132               if (*pp == '"')
133                 *q++ = '\\';
134             }
135           *q++ = *p;
136           p++;
137         }
138
139       if (need_dblquotes)
140         *q++ = '"';
141       *q++ = '\0';
142     }
143   (*new_wargv)[argc] = NULL;
144
145   return argc;
146 }
147
148 int _stdcall
149 WinMain (struct HINSTANCE__ *hInstance,
150          struct HINSTANCE__ *hPrevInstance,
151          char               *lpszCmdLine,
152          int                 nCmdShow)
153 {
154   int child_err_report_fd;
155   int i;
156   int fd;
157   int mode;
158   int handle;
159   int saved_errno;
160   int no_error = CHILD_NO_ERROR;
161   int zero = 0;
162   gint argv_zero_offset = ARG_PROGRAM;
163   wchar_t **new_wargv;
164   int argc;
165   wchar_t **wargv, **wenvp;
166   _startupinfo si = { 0 };
167
168   g_assert (__argc >= ARG_COUNT);
169
170   /* Fetch the wide-char argument vector */
171   __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
172
173   /* We still have the system codepage args in __argv. We can look
174    * at the first args in which gspawn-win32.c passes us flags and
175    * fd numbers in __argv, as we know those are just ASCII anyway.
176    */
177   g_assert (argc == __argc);
178
179   /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
180    * which write error messages.
181    */
182   child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
183
184   /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
185    * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
186    * the program to run and its argv[0] separately.
187    */
188   if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
189     argv_zero_offset++;
190
191   /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
192    * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
193    * should be left alone, and 'z' if it should be connected to the
194    * bit bucket NUL:.
195    */
196   if (__argv[ARG_STDIN][0] == '-')
197     ; /* Nothing */
198   else if (__argv[ARG_STDIN][0] == 'z')
199     {
200       fd = open ("NUL:", O_RDONLY);
201       if (fd != 0)
202         {
203           dup2 (fd, 0);
204           close (fd);
205         }
206     }
207   else
208     {
209       fd = atoi (__argv[ARG_STDIN]);
210       if (fd != 0)
211         {
212           dup2 (fd, 0);
213           close (fd);
214         }
215     }
216
217   if (__argv[ARG_STDOUT][0] == '-')
218     ; /* Nothing */
219   else if (__argv[ARG_STDOUT][0] == 'z')
220     {
221       fd = open ("NUL:", O_WRONLY);
222       if (fd != 1)
223         {
224           dup2 (fd, 1);
225           close (fd);
226         }
227     }
228   else
229     {
230       fd = atoi (__argv[ARG_STDOUT]);
231       if (fd != 1)
232         {
233           dup2 (fd, 1);
234           close (fd);
235         }
236     }
237
238   if (__argv[ARG_STDERR][0] == '-')
239     ; /* Nothing */
240   else if (__argv[ARG_STDERR][0] == 'z')
241     {
242       fd = open ("NUL:", O_WRONLY);
243       if (fd != 2)
244         {
245           dup2 (fd, 2);
246           close (fd);
247         }
248     }
249   else
250     {
251       fd = atoi (__argv[ARG_STDERR]);
252       if (fd != 2)
253         {
254           dup2 (fd, 2);
255           close (fd);
256         }
257     }
258
259   /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
260    * process.  If "-", don't change directory.
261    */
262   if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
263       __argv[ARG_WORKING_DIRECTORY][1] == 0)
264     ; /* Nothing */
265   else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0)
266     write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
267
268   /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
269    *  upwards should be closed
270    */
271   if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
272     for (i = 3; i < 1000; i++)  /* FIXME real limit? */
273       if (i != child_err_report_fd)
274         close (i);
275
276   /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
277   if (__argv[ARG_WAIT][0] == 'w')
278     mode = P_WAIT;
279   else
280     mode = P_NOWAIT;
281
282   /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
283
284   /* __argv[ARG_PROGRAM] is executable file to run,
285    * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
286    * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
287    * case we have a separate executable name and argv[0].
288    */
289
290   /* For the program name passed to spawnv(), don't use the quoted
291    * version.
292    */
293   protect_wargv (wargv + argv_zero_offset, &new_wargv);
294
295   if (__argv[ARG_USE_PATH][0] == 'y')
296     handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
297   else
298     handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
299
300   saved_errno = errno;
301
302   if (handle == -1 && saved_errno != 0)
303     write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
304
305   write (child_err_report_fd, &no_error, sizeof (no_error));
306   if (mode == P_NOWAIT)
307     write (child_err_report_fd, &handle, sizeof (handle));
308   else
309     write (child_err_report_fd, &zero, sizeof (zero));
310   return 0;
311 }