xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/gnulib-lib/w32spawn.h (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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
dup_noinherit(int fd)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 **
prepare_spawn(char ** argv)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 }
170