OSDN Git Service

2011-12-02 Thomas Quinot <quinot@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / terminals.c
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-2011, 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
36 void * __gnat_new_tty (void) { return (void*)0; }
37 char * __gnat_tty_name (void* t) { return (char*)0; }
38 int    __gnat_interrupt_pid (int pid) { return -1; }
39 int    __gnat_interrupt_process (void* desc) { return -1; }
40 int    __gnat_setup_communication (void** desc) { return -1; }
41 void   __gnat_setup_parent_communication
42          (void* d, int* i, int* o, int*e, int*p) { return -1; }
43 int    __gnat_setup_child_communication
44          (void* d, char **n, int u) { return -1; }
45 int    __gnat_terminate_process (void *desc) { return -1; }
46 int    __gnat_tty_fd (void* t) { return -1; }
47 int    __gnat_tty_supported (void) { return 0; }
48 int    __gnat_tty_waitpid (void *desc) { return 1; }
49 void   __gnat_close_tty (void* t) {}
50 void   __gnat_free_process (void** process) {}
51 void   __gnat_reset_tty (void* t) {}
52 void   __gnat_send_header (void* d, char h[5], int s, int *r) {}
53 void   __gnat_setup_winsize (void *desc, int rows, int columns) {}
54
55 /* For Windows platforms. */
56
57 #elif defined(_WIN32)
58
59 #include <errno.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62
63 #include <windows.h>
64
65 #define MAXPATHLEN 1024
66
67 #define NILP(x) ((x) == 0)
68 #define Qnil 0
69 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
70 #define INTEGERP(x) 1
71 #define XINT(x) x
72
73 struct TTY_Process {
74   int pid;           /* Number of this process */
75   PROCESS_INFORMATION procinfo;
76   HANDLE w_infd, w_outfd;
77   HANDLE w_forkin, w_forkout;
78   BOOL usePipe;
79 };
80
81 /* Control whether create_child cause the process to inherit GPS'
82    error mode setting.  The default is 1, to minimize the possibility of
83    subprocesses blocking when accessing unmounted drives.  */
84 static int Vw32_start_process_inherit_error_mode = 1;
85
86 /* Control whether spawnve quotes arguments as necessary to ensure
87    correct parsing by child process.  Because not all uses of spawnve
88    are careful about constructing argv arrays, we make this behaviour
89    conditional (off by default, since a similar operation is already done
90    in g-expect.adb by calling Normalize_Argument). */
91 static int Vw32_quote_process_args = 0;
92
93 static DWORD AbsoluteSeek(HANDLE, DWORD);
94 static VOID  ReadBytes(HANDLE, LPVOID, DWORD);
95
96 #define XFER_BUFFER_SIZE 2048
97
98 /* This tell if the executable we're about to launch uses a GUI interface. */
99 /* if we can't determine it, we will return true */
100 static int
101 is_gui_app (char *exe)
102 {
103   HANDLE hImage;
104
105   DWORD  bytes;
106   DWORD  iSection;
107   DWORD  SectionOffset;
108   DWORD  CoffHeaderOffset;
109   DWORD  MoreDosHeader[16];
110   CHAR   *file;
111   size_t nlen;
112
113   ULONG  ntSignature;
114
115   IMAGE_DOS_HEADER      image_dos_header;
116   IMAGE_FILE_HEADER     image_file_header;
117   IMAGE_OPTIONAL_HEADER image_optional_header;
118   IMAGE_SECTION_HEADER  image_section_header;
119
120   /*
121    *  Open the reference file.
122   */
123   nlen = strlen (exe);
124   file = exe;
125   if (nlen > 2) {
126     if (exe[0] == '"') {
127       /* remove quotes */
128       nlen -= 2;
129       file = malloc ((nlen + 1) * sizeof (char));
130       memcpy (file, &exe[1], nlen);
131       file [nlen] = '\0';
132     }
133   }
134   hImage = CreateFile(file,
135                       GENERIC_READ,
136                       FILE_SHARE_READ,
137                       NULL,
138                       OPEN_EXISTING,
139                       FILE_ATTRIBUTE_NORMAL,
140                       NULL);
141
142   if (file != exe) {
143     free (file);
144   }
145
146   if (INVALID_HANDLE_VALUE == hImage)
147     {
148       report_file_error ("Could not open exe: ", Qnil);
149       report_file_error (exe, Qnil);
150       report_file_error ("\n", Qnil);
151       CloseHandle (hImage);
152       return -1;
153     }
154
155   /*
156    *  Read the MS-DOS image header.
157    */
158   ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
159
160   if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
161     {
162       report_file_error("Sorry, I do not understand this file.\n", Qnil);
163       CloseHandle (hImage);
164       return -1;
165     }
166
167   /*
168    *  Read more MS-DOS header.       */
169   ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
170    /*
171    *  Get actual COFF header.
172    */
173   CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
174                      sizeof(ULONG);
175   if (CoffHeaderOffset < 0) {
176     CloseHandle (hImage);
177     return -1;
178   }
179
180   ReadBytes (hImage, &ntSignature, sizeof(ULONG));
181
182   if (IMAGE_NT_SIGNATURE != ntSignature)
183     {
184       report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
185       CloseHandle (hImage);
186       return -1;
187     }
188
189   SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
190     IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
191
192   ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
193
194   /*
195    *  Read optional header.
196    */
197   ReadBytes(hImage,
198             &image_optional_header,
199             IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
200
201   CloseHandle (hImage);
202
203   switch (image_optional_header.Subsystem)
204     {
205     case IMAGE_SUBSYSTEM_UNKNOWN:
206         return 1;
207         break;
208
209     case IMAGE_SUBSYSTEM_NATIVE:
210         return 1;
211         break;
212
213     case IMAGE_SUBSYSTEM_WINDOWS_GUI:
214         return 1;
215         break;
216
217     case IMAGE_SUBSYSTEM_WINDOWS_CUI:
218         return 0;
219         break;
220
221     case IMAGE_SUBSYSTEM_OS2_CUI:
222         return 0;
223         break;
224
225     case IMAGE_SUBSYSTEM_POSIX_CUI:
226         return 0;
227         break;
228
229     default:
230         /* Unknown, return GUI app to be preservative: if yes, it will be
231            correctly launched, if no, it will be launched, and a console will
232            be also displayed, which is not a big deal */
233         return 1;
234         break;
235     }
236
237 }
238
239 static DWORD
240 AbsoluteSeek (HANDLE hFile, DWORD offset)
241 {
242     DWORD newOffset;
243
244     newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
245
246     if (newOffset == 0xFFFFFFFF)
247       return -1;
248     else
249       return newOffset;
250 }
251
252 static VOID
253 ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
254 {
255   DWORD bytes;
256
257   if (!ReadFile(hFile, buffer, size, &bytes, NULL))
258     {
259       size = 0;
260       return;
261     }
262   else if (size != bytes)
263     {
264       return;
265     }
266 }
267
268 static int
269 nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
270 {
271   STARTUPINFO start;
272   SECURITY_ATTRIBUTES sec_attrs;
273   SECURITY_DESCRIPTOR sec_desc;
274   DWORD flags;
275   char dir[ MAXPATHLEN ];
276   int pid;
277   int is_gui, use_cmd;
278   char *cmdline, *parg, **targ;
279   int do_quoting = 0;
280   char escape_char;
281   int arglen;
282
283   /* we have to do some conjuring here to put argv and envp into the
284      form CreateProcess wants...  argv needs to be a space separated/null
285      terminated list of parameters, and envp is a null
286      separated/double-null terminated list of parameters.
287
288      Additionally, zero-length args and args containing whitespace or
289      quote chars need to be wrapped in double quotes - for this to work,
290      embedded quotes need to be escaped as well.  The aim is to ensure
291      the child process reconstructs the argv array we start with
292      exactly, so we treat quotes at the beginning and end of arguments
293      as embedded quotes.
294
295      Note that using backslash to escape embedded quotes requires
296      additional special handling if an embedded quote is already
297      preceeded by backslash, or if an arg requiring quoting ends with
298      backslash.  In such cases, the run of escape characters needs to be
299      doubled.  For consistency, we apply this special handling as long
300      as the escape character is not quote.
301
302      Since we have no idea how large argv and envp are likely to be we
303      figure out list lengths on the fly and allocate them.  */
304
305   if (!NILP (Vw32_quote_process_args))
306     {
307       do_quoting = 1;
308       /* Override escape char by binding w32-quote-process-args to
309          desired character, or use t for auto-selection.  */
310       if (INTEGERP (Vw32_quote_process_args))
311         escape_char = XINT (Vw32_quote_process_args);
312       else
313         escape_char = '\\';
314     }
315
316   /* do argv...  */
317   arglen = 0;
318   targ = argv;
319   while (*targ)
320     {
321       char *p = *targ;
322       int need_quotes = 0;
323       int escape_char_run = 0;
324
325       if (*p == 0)
326         need_quotes = 1;
327       for ( ; *p; p++)
328         {
329           if (*p == '"')
330             {
331               /* allow for embedded quotes to be escaped */
332               arglen++;
333               need_quotes = 1;
334               /* handle the case where the embedded quote is already escaped */
335               if (escape_char_run > 0)
336                 {
337                   /* To preserve the arg exactly, we need to double the
338                      preceding escape characters (plus adding one to
339                      escape the quote character itself).  */
340                   arglen += escape_char_run;
341                 }
342             }
343           else if (*p == ' ' || *p == '\t')
344             {
345               need_quotes = 1;
346             }
347
348           if (*p == escape_char && escape_char != '"')
349             escape_char_run++;
350           else
351             escape_char_run = 0;
352         }
353       if (need_quotes)
354         {
355           arglen += 2;
356           /* handle the case where the arg ends with an escape char - we
357              must not let the enclosing quote be escaped.  */
358           if (escape_char_run > 0)
359             arglen += escape_char_run;
360         }
361       arglen += strlen (*targ) + 1;
362       targ++;
363     }
364
365   is_gui = is_gui_app (argv[0]);
366   use_cmd = FALSE;
367
368   if (is_gui == -1) {
369     /* could not determine application type. Try launching with "cmd /c" */
370     is_gui = FALSE;
371     arglen += 7;
372     use_cmd = TRUE;
373   }
374
375   cmdline = (char*)malloc (arglen + 1);
376   targ = argv;
377   parg = cmdline;
378
379   if (use_cmd == TRUE) {
380     strcpy (parg, "cmd /c ");
381     parg += 7;
382   }
383
384   while (*targ)
385     {
386       char * p = *targ;
387       int need_quotes = 0;
388
389       if (*p == 0)
390         need_quotes = 1;
391
392       if (do_quoting)
393         {
394           for ( ; *p; p++)
395             if (*p == ' ' || *p == '\t' || *p == '"')
396               need_quotes = 1;
397         }
398       if (need_quotes)
399         {
400           int escape_char_run = 0;
401           char * first;
402           char * last;
403
404           p = *targ;
405           first = p;
406           last = p + strlen (p) - 1;
407           *parg++ = '"';
408           for ( ; *p; p++)
409             {
410               if (*p == '"')
411                 {
412                   /* double preceding escape chars if any */
413                   while (escape_char_run > 0)
414                     {
415                       *parg++ = escape_char;
416                       escape_char_run--;
417                     }
418                   /* escape all quote chars, even at beginning or end */
419                   *parg++ = escape_char;
420                 }
421               *parg++ = *p;
422
423               if (*p == escape_char && escape_char != '"')
424                 escape_char_run++;
425               else
426                 escape_char_run = 0;
427             }
428           /* double escape chars before enclosing quote */
429           while (escape_char_run > 0)
430             {
431               *parg++ = escape_char;
432               escape_char_run--;
433             }
434           *parg++ = '"';
435         }
436       else
437         {
438           strcpy (parg, *targ);
439           parg += strlen (*targ);
440         }
441       *parg++ = ' ';
442       targ++;
443     }
444   *--parg = '\0';
445
446   memset (&start, 0, sizeof (start));
447   start.cb = sizeof (start);
448
449   if (process->usePipe == TRUE) {
450     start.dwFlags = STARTF_USESTDHANDLES;
451     start.hStdInput = process->w_forkin;
452     start.hStdOutput = process->w_forkout;
453     /* child's stderr is always redirected to outfd */
454     start.hStdError = process->w_forkout;
455   } else {
456     start.dwFlags = STARTF_USESTDHANDLES;
457     /* We only need to redirect stderr/stdout here. Stdin will be forced to
458        the spawned process console by explaunch */
459     start.hStdInput = NULL;
460     start.hStdOutput = process->w_forkout;
461     start.hStdError = process->w_forkout;
462   }
463
464   /* Explicitly specify no security */
465   if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
466     goto EH_Fail;
467   if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
468     goto EH_Fail;
469   sec_attrs.nLength = sizeof (sec_attrs);
470   sec_attrs.lpSecurityDescriptor = &sec_desc;
471   sec_attrs.bInheritHandle = FALSE;
472
473   /* creating a new console allow easier close. Do not use
474      CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
475   flags = CREATE_NEW_CONSOLE;
476   if (NILP (Vw32_start_process_inherit_error_mode))
477     flags |= CREATE_DEFAULT_ERROR_MODE;
478
479   /* if app is not a gui application, hide the console */
480   if (is_gui == FALSE) {
481     start.dwFlags |= STARTF_USESHOWWINDOW;
482     start.wShowWindow = SW_HIDE;
483   }
484
485   /* Set initial directory to null character to use current directory */
486   if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
487                       flags, env, NULL, &start, &process->procinfo))
488     goto EH_Fail;
489
490   pid = (int) process->procinfo.hProcess;
491   process->pid=pid;
492
493   return pid;
494
495  EH_Fail:
496   return -1;
497 }
498
499 /*************************
500  ** __gnat_send_header ()
501  *************************/
502
503 #define EXP_SLAVE_CREATE 'c'
504 #define EXP_SLAVE_KEY    'k'
505 #define EXP_SLAVE_MOUSE  'm'
506 #define EXP_SLAVE_WRITE  'w'
507 #define EXP_SLAVE_KILL   'x'
508
509 #define EXP_KILL_TERMINATE  0x1
510 #define EXP_KILL_CTRL_C     0x2
511 #define EXP_KILL_CTRL_BREAK 0x4
512
513 void
514 __gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
515 {
516   if (p->usePipe == FALSE) {
517     header[0] = EXP_SLAVE_WRITE;
518     header[1] = size & 0xff;
519     header[2] = (size & 0xff00) >> 8;
520     header[3] = (size & 0xff0000) >> 16;
521     header[4] = (size & 0xff000000) >> 24;
522     *ret = 1;
523   } else {
524     *ret = 0;
525   }
526 }
527
528 /**********************************
529  **  __gnat_setup_communication ()
530  **********************************/
531
532 int
533 __gnat_setup_communication (struct TTY_Process** process_out) /* output param */
534 {
535   struct TTY_Process* process;
536
537   process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
538   ZeroMemory (process, sizeof (struct TTY_Process));
539   *process_out = process;
540
541   return 0;
542 }
543
544 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
545
546 int
547 __gnat_setup_child_communication
548   (struct TTY_Process* process,
549    char** argv,
550    int Use_Pipes)
551 {
552   int cpid;
553   HANDLE parent;
554   SECURITY_ATTRIBUTES sec_attrs;
555   char slavePath [MAX_PATH];
556   char **nargv;
557   int argc;
558   int i;
559   char pipeNameIn[100];
560   HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
561
562   parent = GetCurrentProcess ();
563
564   /* Set inheritance for the pipe handles */
565   sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
566   sec_attrs.bInheritHandle = TRUE;
567   sec_attrs.lpSecurityDescriptor = NULL;
568
569   if (Use_Pipes) {
570     /* Create in and out pipes */
571     if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
572       report_file_error ("Creation of child's IN handle", Qnil);
573     if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
574       report_file_error ("Creation of child's OUT handle", Qnil);
575
576     /* Do not inherit the parent's side of the pipes */
577     SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
578     SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
579
580     /* use native argv */
581     nargv = argv;
582     process->usePipe = TRUE;
583
584   } else {
585     static int pipeNameId = 0;
586
587     process->w_infd = NULL;
588
589     /* We create a named pipe for Input, as we handle input by sending special
590        commands to the explaunch process, that uses it to feed the actual input
591        of the process */
592     sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
593             GetCurrentProcessId(), pipeNameId);
594     pipeNameId++;
595
596     hSlaveInDrv = CreateNamedPipe(pipeNameIn,
597                                   PIPE_ACCESS_OUTBOUND,
598                                   PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
599                                   20000, NULL);
600     if (hSlaveInDrv == NULL)  goto end;
601
602     if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
603       report_file_error ("Creation of child's OUT handle", Qnil);
604
605     if (SearchPath (NULL, "explaunch.exe", NULL,
606                     MAX_PATH, slavePath, NULL) == 0) goto end;
607
608     for (argc=0; argv[argc] != NULL; argc++) ;
609     nargv = (char **) malloc (sizeof (char*) * (argc + 3));
610     nargv[0] = slavePath;
611     nargv[1] = pipeNameIn;
612
613     for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
614     process->usePipe = FALSE;
615   }
616
617   /* Spawn the child. */
618   cpid = nt_spawnve (nargv[0], nargv, NULL, process);
619
620   /* close the duplicated handles passed to the child */
621   CloseHandle (process->w_forkout);
622
623   if (process->usePipe == TRUE) {
624     CloseHandle (process->w_forkin);
625
626   } else {
627     UCHAR buf[8];               /* enough space for child status info */
628     DWORD count;
629     BOOL bRet;
630     DWORD dwRet;
631
632     /*
633      * Wait for connection with the slave driver
634      */
635     bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
636     if (bRet == FALSE) {
637       dwRet = GetLastError();
638       if (dwRet == ERROR_PIPE_CONNECTED) {
639         ;
640       } else {
641         goto end;
642       }
643     }
644
645     process->w_infd = hSlaveInDrv;
646
647     /*
648      * wait for slave driver to initialize before allowing user to send to it
649      */
650     bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
651     if (bRet == FALSE) {
652       cpid = -1;
653     }
654
655     dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
656     if (dwRet != 0) {
657       cpid = -1;
658     }
659
660     cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
661     process->pid = cpid;
662   }
663
664   if (cpid == -1)
665     /* An error occurred while trying to spawn the process.  */
666     report_file_error ("Spawning child process", Qnil);
667
668   return cpid;
669  end:
670   if (hSlaveInDrv != NULL)
671     CloseHandle (hSlaveInDrv);
672   return -1;
673 }
674
675 void
676 __gnat_setup_parent_communication
677   (struct TTY_Process* process,
678    int* in,
679    int* out,
680    int* err,
681    int* pid)
682 {
683   *in = _open_osfhandle ((long) process->w_infd, 0);
684   *out = _open_osfhandle ((long) process->w_outfd, 0);
685   /* child's stderr is always redirected to outfd */
686   *err = *out;
687   *pid = process->pid;
688 }
689
690 typedef struct _child_process
691 {
692   HWND                 hwnd;
693   PROCESS_INFORMATION *procinfo;
694 } child_process;
695
696 /* The major and minor versions of NT.  */
697 static int w32_major_version;
698 static int w32_minor_version;
699
700 /* Distinguish between Windows NT and Windows 95.  */
701 static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
702
703 /* Cache information describing the NT system for later use.  */
704 static void
705 cache_system_info (void)
706 {
707   union
708     {
709       struct info
710         {
711           char  major;
712           char  minor;
713           short platform;
714         } info;
715       DWORD data;
716     } version;
717
718   /* Cache the version of the operating system.  */
719   version.data = GetVersion ();
720   w32_major_version = version.info.major;
721   w32_minor_version = version.info.minor;
722
723   if (version.info.platform & 0x8000)
724     os_subtype = OS_WIN95;
725   else
726     os_subtype = OS_NT;
727 }
728
729 static BOOL CALLBACK
730 find_child_console (HWND hwnd, child_process * cp)
731 {
732   DWORD thread_id;
733   DWORD process_id;
734
735   thread_id = GetWindowThreadProcessId (hwnd, &process_id);
736   if (process_id == cp->procinfo->dwProcessId)
737     {
738       char window_class[32];
739
740       GetClassName (hwnd, window_class, sizeof (window_class));
741       if (strcmp (window_class,
742                   (os_subtype == OS_WIN95)
743                   ? "tty"
744                   : "ConsoleWindowClass") == 0)
745         {
746           cp->hwnd = hwnd;
747           return FALSE;
748         }
749     }
750   /* keep looking */
751   return TRUE;
752 }
753
754 int
755 __gnat_interrupt_process (struct TTY_Process* p)
756 {
757   char buf[2];
758   DWORD written;
759   BOOL bret;
760
761   if (p->usePipe == TRUE) {
762     bret = FALSE;
763   } else {
764     buf[0] = EXP_SLAVE_KILL;
765     buf[1] = EXP_KILL_CTRL_C;
766     bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
767   }
768
769   if (bret == FALSE) {
770     return __gnat_interrupt_pid (p->procinfo.dwProcessId);
771   }
772   return 0;
773 }
774
775 int
776 __gnat_interrupt_pid (int pid)
777 {
778   volatile child_process cp;
779   int rc = 0;
780
781   cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
782   cp.procinfo->dwProcessId = pid;
783
784   if (os_subtype == OS_UNKNOWN)
785     cache_system_info ();
786
787   /* Try to locate console window for process. */
788   EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
789
790   if (cp.hwnd)
791     {
792       BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
793       /* Retrieve Ctrl-C scancode */
794       BYTE vk_break_code = 'C';
795       BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
796       HWND foreground_window;
797
798       foreground_window = GetForegroundWindow ();
799       if (foreground_window)
800         {
801           /* NT 5.0, and apparently also Windows 98, will not allow
802              a Window to be set to foreground directly without the
803              user's involvement. The workaround is to attach
804              ourselves to the thread that owns the foreground
805              window, since that is the only thread that can set the
806              foreground window.  */
807           DWORD foreground_thread, child_thread;
808
809           foreground_thread =
810             GetWindowThreadProcessId (foreground_window, NULL);
811           if (foreground_thread == GetCurrentThreadId ()
812               || !AttachThreadInput (GetCurrentThreadId (),
813                                      foreground_thread, TRUE))
814             foreground_thread = 0;
815
816           child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
817           if (child_thread == GetCurrentThreadId ()
818               || !AttachThreadInput (GetCurrentThreadId (),
819                                      child_thread, TRUE))
820             child_thread = 0;
821
822           /* Set the foreground window to the child.  */
823           if (SetForegroundWindow (cp.hwnd))
824             {
825               /* Generate keystrokes as if user had typed Ctrl-Break or
826                  Ctrl-C.  */
827               keybd_event (VK_CONTROL, control_scan_code, 0, 0);
828               keybd_event (vk_break_code, break_scan_code,
829                 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
830               keybd_event (vk_break_code, break_scan_code,
831                 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
832                  | KEYEVENTF_KEYUP, 0);
833               keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
834
835               /* Sleep for a bit to give time for the main frame to respond
836               to focus change events.  */
837               Sleep (100);
838
839               SetForegroundWindow (foreground_window);
840             }
841           /* Detach from the foreground and child threads now that
842              the foreground switching is over.  */
843           if (foreground_thread)
844             AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
845           if (child_thread)
846             AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
847         }
848     }
849   /* Ctrl-Break is NT equivalent of SIGINT.  */
850   else if (!GenerateConsoleCtrlEvent
851              (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
852     {
853       errno = EINVAL;
854       rc = -1;
855     }
856
857   free (cp.procinfo);
858   return rc;
859 }
860
861 /* kill a process, as this implementation use CreateProcess on Win32 we need
862    to use Win32 TerminateProcess API */
863 int
864 __gnat_terminate_process (struct TTY_Process* p)
865 {
866   char buf[2];
867   DWORD written;
868   BOOL bret;
869
870   if (p->usePipe == TRUE) {
871     bret = FALSE;
872   } else {
873     buf[0] = EXP_SLAVE_KILL;
874     buf[1] = EXP_KILL_TERMINATE;
875     bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
876   }
877
878   if (bret == FALSE) {
879     if (!TerminateProcess (p->procinfo.hProcess, 1))
880       return -1;
881     else
882       return 0;
883   } else
884     return 0;
885 }
886
887 /* wait for process pid to terminate and return the process status. This
888    implementation is different from the adaint.c one for Windows as it uses
889    the Win32 API instead of the C one. */
890
891 int
892 __gnat_tty_waitpid (struct TTY_Process* p)
893 {
894   DWORD exitcode;
895   DWORD res;
896   HANDLE proc_hand = p->procinfo.hProcess;
897
898   res = WaitForSingleObject (proc_hand, 0);
899   GetExitCodeProcess (proc_hand, &exitcode);
900
901   CloseHandle (p->procinfo.hThread);
902   CloseHandle (p->procinfo.hProcess);
903
904   /* No need to close the handles: they were closed on the ada side */
905
906   return (int) exitcode;
907 }
908
909 /********************************
910  **  __gnat_free_process ()
911  ********************************/
912
913 void
914 __gnat_free_process (struct TTY_Process** process)
915 {
916   free (*process);
917   *process = NULL;
918 }
919
920 /* TTY handling */
921
922 typedef struct {
923   int tty_fd;        /* descriptor for the tty */
924   char tty_name[24]; /* Name of TTY device */
925 } TTY_Handle;
926
927 int
928 __gnat_tty_supported (void)
929 {
930   return 0;
931 }
932
933 /* Return the tty name associated with p */
934
935 char *
936 __gnat_tty_name (TTY_Handle* t)
937 {
938   return t->tty_name;
939 }
940
941 int
942 __gnat_tty_fd (TTY_Handle* t)
943 {
944   return t->tty_fd;
945 }
946
947 TTY_Handle*
948 __gnat_new_tty (void)
949 {
950   return (TTY_Handle*)0;
951 }
952
953 void
954 __gnat_reset_tty (TTY_Handle* t)
955 {
956   return;
957 }
958
959 void
960 __gnat_close_tty (TTY_Handle* t)
961 {
962   free (t);
963 }
964
965 void
966 __gnat_setup_winsize (void *desc, int rows, int columns)
967 {
968 }
969
970 #else /* defined(_WIN32, implementatin for all UNIXes */
971
972 /* First defined some macro to identify easily some systems */
973 #if defined (__FreeBSD__) \
974  || defined (__OpenBSD__) \
975  || defined (__NetBSD__)  \
976  || defined (__DragonFly__)
977 #   define FREEBSD
978 #endif
979 #if defined (__alpha__) && defined (__osf__)
980 #   define OSF1
981 #endif
982 #if defined (__mips) && defined (__sgi)
983 #   define IRIX
984 #endif
985
986 /* Include every system header we need */
987 #define _GNU_SOURCE
988 #include <errno.h>
989 #include <stdio.h>
990 #include <stdlib.h>
991
992 /* On some system termio is either absent or including it will disable termios
993    (HP-UX) */
994 #if ! defined (__hpux__) && ! defined (FREEBSD) && ! defined (__APPLE__)
995 #   include <termio.h>
996 #endif
997
998 #include <sys/ioctl.h>
999 #include <termios.h>
1000 #include <fcntl.h>
1001 #include <string.h>
1002 #include <sys/stat.h>
1003 #include <sys/types.h>
1004 #include <sys/wait.h>
1005 #include <unistd.h>
1006 #if defined (sun)
1007 #   include <sys/stropts.h>
1008 #endif
1009 #if defined (FREEBSD) || defined (sun)
1010 #   include <sys/signal.h>
1011 #endif
1012 #if defined (__hpux__)
1013 #   include <sys/termio.h>
1014 #   include <sys/stropts.h>
1015 #endif
1016
1017 #define CDISABLE _POSIX_VDISABLE
1018
1019 /* On HP-UX and Sun system, there is a bzero function but with a different
1020    signature. Use memset instead */
1021 #if defined (__hpux__) || defined (sun) || defined (_AIX)
1022 #   define bzero(s,n) memset (s,0,n)
1023 #endif
1024
1025 /* POSIX does not specify how to open the master side of a terminal.Several
1026    methods are available (system specific):
1027       1- using a cloning device (USE_CLONE_DEVICE)
1028       2- getpt                  (USE_GETPT)
1029       3- openpty                (USE_OPENPTY)
1030       4- _getpty                (USE_GETPTY)
1031
1032    When using the cloning device method, the macro USE_CLONE_DEVICE should
1033    contains a full path to the adequate device.
1034
1035    When a new system is about to be supported, one of the previous macro should
1036    be set otherwise allocate_pty_desc will return an error
1037 */
1038
1039 /* Configurable part */
1040 #if defined (__APPLE__) || defined (FREEBSD)
1041 #define USE_OPENPTY
1042 #elif defined (IRIX)
1043 #define USE_GETPTY
1044 #elif defined (linux)
1045 #define USE_GETPT
1046 #elif defined (sun)
1047 #define USE_CLONE_DEVICE "/dev/ptmx"
1048 #elif defined (_AIX)
1049 #define USE_CLONE_DEVICE "/dev/ptc"
1050 #elif defined (OSF1)
1051 /* On Tru64, the systems offers various interfaces to open a terminal:
1052     - /dev/ptmx: this the system V driver (stream based),
1053     - /dev/ptmx_bsd: the non stream based clone device,
1054     - the openpty function which use BSD interface.
1055
1056    Using directly /dev/ptmx_bsd on Tru64 5.1B seems to consume all the
1057    available slave ptys (why ?). When using openpty it seems that the function
1058    handles the creation of entries in /dev/pts when necessary and so avoid this
1059    starvation issue. The pty man entry suggests also to use openpty.
1060 */
1061 #define USE_OPENPTY
1062 #elif defined (__hpux__)
1063 /* On HP-UX we use the streamed version. Using the non streamed version is not
1064    recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1065    issues to detect process terminations. */
1066 #define USE_CLONE_DEVICE "/dev/ptmx"
1067 #endif
1068
1069 /* structure that holds information about the terminal used and the process
1070    connected on the slave side */
1071 typedef struct pty_desc_struct {
1072    int  master_fd;     /* fd of the master side if the terminal */
1073    int  slave_fd;      /* fd of the slave side */
1074    char slave_name[32];   /* filename of the slave side */
1075    int  child_pid;     /* PID of the child process connected to the slave side
1076                          of the terminal */
1077 } pty_desc;
1078
1079 /* allocate_pty_desc - allocate a pseudo terminal
1080  *
1081  * PARAMETERS
1082  *   out desc  returned pointer to a pty_desc structure containing information
1083  *             about the opened pseudo terminal
1084  * RETURN VALUE
1085  *   -1        if failed
1086  *    0        if ok
1087  * COMMENTS
1088  *   If the function is successful we should have at least the master side fd
1089  *   and the slave side filename. On some system, the slave side will also be
1090  *   opened. If this is not the case the slave side will be open once we are in
1091  *   the child process (note that opening the slave side at this stage will
1092  *   failed...).
1093  */
1094
1095 extern char* ptsname (int);
1096
1097 static int
1098 allocate_pty_desc (pty_desc **desc) {
1099
1100    pty_desc *result;
1101    int  status      =  0;
1102    int  slave_fd    = -1;
1103    int  master_fd   = -1;
1104    char *slave_name = NULL;
1105
1106 #ifdef USE_GETPT
1107   master_fd = getpt ();
1108 #elif defined (USE_OPENPTY)
1109   status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1110 #elif defined (USE_GETPTY)
1111   slave_name = _getpty (&master_fd, O_RDWR | O_NDELAY, 0600, 0);
1112   if (slave_name == NULL) status = -1;
1113 #elif defined (USE_CLONE_DEVICE)
1114   master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1115 #else
1116   printf ("[error]: terminal support is not configured\n");
1117   return -1;
1118 #endif
1119
1120   /* at this stage we should have the master side fd and status should be 0 */
1121   if (status != 0 || master_fd < 0)
1122     {
1123       /* If this is not the case close all opened files and return -1 */
1124       printf ("[error]: cannot allocate master side of the pty\n");
1125       if (master_fd >= 0) close (master_fd);
1126       if (slave_fd  >= 0) close (slave_fd);
1127       *desc = NULL;
1128       return -1;
1129     }
1130
1131   /* retrieve the file name of the slave side if necessary */
1132   if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1133
1134   /* Now we should have slave file name */
1135   if (slave_name == NULL)
1136     {
1137       /* If not the case close any opened file and return - 1 */
1138       printf ("[error]: cannot allocate slave side of the pty\n");
1139       if (master_fd >= 0) close (master_fd);
1140       if (slave_fd  >= 0) close (slave_fd);
1141       *desc = NULL;
1142       return -1;
1143     }
1144
1145   /* grant access to the slave side */
1146   grantpt (master_fd);
1147   /* unlock the terminal */
1148   unlockpt (master_fd);
1149
1150   /* set desc and return 0 */
1151   result = malloc (sizeof (pty_desc));
1152   result->master_fd  = master_fd;
1153   result->slave_fd   = slave_fd;
1154   /* the string returned by ptsname or _getpty is a static allocated string. So
1155      we should make a copy */
1156   strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1157   result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1158   result->child_pid  = -1;
1159   *desc=result;
1160   return 0;
1161 }
1162
1163 /* some utility macro that make the code of child_setup_tty easier to read */
1164 #define __enable(a, b) ((a) |= (b))
1165 #define __disable(a, b) ((a) &= ~(b))
1166
1167 /* some properties do not exist on all systems. Set their value to 0 in that
1168    case */
1169 #ifndef IUCLC
1170 #define IUCLC 0
1171 #endif
1172 #ifndef OLCUC
1173 #define OLCUC 0
1174 #endif
1175 #ifndef NLDLY
1176 #define NLDLY 0
1177 #define CRDLY 0
1178 #define TABDLY 0
1179 #define BSDLY 0
1180 #define VTDLY 0
1181 #define FFDLY 0
1182 #endif
1183
1184 /* child_setup_tty - set terminal properties
1185  *
1186  * PARAMETERS
1187  *   file descriptor of the slave side of the terminal
1188  *
1189  * RETURN VALUE
1190  *   0 if success, any other value if failed.
1191  *
1192  * COMMENTS
1193  *   None
1194  */
1195 static int
1196 child_setup_tty (int fd)
1197 {
1198   struct termios s;
1199   int    status;
1200
1201   /* ensure that s is filled with 0 */
1202   bzero (&s, sizeof (&s));
1203
1204   /* Get the current terminal settings */
1205   status = tcgetattr (fd, &s);
1206   if (status != 0) return -1;
1207
1208   /* Adjust input modes */
1209   __disable (s.c_iflag, IUCLC);    /* don't transform to lower case */
1210   __disable (s.c_iflag, ISTRIP);   /* don't delete 8th bit */
1211
1212   /* Adjust output modes */
1213   __enable  (s.c_oflag, OPOST);    /* enable postprocessing */
1214   __disable (s.c_oflag, ONLCR);    /* don't map LF to CR-LF */
1215   __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1216                                    /* disable delays */
1217   __disable (s.c_oflag, OLCUC);    /* don't transform to upper case */
1218
1219   /* Adjust control modes */
1220   s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1221
1222   /* Adjust local modes */
1223   __disable (s.c_lflag, ECHO);     /* disable echo */
1224   __enable  (s.c_lflag, ISIG);     /* enable signals */
1225   __enable  (s.c_lflag, ICANON);   /* erase/kill/eof processing */
1226
1227   /* Adjust control characters */
1228   /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1229      otherwise send_signal_via_characters will fail */
1230   s.c_cc[VEOF]   = 04;         /* insure that EOF is Control-D */
1231   s.c_cc[VERASE] = CDISABLE;   /* disable erase processing */
1232   s.c_cc[VKILL]  = CDISABLE;   /* disable kill processing */
1233   s.c_cc[VQUIT]  = 28;         /* Control-\ */
1234   s.c_cc[VINTR]  = 03;         /* Control-C */
1235   s.c_cc[VEOL]   = CDISABLE;
1236   s.c_cc[VSUSP]  = 26;         /* Control-Z */
1237
1238   /* push our changes */
1239   status = tcsetattr (fd, TCSADRAIN, &s);
1240   return status;
1241 }
1242
1243 /* __gnat_setup_communication - interface to the external world. Should be
1244  * called before forking. On Unixes this function only call allocate_pty_desc.
1245  * The Windows implementation (in different part of this file) is very
1246  * different.
1247  *
1248  * PARAMETERS
1249  *  out desc   returned pointer to a pty_desc structure
1250  * RETURN VALUE
1251  *  0 if success, -1 otherwise
1252  */
1253 int __gnat_setup_communication (pty_desc** desc) {
1254   return allocate_pty_desc (desc);
1255 }
1256
1257 /* __gnat_setup_parent_communication - interface to the external world. Should
1258  * be called after forking in the parent process
1259  *
1260  * PARAMETERS
1261  *   out in_fd
1262      out out_fd
1263      out err_fd fds corresponding to the parent side of the
1264                 terminal
1265      in pid_out child process pid
1266  * RETRUN VALUE
1267  *  0
1268  */
1269 void
1270 __gnat_setup_parent_communication
1271   (pty_desc *desc,
1272    int*     in_fd,  /* input */
1273    int*     out_fd, /* output */
1274    int*     err_fd, /* error */
1275    int*     pid_out)
1276 {
1277
1278   *in_fd = desc->master_fd;
1279   *out_fd= desc->master_fd;
1280   *err_fd= desc->master_fd;
1281   desc->child_pid = *pid_out;
1282 }
1283
1284 /* __gnat_setup_winsize - Sets up the size of the terminal
1285  * This lets the process know the size of the terminal
1286  */
1287
1288 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1289 #ifdef TIOCGWINSZ
1290   struct winsize s;
1291   s.ws_row = (unsigned short)rows;
1292   s.ws_col = (unsigned short)columns;
1293   s.ws_xpixel = 0;
1294   s.ws_ypixel = 0;
1295   ioctl (desc->master_fd, TIOCSWINSZ, &s);
1296 #ifdef SIGWINCH
1297   if (desc->child_pid > 0) {
1298      /* Let the process know about the change in size */
1299      kill (desc->child_pid, SIGWINCH);
1300   }
1301 #endif
1302 #endif
1303 }
1304
1305 /* __gnat_setup_child_communication - interface to external world. Should be
1306  * called after forking in the child process. On Unixes, this function
1307  * first adjust the line setting, set standard output, input and error and
1308  * then spawn the program.
1309  *
1310  * PARAMETERS
1311  *   desc      a pty_desc structure containing the pty parameters
1312  *   new_argv  argv of the program to be spawned
1313  * RETURN VALUE
1314  *   this function should not return
1315  */
1316 int
1317 __gnat_setup_child_communication
1318    (pty_desc *desc,
1319     char **new_argv,
1320     int Use_Pipes)
1321 {
1322   int status;
1323   int pid = getpid ();
1324
1325   setsid ();
1326
1327   /* open the slave side of the terminal if necessary */
1328   if (desc->slave_fd == -1)
1329 #if defined (_AIX)
1330     /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1331        then we might have some processes hanging on I/O system calls. Not sure
1332        we can do that for all platforms so do it only on AIX for the moment.
1333        On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1334        reading on the slave fd, in case there is no data available, if O_NDELAY
1335        is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1336        that interactive programs such as GDB prefer the O_NDELAY behavior.
1337        We chose O_NONBLOCK because it allows us to make the distinction
1338        between a true EOF and an EOF returned because there is no data
1339        available to be read.  */
1340     desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1341 #else
1342     desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1343 #endif
1344
1345 #if defined (sun) || defined (__hpux__)
1346   /* On systems such as Solaris we are using stream. We need to push the right
1347      "modules" in order to get the expected terminal behaviors. Otherwise
1348      functionalities such as termios are not available.  */
1349   ioctl (desc->slave_fd, I_PUSH, "ptem");
1350   ioctl (desc->slave_fd, I_PUSH, "ldterm");
1351   ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1352 #endif
1353
1354 #ifdef TIOCSCTTY
1355   /* make the tty the controling terminal */
1356   status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1357 #endif
1358
1359   /* adjust tty settings */
1360   child_setup_tty (desc->slave_fd);
1361   __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1362
1363   /* stdin, stdout and stderr should be now our tty */
1364   dup2 (desc->slave_fd, 0);
1365   dup2 (desc->slave_fd, 1);
1366   dup2 (desc->slave_fd, 2);
1367   if (desc->slave_fd > 2) close (desc->slave_fd);
1368
1369   /* adjust process group settings */
1370   status = setpgid (pid, pid);
1371   status = tcsetpgrp (0, pid);
1372
1373   /* launch the program */
1374   execvp (new_argv[0], new_argv);
1375
1376   /* return the pid */
1377   return pid;
1378 }
1379
1380 /* send_signal_via_characters - Send a characters that will trigger a signal
1381  * in the child process.
1382  *
1383  * PARAMETERS
1384  *  desc  a pty_desc structure containing terminal information
1385  *  int   a signal number
1386  * RETURN VALUE
1387  *  None
1388  */
1389 static void
1390 send_signal_via_characters
1391   (pty_desc *desc,
1392    int signal_number)
1393 {
1394   char ctrl_c         = 03;
1395   char ctrl_backslash = 28;
1396   char ctrl_Z         = 26;
1397
1398   switch (signal_number)
1399     {
1400       case SIGINT:
1401         write (desc->master_fd, &ctrl_c, 1); return;
1402       case SIGQUIT:
1403         write (desc->master_fd, &ctrl_backslash, 1); return;
1404       case SIGTSTP:
1405         write (desc->master_fd, &ctrl_Z, 1); return;
1406     }
1407 }
1408
1409 /* __gnat_interrupt_process - interrupt the child process
1410  *
1411  * PARAMETERS
1412  *   desc a pty_desc structure
1413  */
1414 int
1415 __gnat_interrupt_process (pty_desc *desc)
1416 {
1417   send_signal_via_characters (desc, SIGINT);
1418   return 0;
1419 }
1420
1421 /* __gnat_interrupt_pid - interrupt a process group
1422  *
1423  * PARAMETERS
1424  *   pid  pid of the process to interrupt
1425  */
1426 int
1427 __gnat_interrupt_pid (int pid)
1428 {
1429   kill (-pid, SIGINT);
1430   return 0;
1431 }
1432
1433 /* __gnat_terminate_process - kill a child process
1434  *
1435  * PARAMETERS
1436  *   desc pty_desc structure
1437  */
1438 int __gnat_terminate_process (pty_desc *desc)
1439 {
1440   return kill (desc->child_pid, SIGKILL);
1441 }
1442
1443 /* __gnat_tty_waitpid - wait for the child proces to die
1444  *
1445  * PARAMETERS
1446  *   desc pty_desc structure
1447  * RETURN VALUE
1448  *   exit status of the child process
1449  */
1450 int
1451 __gnat_tty_waitpid (pty_desc *desc)
1452 {
1453   int status = 0;
1454   waitpid (desc->child_pid, &status, 0);
1455   return WEXITSTATUS (status);
1456 }
1457
1458 /* __gnat_tty_supported - Are tty supported ?
1459  *
1460  * RETURN VALUE
1461  *   always 1 on Unix systems
1462  */
1463 int
1464 __gnat_tty_supported (void)
1465 {
1466   return 1;
1467 }
1468
1469 /* __gnat_free_process - free a pty_desc structure
1470  *
1471  * PARAMETERS
1472  *   in out desc: a pty desc structure
1473  */
1474 void
1475 __gnat_free_process (pty_desc** desc)
1476 {
1477   free (*desc);
1478   *desc = NULL;
1479 }
1480
1481 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1482 void
1483 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1484 {
1485   *ret = 0;
1486 }
1487
1488 /* __gnat_reset_tty - reset line setting
1489  *
1490  * PARAMETERS
1491  *   desc: a pty_desc structure
1492  */
1493 void
1494 __gnat_reset_tty (pty_desc* desc)
1495 {
1496   child_setup_tty (desc->master_fd);
1497 }
1498
1499 /* __gnat_new_tty - allocate a new terminal
1500  *
1501  * RETURN VALUE
1502  *   a pty_desc structure
1503  */
1504 pty_desc *
1505 __gnat_new_tty (void)
1506 {
1507   int status;
1508   pty_desc* desc;
1509   status = allocate_pty_desc (&desc);
1510   child_setup_tty (desc->master_fd);
1511   return desc;
1512 }
1513
1514 /* __gnat_close_tty - close a terminal
1515  *
1516  * PARAMETERS
1517  *   desc  a pty_desc strucure
1518  */
1519 void __gnat_close_tty (pty_desc* desc)
1520 {
1521   if (desc->master_fd >= 0) close (desc->master_fd);
1522   if (desc->slave_fd  >= 0) close (desc->slave_fd);
1523 }
1524
1525 /* __gnat_tty_name - return slave side device name
1526  *
1527  * PARAMETERS
1528  *   desc  a pty_desc strucure
1529  * RETURN VALUE
1530  *   a string
1531  */
1532 char *
1533 __gnat_tty_name (pty_desc* desc)
1534 {
1535   return desc->slave_name;
1536 }
1537
1538 /* __gnat_tty_name - return master side fd
1539  *
1540  * PARAMETERS
1541  *   desc  a pty_desc strucure
1542  * RETURN VALUE
1543  *   a fd
1544  */
1545 int
1546 __gnat_tty_fd (pty_desc* desc)
1547 {
1548   return desc->master_fd;
1549 }
1550
1551 #endif /* WIN32 */