1139825Simp/****************************************************************************
286227Stmm *                                                                          *
3128776Stmm *                         GNAT RUN-TIME COMPONENTS                         *
4167308Smarius *                                                                          *
5128776Stmm *                            T E R M I N A L S                             *
686227Stmm *                                                                          *
786227Stmm *                          C Implementation File                           *
886227Stmm *                                                                          *
986227Stmm *                     Copyright (C) 2008-2015, AdaCore                     *
1086227Stmm *                                                                          *
1186227Stmm * GNAT is free software;  you can  redistribute it  and/or modify it under *
1286227Stmm * terms of the  GNU General Public License as published  by the Free Soft- *
1386227Stmm * ware  Foundation;  either version 3,  or (at your option) any later ver- *
1486227Stmm * sion.  GNAT is distributed in the hope that it will be useful, but WITH- *
1586227Stmm * OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY *
1686227Stmm * or FITNESS FOR A PARTICULAR PURPOSE.                                     *
1786227Stmm *                                                                          *
18128776Stmm * As a special exception under Section 7 of GPL version 3, you are granted *
1986227Stmm * additional permissions described in the GCC Runtime Library Exception,   *
2086227Stmm * version 3.1, as published by the Free Software Foundation.               *
2186227Stmm *                                                                          *
2286227Stmm * You should have received a copy of the GNU General Public License and    *
2386227Stmm * a copy of the GCC Runtime Library Exception along with this program;     *
2486227Stmm * see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    *
2586227Stmm * <http://www.gnu.org/licenses/>.                                          *
2686227Stmm *                                                                          *
2786227Stmm * GNAT was originally developed  by the GNAT team at  New York University. *
2886227Stmm * Extensive contributions were provided by Ada Core Technologies Inc.      *
2986227Stmm *                                                                          *
3086227Stmm ****************************************************************************/
3186227Stmm
3286227Stmm/* First all usupported platforms. Add stubs for exported routines. */
3386227Stmm
3486227Stmm#if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
35146474Smarius  || defined (__ANDROID__) || defined (__PikeOS__)
36146474Smarius
37146474Smarius#define ATTRIBUTE_UNUSED __attribute__((unused))
3886227Stmm
3986227Stmmvoid *
4086227Stmm__gnat_new_tty (void)
4186227Stmm{
4286227Stmm  return (void*)0;
43130068Sphk}
4486227Stmm
45167308Smariuschar *
46167308Smarius__gnat_tty_name (void* t ATTRIBUTE_UNUSED)
4786227Stmm{
4886227Stmm  return (char*)0;
4986227Stmm}
50167308Smarius
5186227Stmmint
52167308Smarius__gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED)
5386227Stmm{
54167308Smarius  return -1;
5586227Stmm}
5686227Stmm
5786227Stmmint
5886227Stmm__gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED)
5986227Stmm{
60133862Smarius  return -1;
6186227Stmm}
62128776Stmm
6386227Stmmint
6486227Stmm__gnat_setup_communication (void** desc ATTRIBUTE_UNUSED)
6586227Stmm{
6686227Stmm  return -1;
6786227Stmm}
68133862Smarius
6986227Stmmvoid
7086227Stmm__gnat_setup_parent_communication (void *d ATTRIBUTE_UNUSED,
7186227Stmm				   int *i ATTRIBUTE_UNUSED,
72167308Smarius				   int *o ATTRIBUTE_UNUSED,
73167308Smarius				   int *e ATTRIBUTE_UNUSED,
7486227Stmm				   int *p ATTRIBUTE_UNUSED)
7586227Stmm{
7688823Stmm}
7788823Stmm
7888823Stmmint
7988823Stmm__gnat_setup_child_communication (void *d ATTRIBUTE_UNUSED,
8088823Stmm				  char **n ATTRIBUTE_UNUSED,
81128776Stmm				  int u ATTRIBUTE_UNUSED)
82128776Stmm{
83167308Smarius  return -1;
84146474Smarius}
85128776Stmm
86128776Stmmint
87128776Stmm__gnat_terminate_process (void *desc ATTRIBUTE_UNUSED)
88128776Stmm{
89128776Stmm  return -1;
90128776Stmm}
91128776Stmm
92167308Smariusint
93167308Smarius__gnat_tty_fd (void* t ATTRIBUTE_UNUSED)
94167308Smarius{
9586227Stmm  return -1;
96178443Smarius}
97178443Smarius
98178443Smariusint
99185133Smarius__gnat_tty_supported (void)
100167308Smarius{
101167308Smarius  return 0;
102167308Smarius}
103167308Smarius
10486227Stmmint
10586227Stmm__gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED)
10686227Stmm{
107128776Stmm  return 1;
10886227Stmm}
10986227Stmm
11086227Stmmvoid
11186227Stmm__gnat_close_tty (void* t ATTRIBUTE_UNUSED)
11286227Stmm{
113167308Smarius}
114167308Smarius
115167308Smariusvoid
116167308Smarius__gnat_free_process (void** process ATTRIBUTE_UNUSED)
117167308Smarius{
118146474Smarius}
11986227Stmm
12086227Stmmvoid
12186227Stmm__gnat_reset_tty (void* t ATTRIBUTE_UNUSED)
12286227Stmm{
123167308Smarius}
124167308Smarius
125190099Smariusvoid
126190099Smarius__gnat_send_header (void* d ATTRIBUTE_UNUSED,
127190099Smarius		    char h[5] ATTRIBUTE_UNUSED,
128178443Smarius		    int s ATTRIBUTE_UNUSED,
129178443Smarius		    int *r ATTRIBUTE_UNUSED)
130178443Smarius{
131167308Smarius}
13286227Stmm
133167308Smariusvoid
134167308Smarius__gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED,
135167308Smarius		      int rows ATTRIBUTE_UNUSED,
136167308Smarius		      int columns ATTRIBUTE_UNUSED)
137167308Smarius{
138167308Smarius}
139167308Smarius
140167308Smarius/* For Windows platforms. */
141190099Smarius
14286227Stmm#elif defined(_WIN32)
14386227Stmm
14486227Stmm#include <errno.h>
14586227Stmm#include <stdio.h>
146167308Smarius#include <stdlib.h>
14786227Stmm
148200815Smarius#include <windows.h>
14986227Stmm
150185133Smarius#define MAXPATHLEN 1024
15190622Stmm
152167308Smarius#define NILP(x) ((x) == 0)
15390622Stmm#define Qnil 0
15490622Stmm#define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
15586227Stmm#define INTEGERP(x) 1
15690622Stmm#define XINT(x) x
15786227Stmm
15886227Stmmstruct TTY_Process {
159167308Smarius  int pid;           /* Number of this process */
16090622Stmm  PROCESS_INFORMATION procinfo;
16186227Stmm  HANDLE w_infd, w_outfd;
16286227Stmm  HANDLE w_forkin, w_forkout;
16386227Stmm  BOOL usePipe;
164185133Smarius};
16586227Stmm
16686227Stmm/* Control whether create_child cause the process to inherit GPS'
16786227Stmm   error mode setting.  The default is 1, to minimize the possibility of
16886227Stmm   subprocesses blocking when accessing unmounted drives.  */
16986227Stmmstatic int Vw32_start_process_inherit_error_mode = 1;
17086227Stmm
17186227Stmm/* Control whether spawnve quotes arguments as necessary to ensure
17286227Stmm   correct parsing by child process.  Because not all uses of spawnve
173185133Smarius   are careful about constructing argv arrays, we make this behaviour
17486227Stmm   conditional (off by default, since a similar operation is already done
17586227Stmm   in g-expect.adb by calling Normalize_Argument). */
17686227Stmmstatic int Vw32_quote_process_args = 0;
177167308Smarius
178167308Smariusstatic DWORD AbsoluteSeek(HANDLE, DWORD);
17986227Stmmstatic VOID  ReadBytes(HANDLE, LPVOID, DWORD);
18086227Stmm
18186227Stmm#define XFER_BUFFER_SIZE 2048
18286227Stmm
18386227Stmm/* This tell if the executable we're about to launch uses a GUI interface. */
18486227Stmm/* if we can't determine it, we will return true */
18586227Stmmstatic int
18686227Stmmis_gui_app (char *exe)
18786227Stmm{
18886227Stmm  HANDLE hImage;
18986227Stmm
19086227Stmm  DWORD  bytes;
19186227Stmm  DWORD  iSection;
192128776Stmm  DWORD  SectionOffset;
193128776Stmm  DWORD  CoffHeaderOffset;
194133862Smarius  DWORD  MoreDosHeader[16];
195128776Stmm  CHAR   *file;
196128776Stmm  size_t nlen;
197128776Stmm
198128776Stmm  ULONG  ntSignature;
199128776Stmm
200128776Stmm  IMAGE_DOS_HEADER      image_dos_header;
201167308Smarius  IMAGE_FILE_HEADER     image_file_header;
202167308Smarius  IMAGE_OPTIONAL_HEADER image_optional_header;
20386227Stmm  IMAGE_SECTION_HEADER  image_section_header;
204167308Smarius
20586227Stmm  /*
206167308Smarius   *  Open the reference file.
207167308Smarius  */
208167308Smarius  nlen = strlen (exe);
20986227Stmm  file = exe;
21088823Stmm  if (nlen > 2) {
21188823Stmm    if (exe[0] == '"') {
21288823Stmm      /* remove quotes */
21388823Stmm      nlen -= 2;
214167308Smarius      file = malloc ((nlen + 1) * sizeof (char));
21588823Stmm      memcpy (file, &exe[1], nlen);
21688823Stmm      file [nlen] = '\0';
21797265Sjake    }
218167308Smarius  }
219167308Smarius  hImage = CreateFile(file,
220146474Smarius                      GENERIC_READ,
221146474Smarius                      FILE_SHARE_READ,
222146474Smarius                      NULL,
223146474Smarius                      OPEN_EXISTING,
224146474Smarius                      FILE_ATTRIBUTE_NORMAL,
225146474Smarius                      NULL);
226146474Smarius
227146474Smarius  if (file != exe) {
228146474Smarius    free (file);
229167308Smarius  }
230167308Smarius
23186227Stmm  if (INVALID_HANDLE_VALUE == hImage)
232167308Smarius    {
233167308Smarius      report_file_error ("Could not open exe: ", Qnil);
234167308Smarius      report_file_error (exe, Qnil);
235167308Smarius      report_file_error ("\n", Qnil);
236167308Smarius      CloseHandle (hImage);
23786227Stmm      return -1;
23888823Stmm    }
239167308Smarius
24086227Stmm  /*
241128776Stmm   *  Read the MS-DOS image header.
24286227Stmm   */
24386227Stmm  ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
244146474Smarius
245146474Smarius  if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
246146474Smarius    {
247146474Smarius      report_file_error("Sorry, I do not understand this file.\n", Qnil);
248167308Smarius      CloseHandle (hImage);
249146474Smarius      return -1;
250146474Smarius    }
251146474Smarius
252146474Smarius  /*
253146474Smarius   *  Read more MS-DOS header.       */
254167308Smarius  ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
255167308Smarius   /*
256167308Smarius   *  Get actual COFF header.
257167308Smarius   */
258167308Smarius  CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
259146474Smarius                     sizeof(ULONG);
260146474Smarius  if (CoffHeaderOffset < 0) {
261146474Smarius    CloseHandle (hImage);
262146474Smarius    return -1;
263167308Smarius  }
264167308Smarius
26586227Stmm  ReadBytes (hImage, &ntSignature, sizeof(ULONG));
266167308Smarius
26786227Stmm  if (IMAGE_NT_SIGNATURE != ntSignature)
268167308Smarius    {
269167308Smarius      report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
270167308Smarius      CloseHandle (hImage);
271167308Smarius      return -1;
27286227Stmm    }
27386227Stmm
274167308Smarius  SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
275167308Smarius    IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
27686227Stmm
277167308Smarius  ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
27886227Stmm
279167308Smarius  /*
280167308Smarius   *  Read optional header.
281167308Smarius   */
282167308Smarius  ReadBytes(hImage,
283167308Smarius            &image_optional_header,
28486227Stmm            IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
28586227Stmm
28686227Stmm  CloseHandle (hImage);
28786227Stmm
288166901Spiso  switch (image_optional_header.Subsystem)
28986227Stmm    {
29086227Stmm    case IMAGE_SUBSYSTEM_UNKNOWN:
29188823Stmm        return 1;
29286227Stmm        break;
293167308Smarius
29486227Stmm    case IMAGE_SUBSYSTEM_NATIVE:
295131535Simp        return 1;
29686227Stmm        break;
29786227Stmm
298128776Stmm    case IMAGE_SUBSYSTEM_WINDOWS_GUI:
29986227Stmm        return 1;
30086227Stmm        break;
30186227Stmm
30286227Stmm    case IMAGE_SUBSYSTEM_WINDOWS_CUI:
303131535Simp        return 0;
304166901Spiso        break;
30586227Stmm
306167308Smarius    case IMAGE_SUBSYSTEM_OS2_CUI:
307167308Smarius        return 0;
308167308Smarius        break;
309167308Smarius
310167308Smarius    case IMAGE_SUBSYSTEM_POSIX_CUI:
31186227Stmm        return 0;
31286227Stmm        break;
31386227Stmm
31486227Stmm    default:
31586227Stmm        /* Unknown, return GUI app to be preservative: if yes, it will be
31686227Stmm           correctly launched, if no, it will be launched, and a console will
317146474Smarius           be also displayed, which is not a big deal */
318131535Simp        return 1;
31986227Stmm        break;
32086227Stmm    }
32186227Stmm
322178443Smarius}
323178443Smarius
324178443Smariusstatic DWORD
325178443SmariusAbsoluteSeek (HANDLE hFile, DWORD offset)
326178443Smarius{
327178443Smarius    DWORD newOffset;
328178443Smarius
329178443Smarius    newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
330178443Smarius
33186227Stmm    if (newOffset == 0xFFFFFFFF)
33286227Stmm      return -1;
33386227Stmm    else
33486227Stmm      return newOffset;
335167308Smarius}
336167308Smarius
337167308Smariusstatic VOID
338167308SmariusReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
339167308Smarius{
34086227Stmm  DWORD bytes;
341167308Smarius
342167308Smarius  if (!ReadFile(hFile, buffer, size, &bytes, NULL))
343167308Smarius    {
344167308Smarius      size = 0;
345167308Smarius      return;
34686227Stmm    }
347167308Smarius  else if (size != bytes)
348167308Smarius    {
349167308Smarius      return;
350167308Smarius    }
351167308Smarius}
352167308Smarius
353167308Smariusstatic int
354167308Smariusnt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
355167308Smarius{
356167308Smarius  STARTUPINFO start;
357167308Smarius  SECURITY_ATTRIBUTES sec_attrs;
358167308Smarius  SECURITY_DESCRIPTOR sec_desc;
359167308Smarius  DWORD flags;
360167308Smarius  char dir[ MAXPATHLEN ];
36186227Stmm  int pid;
36286227Stmm  int is_gui, use_cmd;
36388823Stmm  char *cmdline, *parg, **targ;
36486227Stmm  int do_quoting = 0;
36588823Stmm  char escape_char;
36688823Stmm  int arglen;
36788823Stmm
36886227Stmm  /* we have to do some conjuring here to put argv and envp into the
36986227Stmm     form CreateProcess wants...  argv needs to be a space separated/null
37086227Stmm     terminated list of parameters, and envp is a null
37186227Stmm     separated/double-null terminated list of parameters.
372167308Smarius
37386227Stmm     Additionally, zero-length args and args containing whitespace or
37486227Stmm     quote chars need to be wrapped in double quotes - for this to work,
37586227Stmm     embedded quotes need to be escaped as well.  The aim is to ensure
376157896Simp     the child process reconstructs the argv array we start with
37788823Stmm     exactly, so we treat quotes at the beginning and end of arguments
37888823Stmm     as embedded quotes.
37988823Stmm
38088823Stmm     Note that using backslash to escape embedded quotes requires
38186227Stmm     additional special handling if an embedded quote is already
38286227Stmm     preceded by backslash, or if an arg requiring quoting ends with
383128776Stmm     backslash.  In such cases, the run of escape characters needs to be
38486227Stmm     doubled.  For consistency, we apply this special handling as long
38586227Stmm     as the escape character is not quote.
38686227Stmm
38786227Stmm     Since we have no idea how large argv and envp are likely to be we
38888823Stmm     figure out list lengths on the fly and allocate them.  */
389167308Smarius
390167308Smarius  if (!NILP (Vw32_quote_process_args))
391167308Smarius    {
392167308Smarius      do_quoting = 1;
393167308Smarius      /* Override escape char by binding w32-quote-process-args to
394167308Smarius	 desired character, or use t for auto-selection.  */
395167308Smarius      if (INTEGERP (Vw32_quote_process_args))
39686227Stmm	escape_char = XINT (Vw32_quote_process_args);
39786227Stmm      else
39886227Stmm	escape_char = '\\';
39986227Stmm    }
40086227Stmm
40186227Stmm  /* do argv...  */
40286227Stmm  arglen = 0;
40386227Stmm  targ = argv;
40486227Stmm  while (*targ)
40586227Stmm    {
40686227Stmm      char *p = *targ;
40786227Stmm      int need_quotes = 0;
40886227Stmm      int escape_char_run = 0;
40986227Stmm
41086227Stmm      if (*p == 0)
41186227Stmm	need_quotes = 1;
41288823Stmm      for ( ; *p; p++)
41386227Stmm	{
41486227Stmm	  if (*p == '"')
41586227Stmm	    {
41686227Stmm	      /* allow for embedded quotes to be escaped */
41786227Stmm	      arglen++;
41886227Stmm	      need_quotes = 1;
419167308Smarius	      /* handle the case where the embedded quote is already escaped */
42086227Stmm	      if (escape_char_run > 0)
42186227Stmm		{
42286227Stmm		  /* To preserve the arg exactly, we need to double the
42386227Stmm		     preceding escape characters (plus adding one to
42486227Stmm		     escape the quote character itself).  */
42586227Stmm		  arglen += escape_char_run;
426128776Stmm		}
42786227Stmm	    }
42886227Stmm	  else if (*p == ' ' || *p == '\t')
42986227Stmm	    {
430167308Smarius	      need_quotes = 1;
431167308Smarius	    }
432167308Smarius
433167308Smarius	  if (*p == escape_char && escape_char != '"')
434167308Smarius	    escape_char_run++;
435167308Smarius	  else
436167308Smarius	    escape_char_run = 0;
437167308Smarius	}
438167308Smarius      if (need_quotes)
439167308Smarius	{
440167308Smarius	  arglen += 2;
441167308Smarius	  /* handle the case where the arg ends with an escape char - we
442167308Smarius	     must not let the enclosing quote be escaped.  */
443167308Smarius	  if (escape_char_run > 0)
444167308Smarius	    arglen += escape_char_run;
445167308Smarius	}
446167308Smarius      arglen += strlen (*targ) + 1;
447167308Smarius      targ++;
448167308Smarius    }
449167308Smarius
450167308Smarius  is_gui = is_gui_app (argv[0]);
451167308Smarius  use_cmd = FALSE;
452167308Smarius
453167308Smarius  if (is_gui == -1) {
454167308Smarius    /* could not determine application type. Try launching with "cmd /c" */
455167308Smarius    is_gui = FALSE;
456167308Smarius    arglen += 7;
457167308Smarius    use_cmd = TRUE;
458167308Smarius  }
459167308Smarius
460167308Smarius  cmdline = (char*)malloc (arglen + 1);
461167308Smarius  targ = argv;
462167308Smarius  parg = cmdline;
463167308Smarius
464167308Smarius  if (use_cmd == TRUE) {
465167308Smarius    strcpy (parg, "cmd /c ");
466167308Smarius    parg += 7;
467167308Smarius  }
468167308Smarius
469167308Smarius  while (*targ)
470167308Smarius    {
471167308Smarius      char * p = *targ;
472167308Smarius      int need_quotes = 0;
473167308Smarius
474167308Smarius      if (*p == 0)
475167308Smarius	need_quotes = 1;
476167308Smarius
477167308Smarius      if (do_quoting)
478167308Smarius	{
479167308Smarius	  for ( ; *p; p++)
480167308Smarius	    if (*p == ' ' || *p == '\t' || *p == '"')
481167308Smarius	      need_quotes = 1;
482167308Smarius	}
483167308Smarius      if (need_quotes)
484167308Smarius	{
485167308Smarius	  int escape_char_run = 0;
486167308Smarius	  char * first;
487167308Smarius	  char * last;
488167308Smarius
489167308Smarius	  p = *targ;
490167308Smarius	  first = p;
491167308Smarius	  last = p + strlen (p) - 1;
492167308Smarius	  *parg++ = '"';
493167308Smarius	  for ( ; *p; p++)
494167308Smarius	    {
495167308Smarius	      if (*p == '"')
496167308Smarius		{
497167308Smarius		  /* double preceding escape chars if any */
498167308Smarius		  while (escape_char_run > 0)
499167308Smarius		    {
500167308Smarius		      *parg++ = escape_char;
501167308Smarius		      escape_char_run--;
502167308Smarius		    }
503167308Smarius		  /* escape all quote chars, even at beginning or end */
504167308Smarius		  *parg++ = escape_char;
505167308Smarius		}
506167308Smarius	      *parg++ = *p;
507167308Smarius
508167308Smarius	      if (*p == escape_char && escape_char != '"')
509167308Smarius		escape_char_run++;
510167308Smarius	      else
511167308Smarius		escape_char_run = 0;
512167308Smarius	    }
513167308Smarius	  /* double escape chars before enclosing quote */
514167308Smarius	  while (escape_char_run > 0)
515167308Smarius	    {
516167308Smarius	      *parg++ = escape_char;
517167308Smarius	      escape_char_run--;
518167308Smarius	    }
519167308Smarius	  *parg++ = '"';
520167308Smarius	}
521167308Smarius      else
522167308Smarius	{
523167308Smarius	  strcpy (parg, *targ);
524167308Smarius	  parg += strlen (*targ);
525167308Smarius	}
526167308Smarius      *parg++ = ' ';
527167308Smarius      targ++;
528167308Smarius    }
529167308Smarius  *--parg = '\0';
530167308Smarius
531167308Smarius  memset (&start, 0, sizeof (start));
532167308Smarius  start.cb = sizeof (start);
533167308Smarius
534167308Smarius  if (process->usePipe == TRUE) {
535167308Smarius    start.dwFlags = STARTF_USESTDHANDLES;
536167308Smarius    start.hStdInput = process->w_forkin;
537167308Smarius    start.hStdOutput = process->w_forkout;
538167308Smarius    /* child's stderr is always redirected to outfd */
539167308Smarius    start.hStdError = process->w_forkout;
540167308Smarius  } else {
541167308Smarius    start.dwFlags = STARTF_USESTDHANDLES;
542167308Smarius    /* We only need to redirect stderr/stdout here. Stdin will be forced to
543167308Smarius       the spawned process console by explaunch */
544    start.hStdInput = NULL;
545    start.hStdOutput = process->w_forkout;
546    start.hStdError = process->w_forkout;
547  }
548
549  /* Explicitly specify no security */
550  if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
551    goto EH_Fail;
552  if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
553    goto EH_Fail;
554  sec_attrs.nLength = sizeof (sec_attrs);
555  sec_attrs.lpSecurityDescriptor = &sec_desc;
556  sec_attrs.bInheritHandle = FALSE;
557
558  /* creating a new console allow easier close. Do not use
559     CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
560  flags = CREATE_NEW_CONSOLE;
561  if (NILP (Vw32_start_process_inherit_error_mode))
562    flags |= CREATE_DEFAULT_ERROR_MODE;
563
564  /* if app is not a gui application, hide the console */
565  if (is_gui == FALSE) {
566    start.dwFlags |= STARTF_USESHOWWINDOW;
567    start.wShowWindow = SW_HIDE;
568  }
569
570  /* Set initial directory to null character to use current directory */
571  if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
572		      flags, env, NULL, &start, &process->procinfo))
573    goto EH_Fail;
574
575  pid = (int) process->procinfo.hProcess;
576  process->pid=pid;
577
578  return pid;
579
580 EH_Fail:
581  return -1;
582}
583
584/*************************
585 ** __gnat_send_header ()
586 *************************/
587
588#define EXP_SLAVE_CREATE 'c'
589#define EXP_SLAVE_KEY    'k'
590#define EXP_SLAVE_MOUSE  'm'
591#define EXP_SLAVE_WRITE  'w'
592#define EXP_SLAVE_KILL   'x'
593
594#define EXP_KILL_TERMINATE  0x1
595#define EXP_KILL_CTRL_C     0x2
596#define EXP_KILL_CTRL_BREAK 0x4
597
598void
599__gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
600{
601  if (p->usePipe == FALSE) {
602    header[0] = EXP_SLAVE_WRITE;
603    header[1] = size & 0xff;
604    header[2] = (size & 0xff00) >> 8;
605    header[3] = (size & 0xff0000) >> 16;
606    header[4] = (size & 0xff000000) >> 24;
607    *ret = 1;
608  } else {
609    *ret = 0;
610  }
611}
612
613/**********************************
614 **  __gnat_setup_communication ()
615 **********************************/
616
617int
618__gnat_setup_communication (struct TTY_Process** process_out) /* output param */
619{
620  struct TTY_Process* process;
621
622  process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
623  ZeroMemory (process, sizeof (struct TTY_Process));
624  *process_out = process;
625
626  return 0;
627}
628
629#define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
630
631int
632__gnat_setup_child_communication
633  (struct TTY_Process* process,
634   char** argv,
635   int Use_Pipes)
636{
637  int cpid;
638  HANDLE parent;
639  SECURITY_ATTRIBUTES sec_attrs;
640  char slavePath [MAX_PATH];
641  char **nargv;
642  int argc;
643  int i;
644  char pipeNameIn[100];
645  HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
646
647  parent = GetCurrentProcess ();
648
649  /* Set inheritance for the pipe handles */
650  sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
651  sec_attrs.bInheritHandle = TRUE;
652  sec_attrs.lpSecurityDescriptor = NULL;
653
654  if (Use_Pipes) {
655    /* Create in and out pipes */
656    if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
657      report_file_error ("Creation of child's IN handle", Qnil);
658    if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
659      report_file_error ("Creation of child's OUT handle", Qnil);
660
661    /* Do not inherit the parent's side of the pipes */
662    SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
663    SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
664
665    /* use native argv */
666    nargv = argv;
667    process->usePipe = TRUE;
668
669  } else {
670    static int pipeNameId = 0;
671
672    process->w_infd = NULL;
673
674    /* We create a named pipe for Input, as we handle input by sending special
675       commands to the explaunch process, that uses it to feed the actual input
676       of the process */
677    sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
678	    GetCurrentProcessId(), pipeNameId);
679    pipeNameId++;
680
681    hSlaveInDrv = CreateNamedPipe(pipeNameIn,
682				  PIPE_ACCESS_OUTBOUND,
683				  PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
684				  20000, NULL);
685    if (hSlaveInDrv == NULL)  goto end;
686
687    if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
688      report_file_error ("Creation of child's OUT handle", Qnil);
689
690    if (SearchPath (NULL, "explaunch.exe", NULL,
691                    MAX_PATH, slavePath, NULL) == 0) goto end;
692
693    for (argc=0; argv[argc] != NULL; argc++) ;
694    nargv = (char **) malloc (sizeof (char*) * (argc + 3));
695    nargv[0] = slavePath;
696    nargv[1] = pipeNameIn;
697
698    for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
699    process->usePipe = FALSE;
700  }
701
702  /* Spawn the child. */
703  cpid = nt_spawnve (nargv[0], nargv, NULL, process);
704
705  /* close the duplicated handles passed to the child */
706  CloseHandle (process->w_forkout);
707
708  if (process->usePipe == TRUE) {
709    CloseHandle (process->w_forkin);
710
711  } else {
712    UCHAR buf[8];		/* enough space for child status info */
713    DWORD count;
714    BOOL bRet;
715    DWORD dwRet;
716
717    /*
718     * Wait for connection with the slave driver
719     */
720    bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
721    if (bRet == FALSE) {
722      dwRet = GetLastError();
723      if (dwRet == ERROR_PIPE_CONNECTED) {
724	;
725      } else {
726	goto end;
727      }
728    }
729
730    process->w_infd = hSlaveInDrv;
731
732    /*
733     * wait for slave driver to initialize before allowing user to send to it
734     */
735    bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
736    if (bRet == FALSE) {
737      cpid = -1;
738    }
739
740    dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
741    if (dwRet != 0) {
742      cpid = -1;
743    }
744
745    cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
746    process->pid = cpid;
747  }
748
749  if (cpid == -1)
750    /* An error occurred while trying to spawn the process.  */
751    report_file_error ("Spawning child process", Qnil);
752
753  return cpid;
754 end:
755  if (hSlaveInDrv != NULL)
756    CloseHandle (hSlaveInDrv);
757  return -1;
758}
759
760void
761__gnat_setup_parent_communication
762  (struct TTY_Process* process,
763   int* in,
764   int* out,
765   int* err,
766   int* pid)
767{
768  *in = _open_osfhandle ((long) process->w_infd, 0);
769  *out = _open_osfhandle ((long) process->w_outfd, 0);
770  /* child's stderr is always redirected to outfd */
771  *err = *out;
772  *pid = process->pid;
773}
774
775typedef struct _child_process
776{
777  HWND                 hwnd;
778  PROCESS_INFORMATION *procinfo;
779} child_process;
780
781/* The major and minor versions of NT.  */
782static int w32_major_version;
783static int w32_minor_version;
784
785/* Distinguish between Windows NT and Windows 95.  */
786static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
787
788/* Cache information describing the NT system for later use.  */
789static void
790cache_system_info (void)
791{
792  union
793    {
794      struct info
795        {
796          char  major;
797          char  minor;
798          short platform;
799        } info;
800      DWORD data;
801    } version;
802
803  /* Cache the version of the operating system.  */
804  version.data = GetVersion ();
805  w32_major_version = version.info.major;
806  w32_minor_version = version.info.minor;
807
808  if (version.info.platform & 0x8000)
809    os_subtype = OS_WIN95;
810  else
811    os_subtype = OS_NT;
812}
813
814static BOOL CALLBACK
815find_child_console (HWND hwnd, child_process * cp)
816{
817  DWORD thread_id;
818  DWORD process_id;
819
820  thread_id = GetWindowThreadProcessId (hwnd, &process_id);
821  if (process_id == cp->procinfo->dwProcessId)
822    {
823      char window_class[32];
824
825      GetClassName (hwnd, window_class, sizeof (window_class));
826      if (strcmp (window_class,
827                  (os_subtype == OS_WIN95)
828                  ? "tty"
829                  : "ConsoleWindowClass") == 0)
830        {
831          cp->hwnd = hwnd;
832          return FALSE;
833        }
834    }
835  /* keep looking */
836  return TRUE;
837}
838
839int
840__gnat_interrupt_process (struct TTY_Process* p)
841{
842  char buf[2];
843  DWORD written;
844  BOOL bret;
845
846  if (p->usePipe == TRUE) {
847    bret = FALSE;
848  } else {
849    buf[0] = EXP_SLAVE_KILL;
850    buf[1] = EXP_KILL_CTRL_C;
851    bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
852  }
853
854  if (bret == FALSE) {
855    return __gnat_interrupt_pid (p->procinfo.dwProcessId);
856  }
857  return 0;
858}
859
860int
861__gnat_interrupt_pid (int pid)
862{
863  volatile child_process cp;
864  int rc = 0;
865
866  cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
867  cp.procinfo->dwProcessId = pid;
868
869  if (os_subtype == OS_UNKNOWN)
870    cache_system_info ();
871
872  /* Try to locate console window for process. */
873  EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
874
875  if (cp.hwnd)
876    {
877      BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
878      /* Retrieve Ctrl-C scancode */
879      BYTE vk_break_code = 'C';
880      BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
881      HWND foreground_window;
882
883      foreground_window = GetForegroundWindow ();
884      if (foreground_window)
885        {
886          /* NT 5.0, and apparently also Windows 98, will not allow
887             a Window to be set to foreground directly without the
888             user's involvement. The workaround is to attach
889             ourselves to the thread that owns the foreground
890             window, since that is the only thread that can set the
891             foreground window.  */
892          DWORD foreground_thread, child_thread;
893
894          foreground_thread =
895            GetWindowThreadProcessId (foreground_window, NULL);
896          if (foreground_thread == GetCurrentThreadId ()
897              || !AttachThreadInput (GetCurrentThreadId (),
898                                     foreground_thread, TRUE))
899            foreground_thread = 0;
900
901          child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
902          if (child_thread == GetCurrentThreadId ()
903              || !AttachThreadInput (GetCurrentThreadId (),
904                                     child_thread, TRUE))
905            child_thread = 0;
906
907          /* Set the foreground window to the child.  */
908          if (SetForegroundWindow (cp.hwnd))
909            {
910              /* Generate keystrokes as if user had typed Ctrl-Break or
911                 Ctrl-C.  */
912              keybd_event (VK_CONTROL, control_scan_code, 0, 0);
913              keybd_event (vk_break_code, break_scan_code,
914                (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
915              keybd_event (vk_break_code, break_scan_code,
916                (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
917                 | KEYEVENTF_KEYUP, 0);
918              keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
919
920              /* Sleep for a bit to give time for the main frame to respond
921              to focus change events.  */
922              Sleep (100);
923
924              SetForegroundWindow (foreground_window);
925            }
926          /* Detach from the foreground and child threads now that
927             the foreground switching is over.  */
928          if (foreground_thread)
929	    AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
930	  if (child_thread)
931            AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
932        }
933    }
934  /* Ctrl-Break is NT equivalent of SIGINT.  */
935  else if (!GenerateConsoleCtrlEvent
936             (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
937    {
938      errno = EINVAL;
939      rc = -1;
940    }
941
942  free (cp.procinfo);
943  return rc;
944}
945
946/* kill a process, as this implementation use CreateProcess on Win32 we need
947   to use Win32 TerminateProcess API */
948int
949__gnat_terminate_process (struct TTY_Process* p)
950{
951  char buf[2];
952  DWORD written;
953  BOOL bret;
954
955  if (p->usePipe == TRUE) {
956    bret = FALSE;
957  } else {
958    buf[0] = EXP_SLAVE_KILL;
959    buf[1] = EXP_KILL_TERMINATE;
960    bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
961  }
962
963  if (bret == FALSE) {
964    if (!TerminateProcess (p->procinfo.hProcess, 1))
965      return -1;
966    else
967      return 0;
968  } else
969    return 0;
970}
971
972/* wait for process pid to terminate and return the process status. This
973   implementation is different from the adaint.c one for Windows as it uses
974   the Win32 API instead of the C one. */
975
976int
977__gnat_tty_waitpid (struct TTY_Process* p)
978{
979  DWORD exitcode;
980  DWORD res;
981  HANDLE proc_hand = p->procinfo.hProcess;
982
983  res = WaitForSingleObject (proc_hand, 0);
984  GetExitCodeProcess (proc_hand, &exitcode);
985
986  CloseHandle (p->procinfo.hThread);
987  CloseHandle (p->procinfo.hProcess);
988
989  /* No need to close the handles: they were closed on the ada side */
990
991  return (int) exitcode;
992}
993
994/********************************
995 **  __gnat_free_process ()
996 ********************************/
997
998void
999__gnat_free_process (struct TTY_Process** process)
1000{
1001  free (*process);
1002  *process = NULL;
1003}
1004
1005/* TTY handling */
1006
1007typedef struct {
1008  int tty_fd;        /* descriptor for the tty */
1009  char tty_name[24]; /* Name of TTY device */
1010} TTY_Handle;
1011
1012int
1013__gnat_tty_supported (void)
1014{
1015  return 0;
1016}
1017
1018/* Return the tty name associated with p */
1019
1020char *
1021__gnat_tty_name (TTY_Handle* t)
1022{
1023  return t->tty_name;
1024}
1025
1026int
1027__gnat_tty_fd (TTY_Handle* t)
1028{
1029  return t->tty_fd;
1030}
1031
1032TTY_Handle*
1033__gnat_new_tty (void)
1034{
1035  return (TTY_Handle*)0;
1036}
1037
1038void
1039__gnat_reset_tty (TTY_Handle* t)
1040{
1041  return;
1042}
1043
1044void
1045__gnat_close_tty (TTY_Handle* t)
1046{
1047  free (t);
1048}
1049
1050void
1051__gnat_setup_winsize (void *desc, int rows, int columns)
1052{
1053}
1054
1055#else /* defined(_WIN32, implementatin for all UNIXes */
1056
1057/* First defined some macro to identify easily some systems */
1058#if defined (__FreeBSD__) \
1059 || defined (__OpenBSD__) \
1060 || defined (__NetBSD__)  \
1061 || defined (__DragonFly__)
1062#   define FREEBSD
1063#endif
1064
1065/* Include every system header we need */
1066#define _GNU_SOURCE
1067#include <errno.h>
1068#include <stdio.h>
1069#include <stdlib.h>
1070
1071/* On some system termio is either absent or including it will disable termios
1072   (HP-UX) */
1073#if ! defined (__hpux__) && ! defined (FREEBSD) && \
1074    ! defined (__APPLE__) && ! defined(__rtems__)
1075#   include <termio.h>
1076#endif
1077
1078#include <sys/ioctl.h>
1079#include <termios.h>
1080#include <fcntl.h>
1081#include <string.h>
1082#include <sys/stat.h>
1083#include <sys/types.h>
1084#include <sys/wait.h>
1085#include <unistd.h>
1086#if defined (sun)
1087#   include <sys/stropts.h>
1088#endif
1089#if defined (FREEBSD) || defined (sun)
1090#   include <sys/signal.h>
1091#endif
1092#if defined (__hpux__)
1093#   include <sys/termio.h>
1094#   include <sys/stropts.h>
1095#endif
1096
1097#define CDISABLE _POSIX_VDISABLE
1098
1099/* On HP-UX and Sun system, there is a bzero function but with a different
1100   signature. Use memset instead */
1101#if defined (__hpux__) || defined (sun) || defined (_AIX)
1102#   define bzero(s,n) memset (s,0,n)
1103#endif
1104
1105/* POSIX does not specify how to open the master side of a terminal.Several
1106   methods are available (system specific):
1107      1- using a cloning device (USE_CLONE_DEVICE)
1108      2- getpt                  (USE_GETPT)
1109      3- openpty                (USE_OPENPTY)
1110
1111   When using the cloning device method, the macro USE_CLONE_DEVICE should
1112   contains a full path to the adequate device.
1113
1114   When a new system is about to be supported, one of the previous macro should
1115   be set otherwise allocate_pty_desc will return an error
1116*/
1117
1118/* Configurable part */
1119#if defined (__APPLE__) || defined (FREEBSD)
1120#define USE_OPENPTY
1121#elif defined (linux)
1122#define USE_GETPT
1123#elif defined (sun)
1124#define USE_CLONE_DEVICE "/dev/ptmx"
1125#elif defined (_AIX)
1126#define USE_CLONE_DEVICE "/dev/ptc"
1127#elif defined (__hpux__)
1128/* On HP-UX we use the streamed version. Using the non streamed version is not
1129   recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1130   issues to detect process terminations. */
1131#define USE_CLONE_DEVICE "/dev/ptmx"
1132#endif
1133
1134/* structure that holds information about the terminal used and the process
1135   connected on the slave side */
1136typedef struct pty_desc_struct {
1137   int  master_fd;     /* fd of the master side if the terminal */
1138   int  slave_fd;      /* fd of the slave side */
1139   char slave_name[32];   /* filename of the slave side */
1140   int  child_pid;     /* PID of the child process connected to the slave side
1141                         of the terminal */
1142} pty_desc;
1143
1144/* allocate_pty_desc - allocate a pseudo terminal
1145 *
1146 * PARAMETERS
1147 *   out desc  returned pointer to a pty_desc structure containing information
1148 *             about the opened pseudo terminal
1149 * RETURN VALUE
1150 *   -1        if failed
1151 *    0        if ok
1152 * COMMENTS
1153 *   If the function is successful we should have at least the master side fd
1154 *   and the slave side filename. On some system, the slave side will also be
1155 *   opened. If this is not the case the slave side will be open once we are in
1156 *   the child process (note that opening the slave side at this stage will
1157 *   failed...).
1158 */
1159
1160extern char* ptsname (int);
1161
1162static int
1163allocate_pty_desc (pty_desc **desc) {
1164
1165   pty_desc *result;
1166   int  status      =  0;
1167   int  slave_fd    = -1;
1168   int  master_fd   = -1;
1169   char *slave_name = NULL;
1170
1171#ifdef USE_GETPT
1172  master_fd = getpt ();
1173#elif defined (USE_OPENPTY)
1174  status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1175#elif defined (USE_CLONE_DEVICE)
1176  master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1177#else
1178  printf ("[error]: terminal support is not configured\n");
1179  return -1;
1180#endif
1181
1182  /* at this stage we should have the master side fd and status should be 0 */
1183  if (status != 0 || master_fd < 0)
1184    {
1185      /* If this is not the case close all opened files and return -1 */
1186      printf ("[error]: cannot allocate master side of the pty\n");
1187      if (master_fd >= 0) close (master_fd);
1188      if (slave_fd  >= 0) close (slave_fd);
1189      *desc = NULL;
1190      return -1;
1191    }
1192
1193  /* retrieve the file name of the slave side if necessary */
1194  if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1195
1196  /* Now we should have slave file name */
1197  if (slave_name == NULL)
1198    {
1199      /* If not the case close any opened file and return - 1 */
1200      printf ("[error]: cannot allocate slave side of the pty\n");
1201      if (master_fd >= 0) close (master_fd);
1202      if (slave_fd  >= 0) close (slave_fd);
1203      *desc = NULL;
1204      return -1;
1205    }
1206
1207#if !defined(__rtems__)
1208  /* grant access to the slave side */
1209  grantpt (master_fd);
1210  /* unlock the terminal */
1211  unlockpt (master_fd);
1212#endif
1213
1214  /* set desc and return 0 */
1215  result = malloc (sizeof (pty_desc));
1216  result->master_fd  = master_fd;
1217  result->slave_fd   = slave_fd;
1218  /* the string returned by ptsname or _getpty is a static allocated string. So
1219     we should make a copy */
1220  strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1221  result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1222  result->child_pid  = -1;
1223  *desc=result;
1224  return 0;
1225}
1226
1227/* some utility macro that make the code of child_setup_tty easier to read */
1228#define __enable(a, b) ((a) |= (b))
1229#define __disable(a, b) ((a) &= ~(b))
1230
1231/* some properties do not exist on all systems. Set their value to 0 in that
1232   case */
1233#ifndef IUCLC
1234#define IUCLC 0
1235#endif
1236#ifndef OLCUC
1237#define OLCUC 0
1238#endif
1239#ifndef NLDLY
1240#define NLDLY 0
1241#define CRDLY 0
1242#define TABDLY 0
1243#define BSDLY 0
1244#define VTDLY 0
1245#define FFDLY 0
1246#endif
1247
1248/* child_setup_tty - set terminal properties
1249 *
1250 * PARAMETERS
1251 *   file descriptor of the slave side of the terminal
1252 *
1253 * RETURN VALUE
1254 *   0 if success, any other value if failed.
1255 *
1256 * COMMENTS
1257 *   None
1258 */
1259static int
1260child_setup_tty (int fd)
1261{
1262  struct termios s;
1263  int    status;
1264
1265  /* ensure that s is filled with 0 */
1266  bzero (&s, sizeof (s));
1267
1268  /* Get the current terminal settings */
1269  status = tcgetattr (fd, &s);
1270  if (status != 0) return -1;
1271
1272  /* Adjust input modes */
1273  __disable (s.c_iflag, IUCLC);    /* don't transform to lower case */
1274  __disable (s.c_iflag, ISTRIP);   /* don't delete 8th bit */
1275
1276  /* Adjust output modes */
1277  __enable  (s.c_oflag, OPOST);    /* enable postprocessing */
1278  __disable (s.c_oflag, ONLCR);    /* don't map LF to CR-LF */
1279  __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1280                                   /* disable delays */
1281  __disable (s.c_oflag, OLCUC);    /* don't transform to upper case */
1282
1283  /* Adjust control modes */
1284  s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1285
1286  /* Adjust local modes */
1287  __disable (s.c_lflag, ECHO);     /* disable echo */
1288  __enable  (s.c_lflag, ISIG);     /* enable signals */
1289  __enable  (s.c_lflag, ICANON);   /* erase/kill/eof processing */
1290
1291  /* Adjust control characters */
1292  /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1293     otherwise send_signal_via_characters will fail */
1294  s.c_cc[VEOF]   = 04;         /* insure that EOF is Control-D */
1295  s.c_cc[VERASE] = CDISABLE;   /* disable erase processing */
1296  s.c_cc[VKILL]  = CDISABLE;   /* disable kill processing */
1297  s.c_cc[VQUIT]  = 28;         /* Control-\ */
1298  s.c_cc[VINTR]  = 03;         /* Control-C */
1299  s.c_cc[VEOL]   = CDISABLE;
1300  s.c_cc[VSUSP]  = 26;         /* Control-Z */
1301
1302  /* push our changes */
1303  status = tcsetattr (fd, TCSADRAIN, &s);
1304  return status;
1305}
1306
1307/* __gnat_setup_communication - interface to the external world. Should be
1308 * called before forking. On Unixes this function only call allocate_pty_desc.
1309 * The Windows implementation (in different part of this file) is very
1310 * different.
1311 *
1312 * PARAMETERS
1313 *  out desc   returned pointer to a pty_desc structure
1314 * RETURN VALUE
1315 *  0 if success, -1 otherwise
1316 */
1317int __gnat_setup_communication (pty_desc** desc) {
1318  return allocate_pty_desc (desc);
1319}
1320
1321/* __gnat_setup_parent_communication - interface to the external world. Should
1322 * be called after forking in the parent process
1323 *
1324 * PARAMETERS
1325 *   out in_fd
1326     out out_fd
1327     out err_fd fds corresponding to the parent side of the
1328                terminal
1329     in pid_out child process pid
1330 * RETRUN VALUE
1331 *  0
1332 */
1333void
1334__gnat_setup_parent_communication
1335  (pty_desc *desc,
1336   int*     in_fd,  /* input */
1337   int*     out_fd, /* output */
1338   int*     err_fd, /* error */
1339   int*     pid_out)
1340{
1341
1342  *in_fd = desc->master_fd;
1343  *out_fd= desc->master_fd;
1344  *err_fd= desc->master_fd;
1345  desc->child_pid = *pid_out;
1346}
1347
1348/* __gnat_setup_winsize - Sets up the size of the terminal
1349 * This lets the process know the size of the terminal
1350 */
1351
1352void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1353#ifdef TIOCGWINSZ
1354  struct winsize s;
1355  s.ws_row = (unsigned short)rows;
1356  s.ws_col = (unsigned short)columns;
1357  s.ws_xpixel = 0;
1358  s.ws_ypixel = 0;
1359  ioctl (desc->master_fd, TIOCSWINSZ, &s);
1360#ifdef SIGWINCH
1361  if (desc->child_pid > 0) {
1362     /* Let the process know about the change in size */
1363     kill (desc->child_pid, SIGWINCH);
1364  }
1365#endif
1366#endif
1367}
1368
1369/* __gnat_setup_child_communication - interface to external world. Should be
1370 * called after forking in the child process. On Unixes, this function
1371 * first adjust the line setting, set standard output, input and error and
1372 * then spawn the program.
1373 *
1374 * PARAMETERS
1375 *   desc      a pty_desc structure containing the pty parameters
1376 *   new_argv  argv of the program to be spawned
1377 * RETURN VALUE
1378 *   this function should not return
1379 */
1380int
1381__gnat_setup_child_communication
1382   (pty_desc *desc,
1383    char **new_argv,
1384    int Use_Pipes)
1385{
1386  int status;
1387  int pid = getpid ();
1388
1389  setsid ();
1390
1391  /* open the slave side of the terminal if necessary */
1392  if (desc->slave_fd == -1)
1393#if defined (_AIX)
1394    /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1395       then we might have some processes hanging on I/O system calls. Not sure
1396       we can do that for all platforms so do it only on AIX for the moment.
1397       On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1398       reading on the slave fd, in case there is no data available, if O_NDELAY
1399       is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1400       that interactive programs such as GDB prefer the O_NDELAY behavior.
1401       We chose O_NONBLOCK because it allows us to make the distinction
1402       between a true EOF and an EOF returned because there is no data
1403       available to be read.  */
1404    desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1405#else
1406    desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1407#endif
1408
1409#if defined (sun) || defined (__hpux__)
1410  /* On systems such as Solaris we are using stream. We need to push the right
1411     "modules" in order to get the expected terminal behaviors. Otherwise
1412     functionalities such as termios are not available.  */
1413  ioctl (desc->slave_fd, I_PUSH, "ptem");
1414  ioctl (desc->slave_fd, I_PUSH, "ldterm");
1415  ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1416#endif
1417
1418#ifdef TIOCSCTTY
1419  /* make the tty the controlling terminal */
1420  status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1421#endif
1422
1423  /* adjust tty settings */
1424  child_setup_tty (desc->slave_fd);
1425  __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1426
1427  /* stdin, stdout and stderr should be now our tty */
1428  dup2 (desc->slave_fd, 0);
1429  dup2 (desc->slave_fd, 1);
1430  dup2 (desc->slave_fd, 2);
1431  if (desc->slave_fd > 2) close (desc->slave_fd);
1432
1433  /* adjust process group settings */
1434  status = setpgid (pid, pid);
1435  status = tcsetpgrp (0, pid);
1436
1437  /* launch the program */
1438  execvp (new_argv[0], new_argv);
1439
1440  /* return the pid */
1441  return pid;
1442}
1443
1444/* send_signal_via_characters - Send a characters that will trigger a signal
1445 * in the child process.
1446 *
1447 * PARAMETERS
1448 *  desc  a pty_desc structure containing terminal information
1449 *  int   a signal number
1450 * RETURN VALUE
1451 *  None
1452 */
1453static void
1454send_signal_via_characters
1455  (pty_desc *desc,
1456   int signal_number)
1457{
1458  char ctrl_c         = 03;
1459  char ctrl_backslash = 28;
1460  char ctrl_Z         = 26;
1461
1462  switch (signal_number)
1463    {
1464      case SIGINT:
1465	write (desc->master_fd, &ctrl_c, 1); return;
1466      case SIGQUIT:
1467	write (desc->master_fd, &ctrl_backslash, 1); return;
1468      case SIGTSTP:
1469	write (desc->master_fd, &ctrl_Z, 1); return;
1470    }
1471}
1472
1473/* __gnat_interrupt_process - interrupt the child process
1474 *
1475 * PARAMETERS
1476 *   desc a pty_desc structure
1477 */
1478int
1479__gnat_interrupt_process (pty_desc *desc)
1480{
1481  send_signal_via_characters (desc, SIGINT);
1482  return 0;
1483}
1484
1485/* __gnat_interrupt_pid - interrupt a process group
1486 *
1487 * PARAMETERS
1488 *   pid  pid of the process to interrupt
1489 */
1490int
1491__gnat_interrupt_pid (int pid)
1492{
1493  kill (-pid, SIGINT);
1494  return 0;
1495}
1496
1497/* __gnat_terminate_process - kill a child process
1498 *
1499 * PARAMETERS
1500 *   desc pty_desc structure
1501 */
1502int __gnat_terminate_process (pty_desc *desc)
1503{
1504  return kill (desc->child_pid, SIGKILL);
1505}
1506
1507/* __gnat_tty_waitpid - wait for the child process to die
1508 *
1509 * PARAMETERS
1510 *   desc pty_desc structure
1511 * RETURN VALUE
1512 *   exit status of the child process
1513 */
1514int
1515__gnat_tty_waitpid (pty_desc *desc)
1516{
1517  int status = 0;
1518  waitpid (desc->child_pid, &status, 0);
1519  return WEXITSTATUS (status);
1520}
1521
1522/* __gnat_tty_supported - Are tty supported ?
1523 *
1524 * RETURN VALUE
1525 *   always 1 on Unix systems
1526 */
1527int
1528__gnat_tty_supported (void)
1529{
1530  return 1;
1531}
1532
1533/* __gnat_free_process - free a pty_desc structure
1534 *
1535 * PARAMETERS
1536 *   in out desc: a pty desc structure
1537 */
1538void
1539__gnat_free_process (pty_desc** desc)
1540{
1541  free (*desc);
1542  *desc = NULL;
1543}
1544
1545/* __gnat_send_header - dummy function. this interface is only used on Windows */
1546void
1547__gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1548{
1549  *ret = 0;
1550}
1551
1552/* __gnat_reset_tty - reset line setting
1553 *
1554 * PARAMETERS
1555 *   desc: a pty_desc structure
1556 */
1557void
1558__gnat_reset_tty (pty_desc* desc)
1559{
1560  child_setup_tty (desc->master_fd);
1561}
1562
1563/* __gnat_new_tty - allocate a new terminal
1564 *
1565 * RETURN VALUE
1566 *   a pty_desc structure
1567 */
1568pty_desc *
1569__gnat_new_tty (void)
1570{
1571  int status;
1572  pty_desc* desc;
1573  status = allocate_pty_desc (&desc);
1574  child_setup_tty (desc->master_fd);
1575  return desc;
1576}
1577
1578/* __gnat_close_tty - close a terminal
1579 *
1580 * PARAMETERS
1581 *   desc  a pty_desc strucure
1582 */
1583void __gnat_close_tty (pty_desc* desc)
1584{
1585  if (desc->master_fd >= 0) close (desc->master_fd);
1586  if (desc->slave_fd  >= 0) close (desc->slave_fd);
1587}
1588
1589/* __gnat_tty_name - return slave side device name
1590 *
1591 * PARAMETERS
1592 *   desc  a pty_desc strucure
1593 * RETURN VALUE
1594 *   a string
1595 */
1596char *
1597__gnat_tty_name (pty_desc* desc)
1598{
1599  return desc->slave_name;
1600}
1601
1602/* __gnat_tty_name - return master side fd
1603 *
1604 * PARAMETERS
1605 *   desc  a pty_desc strucure
1606 * RETURN VALUE
1607 *   a fd
1608 */
1609int
1610__gnat_tty_fd (pty_desc* desc)
1611{
1612  return desc->master_fd;
1613}
1614
1615#endif /* WIN32 */
1616