OSDN Git Service

Daily bump.
[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) && \
995     ! defined (__APPLE__) && ! defined(__rtems__)
996 #   include <termio.h>
997 #endif
998
999 #include <sys/ioctl.h>
1000 #include <termios.h>
1001 #include <fcntl.h>
1002 #include <string.h>
1003 #include <sys/stat.h>
1004 #include <sys/types.h>
1005 #include <sys/wait.h>
1006 #include <unistd.h>
1007 #if defined (sun)
1008 #   include <sys/stropts.h>
1009 #endif
1010 #if defined (FREEBSD) || defined (sun)
1011 #   include <sys/signal.h>
1012 #endif
1013 #if defined (__hpux__)
1014 #   include <sys/termio.h>
1015 #   include <sys/stropts.h>
1016 #endif
1017
1018 #define CDISABLE _POSIX_VDISABLE
1019
1020 /* On HP-UX and Sun system, there is a bzero function but with a different
1021    signature. Use memset instead */
1022 #if defined (__hpux__) || defined (sun) || defined (_AIX)
1023 #   define bzero(s,n) memset (s,0,n)
1024 #endif
1025
1026 /* POSIX does not specify how to open the master side of a terminal.Several
1027    methods are available (system specific):
1028       1- using a cloning device (USE_CLONE_DEVICE)
1029       2- getpt                  (USE_GETPT)
1030       3- openpty                (USE_OPENPTY)
1031       4- _getpty                (USE_GETPTY)
1032
1033    When using the cloning device method, the macro USE_CLONE_DEVICE should
1034    contains a full path to the adequate device.
1035
1036    When a new system is about to be supported, one of the previous macro should
1037    be set otherwise allocate_pty_desc will return an error
1038 */
1039
1040 /* Configurable part */
1041 #if defined (__APPLE__) || defined (FREEBSD)
1042 #define USE_OPENPTY
1043 #elif defined (IRIX)
1044 #define USE_GETPTY
1045 #elif defined (linux)
1046 #define USE_GETPT
1047 #elif defined (sun)
1048 #define USE_CLONE_DEVICE "/dev/ptmx"
1049 #elif defined (_AIX)
1050 #define USE_CLONE_DEVICE "/dev/ptc"
1051 #elif defined (OSF1)
1052 /* On Tru64, the systems offers various interfaces to open a terminal:
1053     - /dev/ptmx: this the system V driver (stream based),
1054     - /dev/ptmx_bsd: the non stream based clone device,
1055     - the openpty function which use BSD interface.
1056
1057    Using directly /dev/ptmx_bsd on Tru64 5.1B seems to consume all the
1058    available slave ptys (why ?). When using openpty it seems that the function
1059    handles the creation of entries in /dev/pts when necessary and so avoid this
1060    starvation issue. The pty man entry suggests also to use openpty.
1061 */
1062 #define USE_OPENPTY
1063 #elif defined (__hpux__)
1064 /* On HP-UX we use the streamed version. Using the non streamed version is not
1065    recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1066    issues to detect process terminations. */
1067 #define USE_CLONE_DEVICE "/dev/ptmx"
1068 #endif
1069
1070 /* structure that holds information about the terminal used and the process
1071    connected on the slave side */
1072 typedef struct pty_desc_struct {
1073    int  master_fd;     /* fd of the master side if the terminal */
1074    int  slave_fd;      /* fd of the slave side */
1075    char slave_name[32];   /* filename of the slave side */
1076    int  child_pid;     /* PID of the child process connected to the slave side
1077                          of the terminal */
1078 } pty_desc;
1079
1080 /* allocate_pty_desc - allocate a pseudo terminal
1081  *
1082  * PARAMETERS
1083  *   out desc  returned pointer to a pty_desc structure containing information
1084  *             about the opened pseudo terminal
1085  * RETURN VALUE
1086  *   -1        if failed
1087  *    0        if ok
1088  * COMMENTS
1089  *   If the function is successful we should have at least the master side fd
1090  *   and the slave side filename. On some system, the slave side will also be
1091  *   opened. If this is not the case the slave side will be open once we are in
1092  *   the child process (note that opening the slave side at this stage will
1093  *   failed...).
1094  */
1095
1096 extern char* ptsname (int);
1097
1098 static int
1099 allocate_pty_desc (pty_desc **desc) {
1100
1101    pty_desc *result;
1102    int  status      =  0;
1103    int  slave_fd    = -1;
1104    int  master_fd   = -1;
1105    char *slave_name = NULL;
1106
1107 #ifdef USE_GETPT
1108   master_fd = getpt ();
1109 #elif defined (USE_OPENPTY)
1110   status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1111 #elif defined (USE_GETPTY)
1112   slave_name = _getpty (&master_fd, O_RDWR | O_NDELAY, 0600, 0);
1113   if (slave_name == NULL) status = -1;
1114 #elif defined (USE_CLONE_DEVICE)
1115   master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1116 #else
1117   printf ("[error]: terminal support is not configured\n");
1118   return -1;
1119 #endif
1120
1121   /* at this stage we should have the master side fd and status should be 0 */
1122   if (status != 0 || master_fd < 0)
1123     {
1124       /* If this is not the case close all opened files and return -1 */
1125       printf ("[error]: cannot allocate master side of the pty\n");
1126       if (master_fd >= 0) close (master_fd);
1127       if (slave_fd  >= 0) close (slave_fd);
1128       *desc = NULL;
1129       return -1;
1130     }
1131
1132   /* retrieve the file name of the slave side if necessary */
1133   if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1134
1135   /* Now we should have slave file name */
1136   if (slave_name == NULL)
1137     {
1138       /* If not the case close any opened file and return - 1 */
1139       printf ("[error]: cannot allocate slave side of the pty\n");
1140       if (master_fd >= 0) close (master_fd);
1141       if (slave_fd  >= 0) close (slave_fd);
1142       *desc = NULL;
1143       return -1;
1144     }
1145
1146 #if !defined(__rtems__)
1147   /* grant access to the slave side */
1148   grantpt (master_fd);
1149   /* unlock the terminal */
1150   unlockpt (master_fd);
1151 #endif
1152
1153   /* set desc and return 0 */
1154   result = malloc (sizeof (pty_desc));
1155   result->master_fd  = master_fd;
1156   result->slave_fd   = slave_fd;
1157   /* the string returned by ptsname or _getpty is a static allocated string. So
1158      we should make a copy */
1159   strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1160   result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1161   result->child_pid  = -1;
1162   *desc=result;
1163   return 0;
1164 }
1165
1166 /* some utility macro that make the code of child_setup_tty easier to read */
1167 #define __enable(a, b) ((a) |= (b))
1168 #define __disable(a, b) ((a) &= ~(b))
1169
1170 /* some properties do not exist on all systems. Set their value to 0 in that
1171    case */
1172 #ifndef IUCLC
1173 #define IUCLC 0
1174 #endif
1175 #ifndef OLCUC
1176 #define OLCUC 0
1177 #endif
1178 #ifndef NLDLY
1179 #define NLDLY 0
1180 #define CRDLY 0
1181 #define TABDLY 0
1182 #define BSDLY 0
1183 #define VTDLY 0
1184 #define FFDLY 0
1185 #endif
1186
1187 /* child_setup_tty - set terminal properties
1188  *
1189  * PARAMETERS
1190  *   file descriptor of the slave side of the terminal
1191  *
1192  * RETURN VALUE
1193  *   0 if success, any other value if failed.
1194  *
1195  * COMMENTS
1196  *   None
1197  */
1198 static int
1199 child_setup_tty (int fd)
1200 {
1201   struct termios s;
1202   int    status;
1203
1204   /* ensure that s is filled with 0 */
1205   bzero (&s, sizeof (&s));
1206
1207   /* Get the current terminal settings */
1208   status = tcgetattr (fd, &s);
1209   if (status != 0) return -1;
1210
1211   /* Adjust input modes */
1212   __disable (s.c_iflag, IUCLC);    /* don't transform to lower case */
1213   __disable (s.c_iflag, ISTRIP);   /* don't delete 8th bit */
1214
1215   /* Adjust output modes */
1216   __enable  (s.c_oflag, OPOST);    /* enable postprocessing */
1217   __disable (s.c_oflag, ONLCR);    /* don't map LF to CR-LF */
1218   __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1219                                    /* disable delays */
1220   __disable (s.c_oflag, OLCUC);    /* don't transform to upper case */
1221
1222   /* Adjust control modes */
1223   s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1224
1225   /* Adjust local modes */
1226   __disable (s.c_lflag, ECHO);     /* disable echo */
1227   __enable  (s.c_lflag, ISIG);     /* enable signals */
1228   __enable  (s.c_lflag, ICANON);   /* erase/kill/eof processing */
1229
1230   /* Adjust control characters */
1231   /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1232      otherwise send_signal_via_characters will fail */
1233   s.c_cc[VEOF]   = 04;         /* insure that EOF is Control-D */
1234   s.c_cc[VERASE] = CDISABLE;   /* disable erase processing */
1235   s.c_cc[VKILL]  = CDISABLE;   /* disable kill processing */
1236   s.c_cc[VQUIT]  = 28;         /* Control-\ */
1237   s.c_cc[VINTR]  = 03;         /* Control-C */
1238   s.c_cc[VEOL]   = CDISABLE;
1239   s.c_cc[VSUSP]  = 26;         /* Control-Z */
1240
1241   /* push our changes */
1242   status = tcsetattr (fd, TCSADRAIN, &s);
1243   return status;
1244 }
1245
1246 /* __gnat_setup_communication - interface to the external world. Should be
1247  * called before forking. On Unixes this function only call allocate_pty_desc.
1248  * The Windows implementation (in different part of this file) is very
1249  * different.
1250  *
1251  * PARAMETERS
1252  *  out desc   returned pointer to a pty_desc structure
1253  * RETURN VALUE
1254  *  0 if success, -1 otherwise
1255  */
1256 int __gnat_setup_communication (pty_desc** desc) {
1257   return allocate_pty_desc (desc);
1258 }
1259
1260 /* __gnat_setup_parent_communication - interface to the external world. Should
1261  * be called after forking in the parent process
1262  *
1263  * PARAMETERS
1264  *   out in_fd
1265      out out_fd
1266      out err_fd fds corresponding to the parent side of the
1267                 terminal
1268      in pid_out child process pid
1269  * RETRUN VALUE
1270  *  0
1271  */
1272 void
1273 __gnat_setup_parent_communication
1274   (pty_desc *desc,
1275    int*     in_fd,  /* input */
1276    int*     out_fd, /* output */
1277    int*     err_fd, /* error */
1278    int*     pid_out)
1279 {
1280
1281   *in_fd = desc->master_fd;
1282   *out_fd= desc->master_fd;
1283   *err_fd= desc->master_fd;
1284   desc->child_pid = *pid_out;
1285 }
1286
1287 /* __gnat_setup_winsize - Sets up the size of the terminal
1288  * This lets the process know the size of the terminal
1289  */
1290
1291 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1292 #ifdef TIOCGWINSZ
1293   struct winsize s;
1294   s.ws_row = (unsigned short)rows;
1295   s.ws_col = (unsigned short)columns;
1296   s.ws_xpixel = 0;
1297   s.ws_ypixel = 0;
1298   ioctl (desc->master_fd, TIOCSWINSZ, &s);
1299 #ifdef SIGWINCH
1300   if (desc->child_pid > 0) {
1301      /* Let the process know about the change in size */
1302      kill (desc->child_pid, SIGWINCH);
1303   }
1304 #endif
1305 #endif
1306 }
1307
1308 /* __gnat_setup_child_communication - interface to external world. Should be
1309  * called after forking in the child process. On Unixes, this function
1310  * first adjust the line setting, set standard output, input and error and
1311  * then spawn the program.
1312  *
1313  * PARAMETERS
1314  *   desc      a pty_desc structure containing the pty parameters
1315  *   new_argv  argv of the program to be spawned
1316  * RETURN VALUE
1317  *   this function should not return
1318  */
1319 int
1320 __gnat_setup_child_communication
1321    (pty_desc *desc,
1322     char **new_argv,
1323     int Use_Pipes)
1324 {
1325   int status;
1326   int pid = getpid ();
1327
1328   setsid ();
1329
1330   /* open the slave side of the terminal if necessary */
1331   if (desc->slave_fd == -1)
1332 #if defined (_AIX)
1333     /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1334        then we might have some processes hanging on I/O system calls. Not sure
1335        we can do that for all platforms so do it only on AIX for the moment.
1336        On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1337        reading on the slave fd, in case there is no data available, if O_NDELAY
1338        is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1339        that interactive programs such as GDB prefer the O_NDELAY behavior.
1340        We chose O_NONBLOCK because it allows us to make the distinction
1341        between a true EOF and an EOF returned because there is no data
1342        available to be read.  */
1343     desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1344 #else
1345     desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1346 #endif
1347
1348 #if defined (sun) || defined (__hpux__)
1349   /* On systems such as Solaris we are using stream. We need to push the right
1350      "modules" in order to get the expected terminal behaviors. Otherwise
1351      functionalities such as termios are not available.  */
1352   ioctl (desc->slave_fd, I_PUSH, "ptem");
1353   ioctl (desc->slave_fd, I_PUSH, "ldterm");
1354   ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1355 #endif
1356
1357 #ifdef TIOCSCTTY
1358   /* make the tty the controling terminal */
1359   status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1360 #endif
1361
1362   /* adjust tty settings */
1363   child_setup_tty (desc->slave_fd);
1364   __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1365
1366   /* stdin, stdout and stderr should be now our tty */
1367   dup2 (desc->slave_fd, 0);
1368   dup2 (desc->slave_fd, 1);
1369   dup2 (desc->slave_fd, 2);
1370   if (desc->slave_fd > 2) close (desc->slave_fd);
1371
1372   /* adjust process group settings */
1373   status = setpgid (pid, pid);
1374   status = tcsetpgrp (0, pid);
1375
1376   /* launch the program */
1377   execvp (new_argv[0], new_argv);
1378
1379   /* return the pid */
1380   return pid;
1381 }
1382
1383 /* send_signal_via_characters - Send a characters that will trigger a signal
1384  * in the child process.
1385  *
1386  * PARAMETERS
1387  *  desc  a pty_desc structure containing terminal information
1388  *  int   a signal number
1389  * RETURN VALUE
1390  *  None
1391  */
1392 static void
1393 send_signal_via_characters
1394   (pty_desc *desc,
1395    int signal_number)
1396 {
1397   char ctrl_c         = 03;
1398   char ctrl_backslash = 28;
1399   char ctrl_Z         = 26;
1400
1401   switch (signal_number)
1402     {
1403       case SIGINT:
1404         write (desc->master_fd, &ctrl_c, 1); return;
1405       case SIGQUIT:
1406         write (desc->master_fd, &ctrl_backslash, 1); return;
1407       case SIGTSTP:
1408         write (desc->master_fd, &ctrl_Z, 1); return;
1409     }
1410 }
1411
1412 /* __gnat_interrupt_process - interrupt the child process
1413  *
1414  * PARAMETERS
1415  *   desc a pty_desc structure
1416  */
1417 int
1418 __gnat_interrupt_process (pty_desc *desc)
1419 {
1420   send_signal_via_characters (desc, SIGINT);
1421   return 0;
1422 }
1423
1424 /* __gnat_interrupt_pid - interrupt a process group
1425  *
1426  * PARAMETERS
1427  *   pid  pid of the process to interrupt
1428  */
1429 int
1430 __gnat_interrupt_pid (int pid)
1431 {
1432   kill (-pid, SIGINT);
1433   return 0;
1434 }
1435
1436 /* __gnat_terminate_process - kill a child process
1437  *
1438  * PARAMETERS
1439  *   desc pty_desc structure
1440  */
1441 int __gnat_terminate_process (pty_desc *desc)
1442 {
1443   return kill (desc->child_pid, SIGKILL);
1444 }
1445
1446 /* __gnat_tty_waitpid - wait for the child proces to die
1447  *
1448  * PARAMETERS
1449  *   desc pty_desc structure
1450  * RETURN VALUE
1451  *   exit status of the child process
1452  */
1453 int
1454 __gnat_tty_waitpid (pty_desc *desc)
1455 {
1456   int status = 0;
1457   waitpid (desc->child_pid, &status, 0);
1458   return WEXITSTATUS (status);
1459 }
1460
1461 /* __gnat_tty_supported - Are tty supported ?
1462  *
1463  * RETURN VALUE
1464  *   always 1 on Unix systems
1465  */
1466 int
1467 __gnat_tty_supported (void)
1468 {
1469   return 1;
1470 }
1471
1472 /* __gnat_free_process - free a pty_desc structure
1473  *
1474  * PARAMETERS
1475  *   in out desc: a pty desc structure
1476  */
1477 void
1478 __gnat_free_process (pty_desc** desc)
1479 {
1480   free (*desc);
1481   *desc = NULL;
1482 }
1483
1484 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1485 void
1486 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1487 {
1488   *ret = 0;
1489 }
1490
1491 /* __gnat_reset_tty - reset line setting
1492  *
1493  * PARAMETERS
1494  *   desc: a pty_desc structure
1495  */
1496 void
1497 __gnat_reset_tty (pty_desc* desc)
1498 {
1499   child_setup_tty (desc->master_fd);
1500 }
1501
1502 /* __gnat_new_tty - allocate a new terminal
1503  *
1504  * RETURN VALUE
1505  *   a pty_desc structure
1506  */
1507 pty_desc *
1508 __gnat_new_tty (void)
1509 {
1510   int status;
1511   pty_desc* desc;
1512   status = allocate_pty_desc (&desc);
1513   child_setup_tty (desc->master_fd);
1514   return desc;
1515 }
1516
1517 /* __gnat_close_tty - close a terminal
1518  *
1519  * PARAMETERS
1520  *   desc  a pty_desc strucure
1521  */
1522 void __gnat_close_tty (pty_desc* desc)
1523 {
1524   if (desc->master_fd >= 0) close (desc->master_fd);
1525   if (desc->slave_fd  >= 0) close (desc->slave_fd);
1526 }
1527
1528 /* __gnat_tty_name - return slave side device name
1529  *
1530  * PARAMETERS
1531  *   desc  a pty_desc strucure
1532  * RETURN VALUE
1533  *   a string
1534  */
1535 char *
1536 __gnat_tty_name (pty_desc* desc)
1537 {
1538   return desc->slave_name;
1539 }
1540
1541 /* __gnat_tty_name - return master side fd
1542  *
1543  * PARAMETERS
1544  *   desc  a pty_desc strucure
1545  * RETURN VALUE
1546  *   a fd
1547  */
1548 int
1549 __gnat_tty_fd (pty_desc* desc)
1550 {
1551   return desc->master_fd;
1552 }
1553
1554 #endif /* WIN32 */