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.  */
33static int
34dup_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"
83static char **
84prepare_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}
170