merge from gcc
[external/binutils.git] / libiberty / pex-win32.c
1 /* Utilities to execute a program in a subprocess (possibly linked by pipes
2    with other subprocesses), and wait for it.  Generic Win32 specialization.
3    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003
4    Free Software Foundation, Inc.
5
6 This file is part of the libiberty library.
7 Libiberty is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 Libiberty is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public
18 License along with libiberty; see the file COPYING.LIB.  If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.  */
21
22 #include "pex-common.h"
23
24 #ifdef HAVE_STRING_H
25 #include <string.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #ifdef HAVE_SYS_WAIT_H
31 #include <sys/wait.h>
32 #endif
33
34 #include <process.h>
35 #include <io.h>
36 #include <fcntl.h>
37 #include <signal.h>
38
39 /* mingw32 headers may not define the following.  */
40
41 #ifndef _P_WAIT
42 #  define _P_WAIT       0
43 #  define _P_NOWAIT     1
44 #  define _P_OVERLAY    2
45 #  define _P_NOWAITO    3
46 #  define _P_DETACH     4
47
48 #  define WAIT_CHILD            0
49 #  define WAIT_GRANDCHILD       1
50 #endif
51
52 /* This is a kludge to get around the Microsoft C spawn functions' propensity
53    to remove the outermost set of double quotes from all arguments.  */
54
55 static const char * const *
56 fix_argv (argvec)
57      char **argvec;
58 {
59   int i;
60   char * command0 = argvec[0];
61
62   /* Ensure that the executable pathname uses Win32 backslashes. This
63      is not necessary on NT, but on W9x, forward slashes causes failure
64      of spawn* and exec* functions (and probably any function that
65      calls CreateProcess) *iff* the executable pathname (argvec[0]) is
66      a quoted string.  And quoting is necessary in case a pathname
67      contains  embedded white space. You can't win.  */
68   for (; *command0 != '\0'; command0++)
69     if (*command0 == '/')
70       *command0 = '\\';
71  
72   for (i = 1; argvec[i] != 0; i++)
73     {
74       int len, j;
75       char *temp, *newtemp;
76
77       temp = argvec[i];
78       len = strlen (temp);
79       for (j = 0; j < len; j++)
80         {
81           if (temp[j] == '"')
82             {
83               newtemp = xmalloc (len + 2);
84               strncpy (newtemp, temp, j);
85               newtemp [j] = '\\';
86               strncpy (&newtemp [j+1], &temp [j], len-j);
87               newtemp [len+1] = 0;
88               temp = newtemp;
89               len++;
90               j++;
91             }
92         }
93
94         argvec[i] = temp;
95       }
96
97   for (i = 0; argvec[i] != 0; i++)
98     {
99       if (strpbrk (argvec[i], " \t"))
100         {
101           int len, trailing_backslash;
102           char *temp;
103
104           len = strlen (argvec[i]);
105           trailing_backslash = 0;
106
107           /* There is an added complication when an arg with embedded white
108              space ends in a backslash (such as in the case of -iprefix arg
109              passed to cpp). The resulting quoted strings gets misinterpreted
110              by the command interpreter -- it thinks that the ending quote
111              is escaped by the trailing backslash and things get confused. 
112              We handle this case by escaping the trailing backslash, provided
113              it was not escaped in the first place.  */
114           if (len > 1 
115               && argvec[i][len-1] == '\\' 
116               && argvec[i][len-2] != '\\')
117             {
118               trailing_backslash = 1;
119               ++len;                    /* to escape the final backslash. */
120             }
121
122           len += 2;                     /* and for the enclosing quotes. */
123
124           temp = xmalloc (len + 1);
125           temp[0] = '"';
126           strcpy (temp + 1, argvec[i]);
127           if (trailing_backslash)
128             temp[len-2] = '\\';
129           temp[len-1] = '"';
130           temp[len] = '\0';
131
132           argvec[i] = temp;
133         }
134     }
135
136   return (const char * const *) argvec;
137 }
138
139 /* Win32 supports pipes */
140 int
141 pexecute (program, argv, this_pname, temp_base, errmsg_fmt, errmsg_arg, flags)
142      const char *program;
143      char * const *argv;
144      const char *this_pname ATTRIBUTE_UNUSED;
145      const char *temp_base ATTRIBUTE_UNUSED;
146      char **errmsg_fmt, **errmsg_arg;
147      int flags;
148 {
149   int pid;
150   int pdes[2];
151   int org_stdin = -1;
152   int org_stdout = -1;
153   int input_desc, output_desc;
154
155   /* Pipe waiting from last process, to be used as input for the next one.
156      Value is STDIN_FILE_NO if no pipe is waiting
157      (i.e. the next command is the first of a group).  */
158   static int last_pipe_input;
159
160   /* If this is the first process, initialize.  */
161   if (flags & PEXECUTE_FIRST)
162     last_pipe_input = STDIN_FILE_NO;
163
164   input_desc = last_pipe_input;
165
166   /* If this isn't the last process, make a pipe for its output,
167      and record it as waiting to be the input to the next process.  */
168   if (! (flags & PEXECUTE_LAST))
169     {
170       if (_pipe (pdes, 256, O_BINARY) < 0)
171         {
172           *errmsg_fmt = "pipe";
173           *errmsg_arg = NULL;
174           return -1;
175         }
176       output_desc = pdes[WRITE_PORT];
177       last_pipe_input = pdes[READ_PORT];
178     }
179   else
180     {
181       /* Last process.  */
182       output_desc = STDOUT_FILE_NO;
183       last_pipe_input = STDIN_FILE_NO;
184     }
185
186   if (input_desc != STDIN_FILE_NO)
187     {
188       org_stdin = dup (STDIN_FILE_NO);
189       dup2 (input_desc, STDIN_FILE_NO);
190       close (input_desc); 
191     }
192
193   if (output_desc != STDOUT_FILE_NO)
194     {
195       org_stdout = dup (STDOUT_FILE_NO);
196       dup2 (output_desc, STDOUT_FILE_NO);
197       close (output_desc);
198     }
199
200   pid = (flags & PEXECUTE_SEARCH ? _spawnvp : _spawnv)
201     (_P_NOWAIT, program, fix_argv(argv));
202
203   if (input_desc != STDIN_FILE_NO)
204     {
205       dup2 (org_stdin, STDIN_FILE_NO);
206       close (org_stdin);
207     }
208
209   if (output_desc != STDOUT_FILE_NO)
210     {
211       dup2 (org_stdout, STDOUT_FILE_NO);
212       close (org_stdout);
213     }
214
215   if (pid == -1)
216     {
217       *errmsg_fmt = install_error_msg;
218       *errmsg_arg = (char*) program;
219       return -1;
220     }
221
222   return pid;
223 }
224
225 /* MS CRTDLL doesn't return enough information in status to decide if the
226    child exited due to a signal or not, rather it simply returns an
227    integer with the exit code of the child; eg., if the child exited with 
228    an abort() call and didn't have a handler for SIGABRT, it simply returns
229    with status = 3. We fix the status code to conform to the usual WIF*
230    macros. Note that WIFSIGNALED will never be true under CRTDLL. */
231
232 int
233 pwait (pid, status, flags)
234      int pid;
235      int *status;
236      int flags ATTRIBUTE_UNUSED;
237 {
238   int termstat;
239
240   pid = _cwait (&termstat, pid, WAIT_CHILD);
241
242   /* ??? Here's an opportunity to canonicalize the values in STATUS.
243      Needed?  */
244
245   /* cwait returns the child process exit code in termstat.
246      A value of 3 indicates that the child caught a signal, but not
247      which one.  Since only SIGABRT, SIGFPE and SIGINT do anything, we
248      report SIGABRT.  */
249   if (termstat == 3)
250     *status = SIGABRT;
251   else
252     *status = (((termstat) & 0xff) << 8);
253
254   return pid;
255 }