1/* Utilities to execute a program in a subprocess (possibly linked by pipes
2   with other subprocesses), and wait for it.  Generic MSDOS specialization.
3   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2005
4   Free Software Foundation, Inc.
5
6This file is part of the libiberty library.
7Libiberty is free software; you can redistribute it and/or
8modify it under the terms of the GNU Library General Public
9License as published by the Free Software Foundation; either
10version 2 of the License, or (at your option) any later version.
11
12Libiberty is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15Library General Public License for more details.
16
17You should have received a copy of the GNU Library General Public
18License along with libiberty; see the file COPYING.LIB.  If not,
19write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
20Boston, MA 02110-1301, USA.  */
21
22#include "pex-common.h"
23
24#include <stdio.h>
25#include <errno.h>
26#ifdef NEED_DECLARATION_ERRNO
27extern int errno;
28#endif
29#ifdef HAVE_STRING_H
30#include <string.h>
31#endif
32#ifdef HAVE_STDLIB_H
33#include <stdlib.h>
34#endif
35
36#include "safe-ctype.h"
37#include <process.h>
38
39/* The structure we keep in obj->sysdep.  */
40
41#define PEX_MSDOS_FILE_COUNT 3
42
43#define PEX_MSDOS_FD_OFFSET 10
44
45struct pex_msdos
46{
47  /* An array of file names.  We refer to these using file descriptors
48     of 10 + array index.  */
49  const char *files[PEX_MSDOS_FILE_COUNT];
50  /* Exit statuses of programs which have been run.  */
51  int *statuses;
52};
53
54static int pex_msdos_open (struct pex_obj *, const char *, int);
55static int pex_msdos_open (struct pex_obj *, const char *, int);
56static int pex_msdos_fdindex (struct pex_msdos *, int);
57static long pex_msdos_exec_child (struct pex_obj *, int, const char *,
58				  char * const *, int, int, int,
59				  const char **, int *);
60static int pex_msdos_close (struct pex_obj *, int);
61static int pex_msdos_wait (struct pex_obj *, long, int *, struct pex_time *,
62			   int, const char **, int *);
63static void pex_msdos_cleanup (struct pex_obj *);
64
65/* The list of functions we pass to the common routines.  */
66
67const struct pex_funcs funcs =
68{
69  pex_msdos_open,
70  pex_msdos_open,
71  pex_msdos_exec_child,
72  pex_msdos_close,
73  pex_msdos_wait,
74  NULL, /* pipe */
75  NULL, /* fdopenr */
76  NULL, /* fdopenw */
77  pex_msdos_cleanup
78};
79
80/* Return a newly initialized pex_obj structure.  */
81
82struct pex_obj *
83pex_init (int flags, const char *pname, const char *tempbase)
84{
85  struct pex_obj *ret;
86  int i;
87
88  /* MSDOS does not support pipes.  */
89  flags &= ~ PEX_USE_PIPES;
90
91  ret = pex_init_common (flags, pname, tempbase, funcs);
92
93  ret->sysdep = XNEW (struct pex_msdos);
94  for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
95    ret->files[i] = NULL;
96  ret->statuses = NULL;
97
98  return ret;
99}
100
101/* Open a file.  FIXME: We ignore the binary argument, since we have
102   no way to handle it.  */
103
104static int
105pex_msdos_open (struct pex_obj *obj, const char *name,
106		int binary ATTRIBUTE_UNUSED)
107{
108  struct pex_msdos *ms;
109  int i;
110
111  ms = (struct pex_msdos *) obj->sysdep;
112
113  for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
114    {
115      if (ms->files[i] == NULL)
116	{
117	  ms->files[i] = xstrdup (name);
118	  return i + PEX_MSDOS_FD_OFFSET;
119	}
120    }
121
122  abort ();
123}
124
125/* Get the index into msdos->files associated with an open file
126   descriptor.  */
127
128static int
129pex_msdos_fdindex (struct pex_msdos *ms, int fd)
130{
131  fd -= PEX_MSDOS_FD_OFFSET;
132  if (fd < 0 || fd >= PEX_MSDOS_FILE_COUNT || ms->files[fd] == NULL)
133    abort ();
134  return fd;
135}
136
137
138/* Close a file.  */
139
140static int
141pex_msdos_close (struct pex_obj *obj, int fd)
142{
143  struct pex_msdos *ms;
144  int fdinex;
145
146  ms = (struct pex_msdos *) obj->sysdep;
147  fdindex = pe_msdos_fdindex (ms, fd);
148  free (ms->files[fdindex]);
149  ms->files[fdindex] = NULL;
150}
151
152/* Execute a child.  */
153
154static long
155pex_msdos_exec_child (struct pex_obj *obj, int flags, const char *executable,
156		      char * const * argv, int in, int out,
157		      int errdes ATTRIBUTE_UNUSED, const char **errmsg,
158		      int *err)
159{
160  struct pex_msdos *ms;
161  char *temp_base;
162  int temp_base_allocated;
163  char *rf;
164  int inindex;
165  char *infile;
166  int outindex;
167  char *outfile;
168  char *scmd;
169  FILE *argfile;
170  int i;
171  int status;
172
173  ms = (struct pex_msdos *) obj->sysdep;
174
175  /* FIXME: I don't know how to redirect stderr, so we ignore ERRDES
176     and PEX_STDERR_TO_STDOUT.  */
177
178  temp_base = obj->temp_base;
179  if (temp_base != NULL)
180    temp_base_allocated = 0;
181  else
182    {
183      temp_base = choose_temp_base ();
184      temp_base_allocated = 1;
185    }
186
187  rf = concat (temp_base, ".gp", NULL);
188
189  if (temp_base_allocated)
190    free (temp_base);
191
192  if (in == STDIN_FILE_NO)
193    {
194      inindex = -1;
195      infile = "";
196    }
197  else
198    {
199      inindex = pex_msdos_fdindex (ms, in);
200      infile = ms->files[inindex];
201    }
202
203  if (out == STDOUT_FILE_NO)
204    {
205      outindex = -1;
206      outfile = "";
207    }
208  else
209    {
210      outindex = pex_msdos_fdindex (ms, out);
211      outfile = ms->files[outindex];
212    }
213
214  scmd = XNEWVEC (char, strlen (program)
215		  + ((flags & PEXECUTE_SEARCH) != 0 ? 4 : 0)
216		  + strlen (rf)
217		  + strlen (infile)
218		  + strlen (outfile)
219		  + 10);
220  sprintf (scmd, "%s%s @%s%s%s%s%s",
221	   program,
222	   (flags & PEXECUTE_SEARCH) != 0 ? ".exe" : "",
223	   rf,
224	   inindex != -1 ? " <" : "",
225	   infile,
226	   outindex != -1 ? " >" : "",
227	   outfile);
228
229  argfile = fopen (rf, "w");
230  if (argfile == NULL)
231    {
232      *err = errno;
233      free (scmd);
234      free (rf);
235      *errmsg = "cannot open temporary command file";
236      return -1;
237    }
238
239  for (i = 1; argv[i] != NULL; ++i)
240    {
241      char *p;
242
243      for (p = argv[i]; *p != '\0'; ++p)
244	{
245	  if (*p == '"' || *p == '\'' || *p == '\\' || ISSPACE (*p))
246	    putc ('\\', argfile);
247	  putc (*p, argfile);
248	}
249      putc ('\n', argfile);
250    }
251
252  fclose (argfile);
253
254  status = system (scmd);
255
256  if (status == -1)
257    {
258      *err = errno;
259      remove (rf);
260      free (scmd);
261      free (rf);
262      *errmsg = "system";
263      return -1;
264    }
265
266  remove (rf);
267  free (scmd);
268  free (rf);
269
270  /* Save the exit status for later.  When we are called, obj->count
271     is the number of children which have executed before this
272     one.  */
273  ms->statuses = XRESIZEVEC(int, ms->statuses, obj->count + 1);
274  ms->statuses[obj->count] = status;
275
276  return obj->count;
277}
278
279/* Wait for a child process to complete.  Actually the child process
280   has already completed, and we just need to return the exit
281   status.  */
282
283static int
284pex_msdos_wait (struct pex_obj *obj, long pid, int *status,
285		struct pex_time *time, int done ATTRIBUTE_UNUSED,
286		const char **errmsg ATTRIBUTE_UNUSED,
287		int *err ATTRIBUTE_UNUSED)
288{
289  struct pex_msdos *ms;
290
291  ms = (struct pex_msdos *) obj->sysdep;
292
293  if (time != NULL)
294    memset (time, 0, sizeof *time);
295
296  *status = ms->statuses[pid];
297
298  return 0;
299}
300
301/* Clean up the pex_msdos structure.  */
302
303static void
304pex_msdos_cleanup (struct pex_obj  *obj)
305{
306  struct pex_msdos *ms;
307  int i;
308
309  ms = (struct pex_msdos *) obj->sysdep;
310  for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
311    if (msdos->files[i] != NULL)
312      free (msdos->files[i]);
313  if (msdos->statuses != NULL)
314    free (msdos->statuses);
315  free (msdos);
316  obj->sysdep = NULL;
317}
318