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