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 *
fix_argv(argvec)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
pexecute(program,argv,this_pname,temp_base,errmsg_fmt,errmsg_arg,flags)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
pwait(pid,status,flags)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 }
256