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