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-2022 Free Software Foundation, Inc.
4
5This file is part of the libiberty library.
6Libiberty is free software; you can redistribute it and/or
7modify it under the terms of the GNU Library General Public
8License as published by the Free Software Foundation; either
9version 2 of the License, or (at your option) any later version.
10
11Libiberty is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14Library General Public License for more details.
15
16You should have received a copy of the GNU Library General Public
17License along with libiberty; see the file COPYING.LIB.  If not,
18write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19Boston, MA 02110-1301, USA.  */
20
21#include "pex-common.h"
22
23#include <stdio.h>
24#include <errno.h>
25#ifdef NEED_DECLARATION_ERRNO
26extern int errno;
27#endif
28#ifdef HAVE_STRING_H
29#include <string.h>
30#endif
31#ifdef HAVE_STDLIB_H
32#include <stdlib.h>
33#endif
34
35#include "safe-ctype.h"
36#include <process.h>
37
38/* The structure we keep in obj->sysdep.  */
39
40#define PEX_MSDOS_FILE_COUNT 3
41
42#define PEX_MSDOS_FD_OFFSET 10
43
44struct pex_msdos
45{
46  /* An array of file names.  We refer to these using file descriptors
47     of 10 + array index.  */
48  const char *files[PEX_MSDOS_FILE_COUNT];
49  /* Exit statuses of programs which have been run.  */
50  int *statuses;
51};
52
53static int pex_msdos_open (struct pex_obj *, const char *, int);
54static int pex_msdos_open (struct pex_obj *, const char *, int);
55static int pex_msdos_fdindex (struct pex_msdos *, int);
56static pid_t pex_msdos_exec_child (struct pex_obj *, int, const char *,
57				  char * const *, char * const *,
58				  int, int, int, int,
59				  int, const char **, int *);
60static int pex_msdos_close (struct pex_obj *, int);
61static pid_t pex_msdos_wait (struct pex_obj *, pid_t, 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 pid_t
155pex_msdos_exec_child (struct pex_obj *obj, int flags, const char *executable,
156		      char * const * argv, char * const * env, int in, int out,
157		      int toclose ATTRIBUTE_UNUSED,
158		      int errdes ATTRIBUTE_UNUSED, const char **errmsg,
159		      int *err)
160{
161  struct pex_msdos *ms;
162  char *temp_base;
163  int temp_base_allocated;
164  char *rf;
165  int inindex;
166  char *infile;
167  int outindex;
168  char *outfile;
169  char *scmd;
170  FILE *argfile;
171  int i;
172  int status;
173
174  ms = (struct pex_msdos *) obj->sysdep;
175
176  /* FIXME: I don't know how to redirect stderr, so we ignore ERRDES
177     and PEX_STDERR_TO_STDOUT.  */
178
179  temp_base = obj->temp_base;
180  if (temp_base != NULL)
181    temp_base_allocated = 0;
182  else
183    {
184      temp_base = choose_temp_base ();
185      temp_base_allocated = 1;
186    }
187
188  rf = concat (temp_base, ".gp", NULL);
189
190  if (temp_base_allocated)
191    free (temp_base);
192
193  if (in == STDIN_FILE_NO)
194    {
195      inindex = -1;
196      infile = "";
197    }
198  else
199    {
200      inindex = pex_msdos_fdindex (ms, in);
201      infile = ms->files[inindex];
202    }
203
204  if (out == STDOUT_FILE_NO)
205    {
206      outindex = -1;
207      outfile = "";
208    }
209  else
210    {
211      outindex = pex_msdos_fdindex (ms, out);
212      outfile = ms->files[outindex];
213    }
214
215  scmd = XNEWVEC (char, strlen (program)
216		  + ((flags & PEXECUTE_SEARCH) != 0 ? 4 : 0)
217		  + strlen (rf)
218		  + strlen (infile)
219		  + strlen (outfile)
220		  + 10);
221  sprintf (scmd, "%s%s @%s%s%s%s%s",
222	   program,
223	   (flags & PEXECUTE_SEARCH) != 0 ? ".exe" : "",
224	   rf,
225	   inindex != -1 ? " <" : "",
226	   infile,
227	   outindex != -1 ? " >" : "",
228	   outfile);
229
230  argfile = fopen (rf, "w");
231  if (argfile == NULL)
232    {
233      *err = errno;
234      free (scmd);
235      free (rf);
236      *errmsg = "cannot open temporary command file";
237      return (pid_t) -1;
238    }
239
240  for (i = 1; argv[i] != NULL; ++i)
241    {
242      char *p;
243
244      for (p = argv[i]; *p != '\0'; ++p)
245	{
246	  if (*p == '"' || *p == '\'' || *p == '\\' || ISSPACE (*p))
247	    putc ('\\', argfile);
248	  putc (*p, argfile);
249	}
250      putc ('\n', argfile);
251    }
252
253  fclose (argfile);
254
255  status = system (scmd);
256
257  if (status == -1)
258    {
259      *err = errno;
260      remove (rf);
261      free (scmd);
262      free (rf);
263      *errmsg = "system";
264      return (pid_t) -1;
265    }
266
267  remove (rf);
268  free (scmd);
269  free (rf);
270
271  /* Save the exit status for later.  When we are called, obj->count
272     is the number of children which have executed before this
273     one.  */
274  ms->statuses = XRESIZEVEC(int, ms->statuses, obj->count + 1);
275  ms->statuses[obj->count] = status;
276
277  return (pid_t) obj->count;
278}
279
280/* Wait for a child process to complete.  Actually the child process
281   has already completed, and we just need to return the exit
282   status.  */
283
284static pid_t
285pex_msdos_wait (struct pex_obj *obj, pid_t pid, int *status,
286		struct pex_time *time, int done ATTRIBUTE_UNUSED,
287		const char **errmsg ATTRIBUTE_UNUSED,
288		int *err ATTRIBUTE_UNUSED)
289{
290  struct pex_msdos *ms;
291
292  ms = (struct pex_msdos *) obj->sysdep;
293
294  if (time != NULL)
295    memset (time, 0, sizeof *time);
296
297  *status = ms->statuses[pid];
298
299  return 0;
300}
301
302/* Clean up the pex_msdos structure.  */
303
304static void
305pex_msdos_cleanup (struct pex_obj  *obj)
306{
307  struct pex_msdos *ms;
308  int i;
309
310  ms = (struct pex_msdos *) obj->sysdep;
311  for (i = 0; i < PEX_MSDOS_FILE_COUNT; ++i)
312    free (msdos->files[i]);
313  free (msdos->statuses);
314  free (msdos);
315  obj->sysdep = NULL;
316}
317