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