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. */ 32static int 33dup_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" 82static char ** 83prepare_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} 169