7ebbb4c58e6eec2477a7c5f3bd33007d686c7f84
[gnulib.git] / lib / w32spawn.h
1 /* Auxiliary functions for the creation of subprocesses.  Native Woe32 API.
2    Copyright (C) 2003, 2006, 2007 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            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 (argc + 1, char *);
95
96   /* Put quoted arguments into the new argument vector.  */
97   for (i = 0; i < argc; i++)
98     {
99       const char *string = argv[i];
100
101       if (string[0] == '\0')
102         new_argv[i] = xstrdup ("\"\"");
103       else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
104         {
105           bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
106           size_t length;
107           unsigned int backslashes;
108           const char *s;
109           char *quoted_string;
110           char *p;
111
112           length = 0;
113           backslashes = 0;
114           if (quote_around)
115             length++;
116           for (s = string; *s != '\0'; s++)
117             {
118               char c = *s;
119               if (c == '"')
120                 length += backslashes + 1;
121               length++;
122               if (c == '\\')
123                 backslashes++;
124               else
125                 backslashes = 0;
126             }
127           if (quote_around)
128             length += backslashes + 1;
129
130           quoted_string = (char *) xmalloc (length + 1);
131
132           p = quoted_string;
133           backslashes = 0;
134           if (quote_around)
135             *p++ = '"';
136           for (s = string; *s != '\0'; s++)
137             {
138               char c = *s;
139               if (c == '"')
140                 {
141                   unsigned int j;
142                   for (j = backslashes + 1; j > 0; j--)
143                     *p++ = '\\';
144                 }
145               *p++ = c;
146               if (c == '\\')
147                 backslashes++;
148               else
149                 backslashes = 0;
150             }
151           if (quote_around)
152             {
153               unsigned int j;
154               for (j = backslashes; j > 0; j--)
155                 *p++ = '\\';
156               *p++ = '"';
157             }
158           *p = '\0';
159
160           new_argv[i] = quoted_string;
161         }
162       else
163         new_argv[i] = (char *) string;
164     }
165   new_argv[argc] = NULL;
166
167   return new_argv;
168 }