OSDN Git Service

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