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-2013, 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 
__gnat_new_tty(void)37 void * __gnat_new_tty (void) { return (void*)0; }
__gnat_tty_name(void * t)38 char * __gnat_tty_name (void* t) { return (char*)0; }
__gnat_interrupt_pid(int pid)39 int    __gnat_interrupt_pid (int pid) { return -1; }
__gnat_interrupt_process(void * desc)40 int    __gnat_interrupt_process (void* desc) { return -1; }
__gnat_setup_communication(void ** desc)41 int    __gnat_setup_communication (void** desc) { return -1; }
__gnat_setup_parent_communication(void * d,int * i,int * o,int * e,int * p)42 void   __gnat_setup_parent_communication
43          (void* d, int* i, int* o, int*e, int*p) { return -1; }
__gnat_setup_child_communication(void * d,char ** n,int u)44 int    __gnat_setup_child_communication
45          (void* d, char **n, int u) { return -1; }
__gnat_terminate_process(void * desc)46 int    __gnat_terminate_process (void *desc) { return -1; }
__gnat_tty_fd(void * t)47 int    __gnat_tty_fd (void* t) { return -1; }
__gnat_tty_supported(void)48 int    __gnat_tty_supported (void) { return 0; }
__gnat_tty_waitpid(void * desc)49 int    __gnat_tty_waitpid (void *desc) { return 1; }
__gnat_close_tty(void * t)50 void   __gnat_close_tty (void* t) {}
__gnat_free_process(void ** process)51 void   __gnat_free_process (void** process) {}
__gnat_reset_tty(void * t)52 void   __gnat_reset_tty (void* t) {}
__gnat_send_header(void * d,char h[5],int s,int * r)53 void   __gnat_send_header (void* d, char h[5], int s, int *r) {}
__gnat_setup_winsize(void * desc,int rows,int columns)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
is_gui_app(char * exe)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
AbsoluteSeek(HANDLE hFile,DWORD offset)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
ReadBytes(HANDLE hFile,LPVOID buffer,DWORD size)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
nt_spawnve(char * exe,char ** argv,char * env,struct TTY_Process * process)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      preceded 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
__gnat_send_header(struct TTY_Process * p,char header[5],int size,int * ret)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
__gnat_setup_communication(struct TTY_Process ** process_out)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
__gnat_setup_child_communication(struct TTY_Process * process,char ** argv,int Use_Pipes)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
__gnat_setup_parent_communication(struct TTY_Process * process,int * in,int * out,int * err,int * pid)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
cache_system_info(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
find_child_console(HWND hwnd,child_process * cp)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
__gnat_interrupt_process(struct TTY_Process * p)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
__gnat_interrupt_pid(int pid)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
__gnat_terminate_process(struct TTY_Process * p)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
__gnat_tty_waitpid(struct TTY_Process * p)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
__gnat_free_process(struct TTY_Process ** process)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
__gnat_tty_supported(void)929 __gnat_tty_supported (void)
930 {
931   return 0;
932 }
933 
934 /* Return the tty name associated with p */
935 
936 char *
__gnat_tty_name(TTY_Handle * t)937 __gnat_tty_name (TTY_Handle* t)
938 {
939   return t->tty_name;
940 }
941 
942 int
__gnat_tty_fd(TTY_Handle * t)943 __gnat_tty_fd (TTY_Handle* t)
944 {
945   return t->tty_fd;
946 }
947 
948 TTY_Handle*
__gnat_new_tty(void)949 __gnat_new_tty (void)
950 {
951   return (TTY_Handle*)0;
952 }
953 
954 void
__gnat_reset_tty(TTY_Handle * t)955 __gnat_reset_tty (TTY_Handle* t)
956 {
957   return;
958 }
959 
960 void
__gnat_close_tty(TTY_Handle * t)961 __gnat_close_tty (TTY_Handle* t)
962 {
963   free (t);
964 }
965 
966 void
__gnat_setup_winsize(void * desc,int rows,int columns)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
allocate_pty_desc(pty_desc ** desc)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
child_setup_tty(int fd)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  */
__gnat_setup_communication(pty_desc ** desc)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
__gnat_setup_parent_communication(pty_desc * desc,int * in_fd,int * out_fd,int * err_fd,int * pid_out)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 
__gnat_setup_winsize(pty_desc * desc,int rows,int columns)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
__gnat_setup_child_communication(pty_desc * desc,char ** new_argv,int Use_Pipes)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 controlling 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
send_signal_via_characters(pty_desc * desc,int signal_number)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
__gnat_interrupt_process(pty_desc * desc)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
__gnat_interrupt_pid(int pid)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  */
__gnat_terminate_process(pty_desc * desc)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 process to die
1424  *
1425  * PARAMETERS
1426  *   desc pty_desc structure
1427  * RETURN VALUE
1428  *   exit status of the child process
1429  */
1430 int
__gnat_tty_waitpid(pty_desc * desc)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
__gnat_tty_supported(void)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
__gnat_free_process(pty_desc ** desc)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
__gnat_send_header(pty_desc * desc,char header[5],int size,int * ret)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
__gnat_reset_tty(pty_desc * desc)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 *
__gnat_new_tty(void)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  */
__gnat_close_tty(pty_desc * desc)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 *
__gnat_tty_name(pty_desc * desc)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
__gnat_tty_fd(pty_desc * desc)1526 __gnat_tty_fd (pty_desc* desc)
1527 {
1528   return desc->master_fd;
1529 }
1530 
1531 #endif /* WIN32 */
1532