Merge commit 'b572c3a256e7bf1e4eecc8c36448c08093240a6a' into stable
[gnulib.git] / lib / w32spawn.h
1 /* Auxiliary functions for the creation of subprocesses.  Native Woe32 API.
2    Copyright (C) 2001, 2003-2011 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 /* Get declarations of the Win32 API functions.  */
19 #define WIN32_LEAN_AND_MEAN
20 #include <windows.h>
21
22 /* Get _open_osfhandle().  */
23 #include <io.h>
24
25 #include <stdbool.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <errno.h>
29
30 /* Get _get_osfhandle().  */
31 #include "msvc-nothrow.h"
32
33 #include "cloexec.h"
34 #include "xalloc.h"
35
36 /* Duplicates a file handle, making the copy uninheritable.
37    Returns -1 for a file handle that is equivalent to closed.  */
38 static int
39 dup_noinherit (int fd)
40 {
41   fd = dup_cloexec (fd);
42   if (fd < 0 && errno == EMFILE)
43     error (EXIT_FAILURE, errno, _("_open_osfhandle failed"));
44
45   return fd;
46 }
47
48 /* Returns a file descriptor equivalent to FD, except that the resulting file
49    descriptor is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
50    FD must be open and non-inheritable.  The result will be non-inheritable as
51    well.
52    If FD < 0, FD itself is returned.  */
53 static int
54 fd_safer_noinherit (int fd)
55 {
56   if (STDIN_FILENO <= fd && fd <= STDERR_FILENO)
57     {
58       /* The recursion depth is at most 3.  */
59       int nfd = fd_safer_noinherit (dup_noinherit (fd));
60       int saved_errno = errno;
61       close (fd);
62       errno = saved_errno;
63       return nfd;
64     }
65   return fd;
66 }
67
68 /* Duplicates a file handle, making the copy uninheritable and ensuring the
69    result is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
70    Returns -1 for a file handle that is equivalent to closed.  */
71 static int
72 dup_safer_noinherit (int fd)
73 {
74   return fd_safer_noinherit (dup_noinherit (fd));
75 }
76
77 /* Undoes the effect of TEMPFD = dup_safer_noinherit (ORIGFD);  */
78 static void
79 undup_safer_noinherit (int tempfd, int origfd)
80 {
81   if (tempfd >= 0)
82     {
83       if (dup2 (tempfd, origfd) < 0)
84         error (EXIT_FAILURE, errno, _("cannot restore fd %d: dup2 failed"),
85                origfd);
86       close (tempfd);
87     }
88   else
89     {
90       /* origfd was closed or open to no handle at all.  Set it to a closed
91          state.  This is (nearly) equivalent to the original state.  */
92       close (origfd);
93     }
94 }
95
96 /* Prepares an argument vector before calling spawn().
97    Note that spawn() does not by itself call the command interpreter
98      (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
99       ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
100          GetVersionEx(&v);
101          v.dwPlatformId == VER_PLATFORM_WIN32_NT;
102       }) ? "cmd.exe" : "command.com").
103    Instead it simply concatenates the arguments, separated by ' ', and calls
104    CreateProcess().  We must quote the arguments since Win32 CreateProcess()
105    interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
106    special way:
107    - Space and tab are interpreted as delimiters. They are not treated as
108      delimiters if they are surrounded by double quotes: "...".
109    - Unescaped double quotes are removed from the input. Their only effect is
110      that within double quotes, space and tab are treated like normal
111      characters.
112    - Backslashes not followed by double quotes are not special.
113    - But 2*n+1 backslashes followed by a double quote become
114      n backslashes followed by a double quote (n >= 0):
115        \" -> "
116        \\\" -> \"
117        \\\\\" -> \\"
118  */
119 #define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
120 #define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
121 static char **
122 prepare_spawn (char **argv)
123 {
124   size_t argc;
125   char **new_argv;
126   size_t i;
127
128   /* Count number of arguments.  */
129   for (argc = 0; argv[argc] != NULL; argc++)
130     ;
131
132   /* Allocate new argument vector.  */
133   new_argv = XNMALLOC (1 + argc + 1, char *);
134
135   /* Add an element upfront that can be used when argv[0] turns out to be a
136      script, not a program.
137      On Unix, this would be "/bin/sh". On native Windows, "sh" is actually
138      "sh.exe".  We have to omit the directory part and rely on the search in
139      PATH, because the mingw "mount points" are not visible inside Win32
140      CreateProcess().  */
141   *new_argv++ = "sh.exe";
142
143   /* Put quoted arguments into the new argument vector.  */
144   for (i = 0; i < argc; i++)
145     {
146       const char *string = argv[i];
147
148       if (string[0] == '\0')
149         new_argv[i] = xstrdup ("\"\"");
150       else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
151         {
152           bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
153           size_t length;
154           unsigned int backslashes;
155           const char *s;
156           char *quoted_string;
157           char *p;
158
159           length = 0;
160           backslashes = 0;
161           if (quote_around)
162             length++;
163           for (s = string; *s != '\0'; s++)
164             {
165               char c = *s;
166               if (c == '"')
167                 length += backslashes + 1;
168               length++;
169               if (c == '\\')
170                 backslashes++;
171               else
172                 backslashes = 0;
173             }
174           if (quote_around)
175             length += backslashes + 1;
176
177           quoted_string = (char *) xmalloc (length + 1);
178
179           p = quoted_string;
180           backslashes = 0;
181           if (quote_around)
182             *p++ = '"';
183           for (s = string; *s != '\0'; s++)
184             {
185               char c = *s;
186               if (c == '"')
187                 {
188                   unsigned int j;
189                   for (j = backslashes + 1; j > 0; j--)
190                     *p++ = '\\';
191                 }
192               *p++ = c;
193               if (c == '\\')
194                 backslashes++;
195               else
196                 backslashes = 0;
197             }
198           if (quote_around)
199             {
200               unsigned int j;
201               for (j = backslashes; j > 0; j--)
202                 *p++ = '\\';
203               *p++ = '"';
204             }
205           *p = '\0';
206
207           new_argv[i] = quoted_string;
208         }
209       else
210         new_argv[i] = (char *) string;
211     }
212   new_argv[argc] = NULL;
213
214   return new_argv;
215 }