Avoid a gcc warning.
[gnulib.git] / lib / w32spawn.h
1 /* Auxiliary functions for the creation of subprocesses.  Native Woe32 API.
2    Copyright (C) 2003, 2006-2009 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 _get_osfhandle() and _open_osfhandle().  */
23 #include <io.h>
24
25 #include <stdbool.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include "xalloc.h"
30
31 /* Duplicates a file handle, making the copy uninheritable.  */
32 static int
33 dup_noinherit (int fd)
34 {
35   HANDLE curr_process = GetCurrentProcess ();
36   HANDLE old_handle = (HANDLE) _get_osfhandle (fd);
37   HANDLE new_handle;
38   int nfd;
39
40   if (!DuplicateHandle (curr_process,               /* SourceProcessHandle */
41                         old_handle,                 /* SourceHandle */
42                         curr_process,               /* TargetProcessHandle */
43                         (PHANDLE) &new_handle,      /* TargetHandle */
44                         (DWORD) 0,                  /* DesiredAccess */
45                         FALSE,                      /* InheritHandle */
46                         DUPLICATE_SAME_ACCESS))     /* Options */
47     error (EXIT_FAILURE, 0, _("DuplicateHandle failed with error code 0x%08x"),
48            (unsigned int) GetLastError ());
49
50   nfd = _open_osfhandle ((long) new_handle, O_BINARY);
51   if (nfd < 0)
52     error (EXIT_FAILURE, errno, _("_open_osfhandle failed"));
53
54   return nfd;
55 }
56
57 /* Prepares an argument vector before calling spawn().
58    Note that spawn() does not by itself call the command interpreter
59      (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
60       ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
61          GetVersionEx(&v);
62          v.dwPlatformId == VER_PLATFORM_WIN32_NT;
63       }) ? "cmd.exe" : "command.com").
64    Instead it simply concatenates the arguments, separated by ' ', and calls
65    CreateProcess().  We must quote the arguments since Win32 CreateProcess()
66    interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
67    special way:
68    - Space and tab are interpreted as delimiters. They are not treated as
69      delimiters if they are surrounded by double quotes: "...".
70    - Unescaped double quotes are removed from the input. Their only effect is
71      that within double quotes, space and tab are treated like normal
72      characters.
73    - Backslashes not followed by double quotes are not special.
74    - But 2*n+1 backslashes followed by a double quote become
75      n backslashes followed by a double quote (n >= 0):
76        \" -> "
77        \\\" -> \"
78        \\\\\" -> \\"
79  */
80 #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"
81 #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"
82 static char **
83 prepare_spawn (char **argv)
84 {
85   size_t argc;
86   char **new_argv;
87   size_t i;
88
89   /* Count number of arguments.  */
90   for (argc = 0; argv[argc] != NULL; argc++)
91     ;
92
93   /* Allocate new argument vector.  */
94   new_argv = XNMALLOC (1 + argc + 1, char *);
95
96   /* Add an element upfront that can be used when argv[0] turns out to be a
97      script, not a program.
98      On Unix, this would be "/bin/sh". On native Windows, "sh" is actually
99      "sh.exe".  We have to omit the directory part and rely on the search in
100      PATH, because the mingw "mount points" are not visible inside Win32
101      CreateProcess().  */
102   *new_argv++ = "sh.exe";
103
104   /* Put quoted arguments into the new argument vector.  */
105   for (i = 0; i < argc; i++)
106     {
107       const char *string = argv[i];
108
109       if (string[0] == '\0')
110         new_argv[i] = xstrdup ("\"\"");
111       else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
112         {
113           bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
114           size_t length;
115           unsigned int backslashes;
116           const char *s;
117           char *quoted_string;
118           char *p;
119
120           length = 0;
121           backslashes = 0;
122           if (quote_around)
123             length++;
124           for (s = string; *s != '\0'; s++)
125             {
126               char c = *s;
127               if (c == '"')
128                 length += backslashes + 1;
129               length++;
130               if (c == '\\')
131                 backslashes++;
132               else
133                 backslashes = 0;
134             }
135           if (quote_around)
136             length += backslashes + 1;
137
138           quoted_string = (char *) xmalloc (length + 1);
139
140           p = quoted_string;
141           backslashes = 0;
142           if (quote_around)
143             *p++ = '"';
144           for (s = string; *s != '\0'; s++)
145             {
146               char c = *s;
147               if (c == '"')
148                 {
149                   unsigned int j;
150                   for (j = backslashes + 1; j > 0; j--)
151                     *p++ = '\\';
152                 }
153               *p++ = c;
154               if (c == '\\')
155                 backslashes++;
156               else
157                 backslashes = 0;
158             }
159           if (quote_around)
160             {
161               unsigned int j;
162               for (j = backslashes; j > 0; j--)
163                 *p++ = '\\';
164               *p++ = '"';
165             }
166           *p = '\0';
167
168           new_argv[i] = quoted_string;
169         }
170       else
171         new_argv[i] = (char *) string;
172     }
173   new_argv[argc] = NULL;
174
175   return new_argv;
176 }