1/****************************************************************************
2 *                                                                          *
3 *                         GNAT RUN-TIME COMPONENTS                         *
4 *                                                                          *
5 *                            T E R M I N A L S                             *
6 *                                                                          *
7 *                          C Implementation File                           *
8 *                                                                          *
9 *                     Copyright (C) 2008-2015, AdaCore                     *
10 *                                                                          *
11 * GNAT is free software;  you can  redistribute it  and/or modify it under *
12 * terms of the  GNU General Public License as published  by the Free Soft- *
13 * ware  Foundation;  either version 3,  or (at your option) any later ver- *
14 * sion.  GNAT is distributed in the hope that it will be useful, but WITH- *
15 * OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY *
16 * or FITNESS FOR A PARTICULAR PURPOSE.                                     *
17 *                                                                          *
18 * As a special exception under Section 7 of GPL version 3, you are granted *
19 * additional permissions described in the GCC Runtime Library Exception,   *
20 * version 3.1, as published by the Free Software Foundation.               *
21 *                                                                          *
22 * You should have received a copy of the GNU General Public License and    *
23 * a copy of the GCC Runtime Library Exception along with this program;     *
24 * see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    *
25 * <http://www.gnu.org/licenses/>.                                          *
26 *                                                                          *
27 * GNAT was originally developed  by the GNAT team at  New York University. *
28 * Extensive contributions were provided by Ada Core Technologies Inc.      *
29 *                                                                          *
30 ****************************************************************************/
31
32/* First all usupported platforms. Add stubs for exported routines. */
33
34#if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
35  || defined (__ANDROID__) || defined (__PikeOS__)
36
37#define ATTRIBUTE_UNUSED __attribute__((unused))
38
39void *
40__gnat_new_tty (void)
41{
42  return (void*)0;
43}
44
45char *
46__gnat_tty_name (void* t ATTRIBUTE_UNUSED)
47{
48  return (char*)0;
49}
50
51int
52__gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED)
53{
54  return -1;
55}
56
57int
58__gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED)
59{
60  return -1;
61}
62
63int
64__gnat_setup_communication (void** desc ATTRIBUTE_UNUSED)
65{
66  return -1;
67}
68
69void
70__gnat_setup_parent_communication (void *d ATTRIBUTE_UNUSED,
71				   int *i ATTRIBUTE_UNUSED,
72				   int *o ATTRIBUTE_UNUSED,
73				   int *e ATTRIBUTE_UNUSED,
74				   int *p ATTRIBUTE_UNUSED)
75{
76}
77
78int
79__gnat_setup_child_communication (void *d ATTRIBUTE_UNUSED,
80				  char **n ATTRIBUTE_UNUSED,
81				  int u ATTRIBUTE_UNUSED)
82{
83  return -1;
84}
85
86int
87__gnat_terminate_process (void *desc ATTRIBUTE_UNUSED)
88{
89  return -1;
90}
91
92int
93__gnat_tty_fd (void* t ATTRIBUTE_UNUSED)
94{
95  return -1;
96}
97
98int
99__gnat_tty_supported (void)
100{
101  return 0;
102}
103
104int
105__gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED)
106{
107  return 1;
108}
109
110void
111__gnat_close_tty (void* t ATTRIBUTE_UNUSED)
112{
113}
114
115void
116__gnat_free_process (void** process ATTRIBUTE_UNUSED)
117{
118}
119
120void
121__gnat_reset_tty (void* t ATTRIBUTE_UNUSED)
122{
123}
124
125void
126__gnat_send_header (void* d ATTRIBUTE_UNUSED,
127		    char h[5] ATTRIBUTE_UNUSED,
128		    int s ATTRIBUTE_UNUSED,
129		    int *r ATTRIBUTE_UNUSED)
130{
131}
132
133void
134__gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED,
135		      int rows ATTRIBUTE_UNUSED,
136		      int columns ATTRIBUTE_UNUSED)
137{
138}
139
140/* For Windows platforms. */
141
142#elif defined(_WIN32)
143
144#include <errno.h>
145#include <stdio.h>
146#include <stdlib.h>
147
148#include <windows.h>
149
150#define MAXPATHLEN 1024
151
152#define NILP(x) ((x) == 0)
153#define Qnil 0
154#define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
155#define INTEGERP(x) 1
156#define XINT(x) x
157
158struct TTY_Process {
159  int pid;           /* Number of this process */
160  PROCESS_INFORMATION procinfo;
161  HANDLE w_infd, w_outfd;
162  HANDLE w_forkin, w_forkout;
163  BOOL usePipe;
164};
165
166/* Control whether create_child cause the process to inherit GPS'
167   error mode setting.  The default is 1, to minimize the possibility of
168   subprocesses blocking when accessing unmounted drives.  */
169static int Vw32_start_process_inherit_error_mode = 1;
170
171/* Control whether spawnve quotes arguments as necessary to ensure
172   correct parsing by child process.  Because not all uses of spawnve
173   are careful about constructing argv arrays, we make this behaviour
174   conditional (off by default, since a similar operation is already done
175   in g-expect.adb by calling Normalize_Argument). */
176static int Vw32_quote_process_args = 0;
177
178static DWORD AbsoluteSeek(HANDLE, DWORD);
179static VOID  ReadBytes(HANDLE, LPVOID, DWORD);
180
181#define XFER_BUFFER_SIZE 2048
182
183/* This tell if the executable we're about to launch uses a GUI interface. */
184/* if we can't determine it, we will return true */
185static int
186is_gui_app (char *exe)
187{
188  HANDLE hImage;
189
190  DWORD  bytes;
191  DWORD  iSection;
192  DWORD  SectionOffset;
193  DWORD  CoffHeaderOffset;
194  DWORD  MoreDosHeader[16];
195  CHAR   *file;
196  size_t nlen;
197
198  ULONG  ntSignature;
199
200  IMAGE_DOS_HEADER      image_dos_header;
201  IMAGE_FILE_HEADER     image_file_header;
202  IMAGE_OPTIONAL_HEADER image_optional_header;
203  IMAGE_SECTION_HEADER  image_section_header;
204
205  /*
206   *  Open the reference file.
207  */
208  nlen = strlen (exe);
209  file = exe;
210  if (nlen > 2) {
211    if (exe[0] == '"') {
212      /* remove quotes */
213      nlen -= 2;
214      file = malloc ((nlen + 1) * sizeof (char));
215      memcpy (file, &exe[1], nlen);
216      file [nlen] = '\0';
217    }
218  }
219  hImage = CreateFile(file,
220                      GENERIC_READ,
221                      FILE_SHARE_READ,
222                      NULL,
223                      OPEN_EXISTING,
224                      FILE_ATTRIBUTE_NORMAL,
225                      NULL);
226
227  if (file != exe) {
228    free (file);
229  }
230
231  if (INVALID_HANDLE_VALUE == hImage)
232    {
233      report_file_error ("Could not open exe: ", Qnil);
234      report_file_error (exe, Qnil);
235      report_file_error ("\n", Qnil);
236      CloseHandle (hImage);
237      return -1;
238    }
239
240  /*
241   *  Read the MS-DOS image header.
242   */
243  ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
244
245  if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
246    {
247      report_file_error("Sorry, I do not understand this file.\n", Qnil);
248      CloseHandle (hImage);
249      return -1;
250    }
251
252  /*
253   *  Read more MS-DOS header.       */
254  ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
255   /*
256   *  Get actual COFF header.
257   */
258  CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
259                     sizeof(ULONG);
260  if (CoffHeaderOffset < 0) {
261    CloseHandle (hImage);
262    return -1;
263  }
264
265  ReadBytes (hImage, &ntSignature, sizeof(ULONG));
266
267  if (IMAGE_NT_SIGNATURE != ntSignature)
268    {
269      report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
270      CloseHandle (hImage);
271      return -1;
272    }
273
274  SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
275    IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
276
277  ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
278
279  /*
280   *  Read optional header.
281   */
282  ReadBytes(hImage,
283            &image_optional_header,
284            IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
285
286  CloseHandle (hImage);
287
288  switch (image_optional_header.Subsystem)
289    {
290    case IMAGE_SUBSYSTEM_UNKNOWN:
291        return 1;
292        break;
293
294    case IMAGE_SUBSYSTEM_NATIVE:
295        return 1;
296        break;
297
298    case IMAGE_SUBSYSTEM_WINDOWS_GUI:
299        return 1;
300        break;
301
302    case IMAGE_SUBSYSTEM_WINDOWS_CUI:
303        return 0;
304        break;
305
306    case IMAGE_SUBSYSTEM_OS2_CUI:
307        return 0;
308        break;
309
310    case IMAGE_SUBSYSTEM_POSIX_CUI:
311        return 0;
312        break;
313
314    default:
315        /* Unknown, return GUI app to be preservative: if yes, it will be
316           correctly launched, if no, it will be launched, and a console will
317           be also displayed, which is not a big deal */
318        return 1;
319        break;
320    }
321
322}
323
324static DWORD
325AbsoluteSeek (HANDLE hFile, DWORD offset)
326{
327    DWORD newOffset;
328
329    newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
330
331    if (newOffset == 0xFFFFFFFF)
332      return -1;
333    else
334      return newOffset;
335}
336
337static VOID
338ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
339{
340  DWORD bytes;
341
342  if (!ReadFile(hFile, buffer, size, &bytes, NULL))
343    {
344      size = 0;
345      return;
346    }
347  else if (size != bytes)
348    {
349      return;
350    }
351}
352
353static int
354nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
355{
356  STARTUPINFO start;
357  SECURITY_ATTRIBUTES sec_attrs;
358  SECURITY_DESCRIPTOR sec_desc;
359  DWORD flags;
360  char dir[ MAXPATHLEN ];
361  int pid;
362  int is_gui, use_cmd;
363  char *cmdline, *parg, **targ;
364  int do_quoting = 0;
365  char escape_char;
366  int arglen;
367
368  /* we have to do some conjuring here to put argv and envp into the
369     form CreateProcess wants...  argv needs to be a space separated/null
370     terminated list of parameters, and envp is a null
371     separated/double-null terminated list of parameters.
372
373     Additionally, zero-length args and args containing whitespace or
374     quote chars need to be wrapped in double quotes - for this to work,
375     embedded quotes need to be escaped as well.  The aim is to ensure
376     the child process reconstructs the argv array we start with
377     exactly, so we treat quotes at the beginning and end of arguments
378     as embedded quotes.
379
380     Note that using backslash to escape embedded quotes requires
381     additional special handling if an embedded quote is already
382     preceded by backslash, or if an arg requiring quoting ends with
383     backslash.  In such cases, the run of escape characters needs to be
384     doubled.  For consistency, we apply this special handling as long
385     as the escape character is not quote.
386
387     Since we have no idea how large argv and envp are likely to be we
388     figure out list lengths on the fly and allocate them.  */
389
390  if (!NILP (Vw32_quote_process_args))
391    {
392      do_quoting = 1;
393      /* Override escape char by binding w32-quote-process-args to
394	 desired character, or use t for auto-selection.  */
395      if (INTEGERP (Vw32_quote_process_args))
396	escape_char = XINT (Vw32_quote_process_args);
397      else
398	escape_char = '\\';
399    }
400
401  /* do argv...  */
402  arglen = 0;
403  targ = argv;
404  while (*targ)
405    {
406      char *p = *targ;
407      int need_quotes = 0;
408      int escape_char_run = 0;
409
410      if (*p == 0)
411	need_quotes = 1;
412      for ( ; *p; p++)
413	{
414	  if (*p == '"')
415	    {
416	      /* allow for embedded quotes to be escaped */
417	      arglen++;
418	      need_quotes = 1;
419	      /* handle the case where the embedded quote is already escaped */
420	      if (escape_char_run > 0)
421		{
422		  /* To preserve the arg exactly, we need to double the
423		     preceding escape characters (plus adding one to
424		     escape the quote character itself).  */
425		  arglen += escape_char_run;
426		}
427	    }
428	  else if (*p == ' ' || *p == '\t')
429	    {
430	      need_quotes = 1;
431	    }
432
433	  if (*p == escape_char && escape_char != '"')
434	    escape_char_run++;
435	  else
436	    escape_char_run = 0;
437	}
438      if (need_quotes)
439	{
440	  arglen += 2;
441	  /* handle the case where the arg ends with an escape char - we
442	     must not let the enclosing quote be escaped.  */
443	  if (escape_char_run > 0)
444	    arglen += escape_char_run;
445	}
446      arglen += strlen (*targ) + 1;
447      targ++;
448    }
449
450  is_gui = is_gui_app (argv[0]);
451  use_cmd = FALSE;
452
453  if (is_gui == -1) {
454    /* could not determine application type. Try launching with "cmd /c" */
455    is_gui = FALSE;
456    arglen += 7;
457    use_cmd = TRUE;
458  }
459
460  cmdline = (char*)malloc (arglen + 1);
461  targ = argv;
462  parg = cmdline;
463
464  if (use_cmd == TRUE) {
465    strcpy (parg, "cmd /c ");
466    parg += 7;
467  }
468
469  while (*targ)
470    {
471      char * p = *targ;
472      int need_quotes = 0;
473
474      if (*p == 0)
475	need_quotes = 1;
476
477      if (do_quoting)
478	{
479	  for ( ; *p; p++)
480	    if (*p == ' ' || *p == '\t' || *p == '"')
481	      need_quotes = 1;
482	}
483      if (need_quotes)
484	{
485	  int escape_char_run = 0;
486	  char * first;
487	  char * last;
488
489	  p = *targ;
490	  first = p;
491	  last = p + strlen (p) - 1;
492	  *parg++ = '"';
493	  for ( ; *p; p++)
494	    {
495	      if (*p == '"')
496		{
497		  /* double preceding escape chars if any */
498		  while (escape_char_run > 0)
499		    {
500		      *parg++ = escape_char;
501		      escape_char_run--;
502		    }
503		  /* escape all quote chars, even at beginning or end */
504		  *parg++ = escape_char;
505		}
506	      *parg++ = *p;
507
508	      if (*p == escape_char && escape_char != '"')
509		escape_char_run++;
510	      else
511		escape_char_run = 0;
512	    }
513	  /* double escape chars before enclosing quote */
514	  while (escape_char_run > 0)
515	    {
516	      *parg++ = escape_char;
517	      escape_char_run--;
518	    }
519	  *parg++ = '"';
520	}
521      else
522	{
523	  strcpy (parg, *targ);
524	  parg += strlen (*targ);
525	}
526      *parg++ = ' ';
527      targ++;
528    }
529  *--parg = '\0';
530
531  memset (&start, 0, sizeof (start));
532  start.cb = sizeof (start);
533
534  if (process->usePipe == TRUE) {
535    start.dwFlags = STARTF_USESTDHANDLES;
536    start.hStdInput = process->w_forkin;
537    start.hStdOutput = process->w_forkout;
538    /* child's stderr is always redirected to outfd */
539    start.hStdError = process->w_forkout;
540  } else {
541    start.dwFlags = STARTF_USESTDHANDLES;
542    /* We only need to redirect stderr/stdout here. Stdin will be forced to
543       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