Make also the g_spawn*() functions take parameters in the GLib file name
[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   gchar **new_argv;
164   wchar_t **new_wargv;
165   int argc;
166   wchar_t **wargv, **wenvp;
167   _startupinfo si = { 0 };
168
169   g_assert (__argc >= ARG_COUNT);
170
171   if (G_WIN32_HAVE_WIDECHAR_API ())
172     {
173       /* Fetch the wide-char argument vector */
174       __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
175
176       /* We still have the system codepage args in __argv. We can look
177        * at the first args in which gspawn-win32.c passes us flags and
178        * fd numbers in __argv, as we know those are just ASCII anyway.
179        */
180       g_assert (argc == __argc);
181     }
182
183   /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
184    * which write error messages.
185    */
186   child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
187
188   /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
189    * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
190    * the program to run and its argv[0] separately.
191    */
192   if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
193     argv_zero_offset++;
194
195   /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
196    * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
197    * should be left alone, and 'z' if it should be connected to the
198    * bit bucket NUL:.
199    */
200   if (__argv[ARG_STDIN][0] == '-')
201     ; /* Nothing */
202   else if (__argv[ARG_STDIN][0] == 'z')
203     {
204       fd = open ("NUL:", O_RDONLY);
205       if (fd != 0)
206         {
207           dup2 (fd, 0);
208           close (fd);
209         }
210     }
211   else
212     {
213       fd = atoi (__argv[ARG_STDIN]);
214       if (fd != 0)
215         {
216           dup2 (fd, 0);
217           close (fd);
218         }
219     }
220
221   if (__argv[ARG_STDOUT][0] == '-')
222     ; /* Nothing */
223   else if (__argv[ARG_STDOUT][0] == 'z')
224     {
225       fd = open ("NUL:", O_WRONLY);
226       if (fd != 1)
227         {
228           dup2 (fd, 1);
229           close (fd);
230         }
231     }
232   else
233     {
234       fd = atoi (__argv[ARG_STDOUT]);
235       if (fd != 1)
236         {
237           dup2 (fd, 1);
238           close (fd);
239         }
240     }
241
242   if (__argv[ARG_STDERR][0] == '-')
243     ; /* Nothing */
244   else if (__argv[ARG_STDERR][0] == 'z')
245     {
246       fd = open ("NUL:", O_WRONLY);
247       if (fd != 2)
248         {
249           dup2 (fd, 2);
250           close (fd);
251         }
252     }
253   else
254     {
255       fd = atoi (__argv[ARG_STDERR]);
256       if (fd != 2)
257         {
258           dup2 (fd, 2);
259           close (fd);
260         }
261     }
262
263   /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
264    * process.  If "-", don't change directory.
265    */
266   if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
267       __argv[ARG_WORKING_DIRECTORY][1] == 0)
268     ; /* Nothing */
269   else if ((G_WIN32_HAVE_WIDECHAR_API () &&
270             _wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0) ||
271            (!G_WIN32_HAVE_WIDECHAR_API () &&
272             chdir (__argv[ARG_WORKING_DIRECTORY]) < 0))
273     write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
274
275   /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
276    *  upwards should be closed
277    */
278   if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
279     for (i = 3; i < 1000; i++)  /* FIXME real limit? */
280       if (i != child_err_report_fd)
281         close (i);
282
283   /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
284   if (__argv[ARG_WAIT][0] == 'w')
285     mode = P_WAIT;
286   else
287     mode = P_NOWAIT;
288
289   /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
290
291   /* __argv[ARG_PROGRAM] is executable file to run,
292    * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
293    * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
294    * case we have a separate executable name and argv[0].
295    */
296
297   /* For the program name passed to spawnv(), don't use the quoted
298    * version.
299    */
300   if (G_WIN32_HAVE_WIDECHAR_API ())
301     {
302       protect_wargv (wargv + argv_zero_offset, &new_wargv);
303
304       if (__argv[ARG_USE_PATH][0] == 'y')
305         handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
306       else
307         handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
308     }
309   else
310     {
311       protect_argv (__argv + argv_zero_offset, &new_argv);
312
313       if (__argv[ARG_USE_PATH][0] == 'y')
314         handle = spawnvp (mode, __argv[ARG_PROGRAM], (const char **) new_argv);
315       else
316         handle = spawnv (mode, __argv[ARG_PROGRAM], (const char **) new_argv);
317     }
318
319   saved_errno = errno;
320
321   if (handle == -1 && saved_errno != 0)
322     write_err_and_exit (child_err_report_fd, CHILD_SPAWN_FAILED);
323
324   write (child_err_report_fd, &no_error, sizeof (no_error));
325   if (mode == P_NOWAIT)
326     write (child_err_report_fd, &handle, sizeof (handle));
327   else
328     write (child_err_report_fd, &zero, sizeof (zero));
329   return 0;
330 }