PR c++/77539
[official-gcc.git] / gcc / ada / terminals.c
blobd72eb098850a916eeb6b3f181a48fdce4cc671ff
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-2015, 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 ****************************************************************************/
32 /* First all usupported platforms. Add stubs for exported routines. */
34 #if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
35 || defined (__ANDROID__) || defined (__PikeOS__)
37 #define ATTRIBUTE_UNUSED __attribute__((unused))
39 void *
40 __gnat_new_tty (void)
42 return (void*)0;
45 char *
46 __gnat_tty_name (void* t ATTRIBUTE_UNUSED)
48 return (char*)0;
51 int
52 __gnat_interrupt_pid (int pid ATTRIBUTE_UNUSED)
54 return -1;
57 int
58 __gnat_interrupt_process (void* desc ATTRIBUTE_UNUSED)
60 return -1;
63 int
64 __gnat_setup_communication (void** desc ATTRIBUTE_UNUSED)
66 return -1;
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)
78 int
79 __gnat_setup_child_communication (void *d ATTRIBUTE_UNUSED,
80 char **n ATTRIBUTE_UNUSED,
81 int u ATTRIBUTE_UNUSED)
83 return -1;
86 int
87 __gnat_terminate_process (void *desc ATTRIBUTE_UNUSED)
89 return -1;
92 int
93 __gnat_tty_fd (void* t ATTRIBUTE_UNUSED)
95 return -1;
98 int
99 __gnat_tty_supported (void)
101 return 0;
105 __gnat_tty_waitpid (void *desc ATTRIBUTE_UNUSED)
107 return 1;
110 void
111 __gnat_close_tty (void* t ATTRIBUTE_UNUSED)
115 void
116 __gnat_free_process (void** process ATTRIBUTE_UNUSED)
120 void
121 __gnat_reset_tty (void* t ATTRIBUTE_UNUSED)
125 void
126 __gnat_send_header (void* d ATTRIBUTE_UNUSED,
127 char h[5] ATTRIBUTE_UNUSED,
128 int s ATTRIBUTE_UNUSED,
129 int *r ATTRIBUTE_UNUSED)
133 void
134 __gnat_setup_winsize (void *desc ATTRIBUTE_UNUSED,
135 int rows ATTRIBUTE_UNUSED,
136 int columns ATTRIBUTE_UNUSED)
140 /* For Windows platforms. */
142 #elif defined(_WIN32)
144 #include <errno.h>
145 #include <stdio.h>
146 #include <stdlib.h>
148 #include <windows.h>
150 #define MAXPATHLEN 1024
152 #define NILP(x) ((x) == 0)
153 #define Qnil 0
154 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
155 #define INTEGERP(x) 1
156 #define XINT(x) x
158 struct TTY_Process {
159 int pid; /* Number of this process */
160 PROCESS_INFORMATION procinfo;
161 HANDLE w_infd, w_outfd;
162 HANDLE w_forkin, w_forkout;
163 BOOL usePipe;
166 /* Control whether create_child cause the process to inherit GPS'
167 error mode setting. The default is 1, to minimize the possibility of
168 subprocesses blocking when accessing unmounted drives. */
169 static int Vw32_start_process_inherit_error_mode = 1;
171 /* Control whether spawnve quotes arguments as necessary to ensure
172 correct parsing by child process. Because not all uses of spawnve
173 are careful about constructing argv arrays, we make this behavior
174 conditional (off by default, since a similar operation is already done
175 in g-expect.adb by calling Normalize_Argument). */
176 static int Vw32_quote_process_args = 0;
178 static DWORD AbsoluteSeek(HANDLE, DWORD);
179 static VOID ReadBytes(HANDLE, LPVOID, DWORD);
181 #define XFER_BUFFER_SIZE 2048
183 /* This tell if the executable we're about to launch uses a GUI interface. */
184 /* if we can't determine it, we will return true */
185 static int
186 is_gui_app (char *exe)
188 HANDLE hImage;
190 DWORD bytes;
191 DWORD iSection;
192 DWORD SectionOffset;
193 DWORD CoffHeaderOffset;
194 DWORD MoreDosHeader[16];
195 CHAR *file;
196 size_t nlen;
198 ULONG ntSignature;
200 IMAGE_DOS_HEADER image_dos_header;
201 IMAGE_FILE_HEADER image_file_header;
202 IMAGE_OPTIONAL_HEADER image_optional_header;
203 IMAGE_SECTION_HEADER image_section_header;
206 * Open the reference file.
208 nlen = strlen (exe);
209 file = exe;
210 if (nlen > 2) {
211 if (exe[0] == '"') {
212 /* remove quotes */
213 nlen -= 2;
214 file = malloc ((nlen + 1) * sizeof (char));
215 memcpy (file, &exe[1], nlen);
216 file [nlen] = '\0';
219 hImage = CreateFile(file,
220 GENERIC_READ,
221 FILE_SHARE_READ,
222 NULL,
223 OPEN_EXISTING,
224 FILE_ATTRIBUTE_NORMAL,
225 NULL);
227 if (file != exe) {
228 free (file);
231 if (INVALID_HANDLE_VALUE == hImage)
233 report_file_error ("Could not open exe: ", Qnil);
234 report_file_error (exe, Qnil);
235 report_file_error ("\n", Qnil);
236 CloseHandle (hImage);
237 return -1;
241 * Read the MS-DOS image header.
243 ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
245 if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
247 report_file_error("Sorry, I do not understand this file.\n", Qnil);
248 CloseHandle (hImage);
249 return -1;
253 * Read more MS-DOS header. */
254 ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
256 * Get actual COFF header.
258 CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
259 sizeof(ULONG);
260 if (CoffHeaderOffset < 0) {
261 CloseHandle (hImage);
262 return -1;
265 ReadBytes (hImage, &ntSignature, sizeof(ULONG));
267 if (IMAGE_NT_SIGNATURE != ntSignature)
269 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
270 CloseHandle (hImage);
271 return -1;
274 SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
275 IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
277 ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
280 * Read optional header.
282 ReadBytes(hImage,
283 &image_optional_header,
284 IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
286 CloseHandle (hImage);
288 switch (image_optional_header.Subsystem)
290 case IMAGE_SUBSYSTEM_UNKNOWN:
291 return 1;
292 break;
294 case IMAGE_SUBSYSTEM_NATIVE:
295 return 1;
296 break;
298 case IMAGE_SUBSYSTEM_WINDOWS_GUI:
299 return 1;
300 break;
302 case IMAGE_SUBSYSTEM_WINDOWS_CUI:
303 return 0;
304 break;
306 case IMAGE_SUBSYSTEM_OS2_CUI:
307 return 0;
308 break;
310 case IMAGE_SUBSYSTEM_POSIX_CUI:
311 return 0;
312 break;
314 default:
315 /* Unknown, return GUI app to be preservative: if yes, it will be
316 correctly launched, if no, it will be launched, and a console will
317 be also displayed, which is not a big deal */
318 return 1;
319 break;
324 static DWORD
325 AbsoluteSeek (HANDLE hFile, DWORD offset)
327 DWORD newOffset;
329 newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
331 if (newOffset == 0xFFFFFFFF)
332 return -1;
333 else
334 return newOffset;
337 static VOID
338 ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
340 DWORD bytes;
342 if (!ReadFile(hFile, buffer, size, &bytes, NULL))
344 size = 0;
345 return;
347 else if (size != bytes)
349 return;
353 static int
354 nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
356 STARTUPINFO start;
357 SECURITY_ATTRIBUTES sec_attrs;
358 SECURITY_DESCRIPTOR sec_desc;
359 DWORD flags;
360 char dir[ MAXPATHLEN ];
361 int pid;
362 int is_gui, use_cmd;
363 char *cmdline, *parg, **targ;
364 int do_quoting = 0;
365 char escape_char;
366 int arglen;
368 /* we have to do some conjuring here to put argv and envp into the
369 form CreateProcess wants... argv needs to be a space separated/null
370 terminated list of parameters, and envp is a null
371 separated/double-null terminated list of parameters.
373 Additionally, zero-length args and args containing whitespace or
374 quote chars need to be wrapped in double quotes - for this to work,
375 embedded quotes need to be escaped as well. The aim is to ensure
376 the child process reconstructs the argv array we start with
377 exactly, so we treat quotes at the beginning and end of arguments
378 as embedded quotes.
380 Note that using backslash to escape embedded quotes requires
381 additional special handling if an embedded quote is already
382 preceded by backslash, or if an arg requiring quoting ends with
383 backslash. In such cases, the run of escape characters needs to be
384 doubled. For consistency, we apply this special handling as long
385 as the escape character is not quote.
387 Since we have no idea how large argv and envp are likely to be we
388 figure out list lengths on the fly and allocate them. */
390 if (!NILP (Vw32_quote_process_args))
392 do_quoting = 1;
393 /* Override escape char by binding w32-quote-process-args to
394 desired character, or use t for auto-selection. */
395 if (INTEGERP (Vw32_quote_process_args))
396 escape_char = XINT (Vw32_quote_process_args);
397 else
398 escape_char = '\\';
401 /* do argv... */
402 arglen = 0;
403 targ = argv;
404 while (*targ)
406 char *p = *targ;
407 int need_quotes = 0;
408 int escape_char_run = 0;
410 if (*p == 0)
411 need_quotes = 1;
412 for ( ; *p; p++)
414 if (*p == '"')
416 /* allow for embedded quotes to be escaped */
417 arglen++;
418 need_quotes = 1;
419 /* handle the case where the embedded quote is already escaped */
420 if (escape_char_run > 0)
422 /* To preserve the arg exactly, we need to double the
423 preceding escape characters (plus adding one to
424 escape the quote character itself). */
425 arglen += escape_char_run;
428 else if (*p == ' ' || *p == '\t')
430 need_quotes = 1;
433 if (*p == escape_char && escape_char != '"')
434 escape_char_run++;
435 else
436 escape_char_run = 0;
438 if (need_quotes)
440 arglen += 2;
441 /* handle the case where the arg ends with an escape char - we
442 must not let the enclosing quote be escaped. */
443 if (escape_char_run > 0)
444 arglen += escape_char_run;
446 arglen += strlen (*targ) + 1;
447 targ++;
450 is_gui = is_gui_app (argv[0]);
451 use_cmd = FALSE;
453 if (is_gui == -1) {
454 /* could not determine application type. Try launching with "cmd /c" */
455 is_gui = FALSE;
456 arglen += 7;
457 use_cmd = TRUE;
460 cmdline = (char*)malloc (arglen + 1);
461 targ = argv;
462 parg = cmdline;
464 if (use_cmd == TRUE) {
465 strcpy (parg, "cmd /c ");
466 parg += 7;
469 while (*targ)
471 char * p = *targ;
472 int need_quotes = 0;
474 if (*p == 0)
475 need_quotes = 1;
477 if (do_quoting)
479 for ( ; *p; p++)
480 if (*p == ' ' || *p == '\t' || *p == '"')
481 need_quotes = 1;
483 if (need_quotes)
485 int escape_char_run = 0;
486 char * first;
487 char * last;
489 p = *targ;
490 first = p;
491 last = p + strlen (p) - 1;
492 *parg++ = '"';
493 for ( ; *p; p++)
495 if (*p == '"')
497 /* double preceding escape chars if any */
498 while (escape_char_run > 0)
500 *parg++ = escape_char;
501 escape_char_run--;
503 /* escape all quote chars, even at beginning or end */
504 *parg++ = escape_char;
506 *parg++ = *p;
508 if (*p == escape_char && escape_char != '"')
509 escape_char_run++;
510 else
511 escape_char_run = 0;
513 /* double escape chars before enclosing quote */
514 while (escape_char_run > 0)
516 *parg++ = escape_char;
517 escape_char_run--;
519 *parg++ = '"';
521 else
523 strcpy (parg, *targ);
524 parg += strlen (*targ);
526 *parg++ = ' ';
527 targ++;
529 *--parg = '\0';
531 memset (&start, 0, sizeof (start));
532 start.cb = sizeof (start);
534 if (process->usePipe == TRUE) {
535 start.dwFlags = STARTF_USESTDHANDLES;
536 start.hStdInput = process->w_forkin;
537 start.hStdOutput = process->w_forkout;
538 /* child's stderr is always redirected to outfd */
539 start.hStdError = process->w_forkout;
540 } else {
541 start.dwFlags = STARTF_USESTDHANDLES;
542 /* We only need to redirect stderr/stdout here. Stdin will be forced to
543 the spawned process console by explaunch */
544 start.hStdInput = NULL;
545 start.hStdOutput = process->w_forkout;
546 start.hStdError = process->w_forkout;
549 /* Explicitly specify no security */
550 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
551 goto EH_Fail;
552 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
553 goto EH_Fail;
554 sec_attrs.nLength = sizeof (sec_attrs);
555 sec_attrs.lpSecurityDescriptor = &sec_desc;
556 sec_attrs.bInheritHandle = FALSE;
558 /* creating a new console allow easier close. Do not use
559 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
560 flags = CREATE_NEW_CONSOLE;
561 if (NILP (Vw32_start_process_inherit_error_mode))
562 flags |= CREATE_DEFAULT_ERROR_MODE;
564 /* if app is not a gui application, hide the console */
565 if (is_gui == FALSE) {
566 start.dwFlags |= STARTF_USESHOWWINDOW;
567 start.wShowWindow = SW_HIDE;
570 /* Set initial directory to null character to use current directory */
571 if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
572 flags, env, NULL, &start, &process->procinfo))
573 goto EH_Fail;
575 pid = (int) process->procinfo.hProcess;
576 process->pid=pid;
578 return pid;
580 EH_Fail:
581 return -1;
584 /*************************
585 ** __gnat_send_header ()
586 *************************/
588 #define EXP_SLAVE_CREATE 'c'
589 #define EXP_SLAVE_KEY 'k'
590 #define EXP_SLAVE_MOUSE 'm'
591 #define EXP_SLAVE_WRITE 'w'
592 #define EXP_SLAVE_KILL 'x'
594 #define EXP_KILL_TERMINATE 0x1
595 #define EXP_KILL_CTRL_C 0x2
596 #define EXP_KILL_CTRL_BREAK 0x4
598 void
599 __gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
601 if (p->usePipe == FALSE) {
602 header[0] = EXP_SLAVE_WRITE;
603 header[1] = size & 0xff;
604 header[2] = (size & 0xff00) >> 8;
605 header[3] = (size & 0xff0000) >> 16;
606 header[4] = (size & 0xff000000) >> 24;
607 *ret = 1;
608 } else {
609 *ret = 0;
613 /**********************************
614 ** __gnat_setup_communication ()
615 **********************************/
618 __gnat_setup_communication (struct TTY_Process** process_out) /* output param */
620 struct TTY_Process* process;
622 process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
623 ZeroMemory (process, sizeof (struct TTY_Process));
624 *process_out = process;
626 return 0;
629 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
632 __gnat_setup_child_communication
633 (struct TTY_Process* process,
634 char** argv,
635 int Use_Pipes)
637 int cpid;
638 HANDLE parent;
639 SECURITY_ATTRIBUTES sec_attrs;
640 char slavePath [MAX_PATH];
641 char **nargv;
642 int argc;
643 int i;
644 char pipeNameIn[100];
645 HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
647 parent = GetCurrentProcess ();
649 /* Set inheritance for the pipe handles */
650 sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
651 sec_attrs.bInheritHandle = TRUE;
652 sec_attrs.lpSecurityDescriptor = NULL;
654 if (Use_Pipes) {
655 /* Create in and out pipes */
656 if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
657 report_file_error ("Creation of child's IN handle", Qnil);
658 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
659 report_file_error ("Creation of child's OUT handle", Qnil);
661 /* Do not inherit the parent's side of the pipes */
662 SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
663 SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
665 /* use native argv */
666 nargv = argv;
667 process->usePipe = TRUE;
669 } else {
670 static int pipeNameId = 0;
672 process->w_infd = NULL;
674 /* We create a named pipe for Input, as we handle input by sending special
675 commands to the explaunch process, that uses it to feed the actual input
676 of the process */
677 sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
678 GetCurrentProcessId(), pipeNameId);
679 pipeNameId++;
681 hSlaveInDrv = CreateNamedPipe(pipeNameIn,
682 PIPE_ACCESS_OUTBOUND,
683 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
684 20000, NULL);
685 if (hSlaveInDrv == NULL) goto end;
687 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
688 report_file_error ("Creation of child's OUT handle", Qnil);
690 if (SearchPath (NULL, "explaunch.exe", NULL,
691 MAX_PATH, slavePath, NULL) == 0) goto end;
693 for (argc=0; argv[argc] != NULL; argc++) ;
694 nargv = (char **) malloc (sizeof (char*) * (argc + 3));
695 nargv[0] = slavePath;
696 nargv[1] = pipeNameIn;
698 for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
699 process->usePipe = FALSE;
702 /* Spawn the child. */
703 cpid = nt_spawnve (nargv[0], nargv, NULL, process);
705 /* close the duplicated handles passed to the child */
706 CloseHandle (process->w_forkout);
708 if (process->usePipe == TRUE) {
709 CloseHandle (process->w_forkin);
711 } else {
712 UCHAR buf[8]; /* enough space for child status info */
713 DWORD count;
714 BOOL bRet;
715 DWORD dwRet;
718 * Wait for connection with the slave driver
720 bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
721 if (bRet == FALSE) {
722 dwRet = GetLastError();
723 if (dwRet == ERROR_PIPE_CONNECTED) {
725 } else {
726 goto end;
730 process->w_infd = hSlaveInDrv;
733 * wait for slave driver to initialize before allowing user to send to it
735 bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
736 if (bRet == FALSE) {
737 cpid = -1;
740 dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
741 if (dwRet != 0) {
742 cpid = -1;
745 cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
746 process->pid = cpid;
749 if (cpid == -1)
750 /* An error occurred while trying to spawn the process. */
751 report_file_error ("Spawning child process", Qnil);
753 return cpid;
754 end:
755 if (hSlaveInDrv != NULL)
756 CloseHandle (hSlaveInDrv);
757 return -1;
760 void
761 __gnat_setup_parent_communication
762 (struct TTY_Process* process,
763 int* in,
764 int* out,
765 int* err,
766 int* pid)
768 *in = _open_osfhandle ((long) process->w_infd, 0);
769 *out = _open_osfhandle ((long) process->w_outfd, 0);
770 /* child's stderr is always redirected to outfd */
771 *err = *out;
772 *pid = process->pid;
775 typedef struct _child_process
777 HWND hwnd;
778 PROCESS_INFORMATION *procinfo;
779 } child_process;
781 /* The major and minor versions of NT. */
782 static int w32_major_version;
783 static int w32_minor_version;
785 /* Distinguish between Windows NT and Windows 95. */
786 static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
788 /* Cache information describing the NT system for later use. */
789 static void
790 cache_system_info (void)
792 union
794 struct info
796 char major;
797 char minor;
798 short platform;
799 } info;
800 DWORD data;
801 } version;
803 /* Cache the version of the operating system. */
804 version.data = GetVersion ();
805 w32_major_version = version.info.major;
806 w32_minor_version = version.info.minor;
808 if (version.info.platform & 0x8000)
809 os_subtype = OS_WIN95;
810 else
811 os_subtype = OS_NT;
814 static BOOL CALLBACK
815 find_child_console (HWND hwnd, child_process * cp)
817 DWORD thread_id;
818 DWORD process_id;
820 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
821 if (process_id == cp->procinfo->dwProcessId)
823 char window_class[32];
825 GetClassName (hwnd, window_class, sizeof (window_class));
826 if (strcmp (window_class,
827 (os_subtype == OS_WIN95)
828 ? "tty"
829 : "ConsoleWindowClass") == 0)
831 cp->hwnd = hwnd;
832 return FALSE;
835 /* keep looking */
836 return TRUE;
840 __gnat_interrupt_process (struct TTY_Process* p)
842 char buf[2];
843 DWORD written;
844 BOOL bret;
846 if (p->usePipe == TRUE) {
847 bret = FALSE;
848 } else {
849 buf[0] = EXP_SLAVE_KILL;
850 buf[1] = EXP_KILL_CTRL_C;
851 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
854 if (bret == FALSE) {
855 return __gnat_interrupt_pid (p->procinfo.dwProcessId);
857 return 0;
861 __gnat_interrupt_pid (int pid)
863 volatile child_process cp;
864 int rc = 0;
866 cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
867 cp.procinfo->dwProcessId = pid;
869 if (os_subtype == OS_UNKNOWN)
870 cache_system_info ();
872 /* Try to locate console window for process. */
873 EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
875 if (cp.hwnd)
877 BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
878 /* Retrieve Ctrl-C scancode */
879 BYTE vk_break_code = 'C';
880 BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
881 HWND foreground_window;
883 foreground_window = GetForegroundWindow ();
884 if (foreground_window)
886 /* NT 5.0, and apparently also Windows 98, will not allow
887 a Window to be set to foreground directly without the
888 user's involvement. The workaround is to attach
889 ourselves to the thread that owns the foreground
890 window, since that is the only thread that can set the
891 foreground window. */
892 DWORD foreground_thread, child_thread;
894 foreground_thread =
895 GetWindowThreadProcessId (foreground_window, NULL);
896 if (foreground_thread == GetCurrentThreadId ()
897 || !AttachThreadInput (GetCurrentThreadId (),
898 foreground_thread, TRUE))
899 foreground_thread = 0;
901 child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
902 if (child_thread == GetCurrentThreadId ()
903 || !AttachThreadInput (GetCurrentThreadId (),
904 child_thread, TRUE))
905 child_thread = 0;
907 /* Set the foreground window to the child. */
908 if (SetForegroundWindow (cp.hwnd))
910 /* Generate keystrokes as if user had typed Ctrl-Break or
911 Ctrl-C. */
912 keybd_event (VK_CONTROL, control_scan_code, 0, 0);
913 keybd_event (vk_break_code, break_scan_code,
914 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
915 keybd_event (vk_break_code, break_scan_code,
916 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
917 | KEYEVENTF_KEYUP, 0);
918 keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
920 /* Sleep for a bit to give time for the main frame to respond
921 to focus change events. */
922 Sleep (100);
924 SetForegroundWindow (foreground_window);
926 /* Detach from the foreground and child threads now that
927 the foreground switching is over. */
928 if (foreground_thread)
929 AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
930 if (child_thread)
931 AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
934 /* Ctrl-Break is NT equivalent of SIGINT. */
935 else if (!GenerateConsoleCtrlEvent
936 (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
938 errno = EINVAL;
939 rc = -1;
942 free (cp.procinfo);
943 return rc;
946 /* kill a process, as this implementation use CreateProcess on Win32 we need
947 to use Win32 TerminateProcess API */
949 __gnat_terminate_process (struct TTY_Process* p)
951 char buf[2];
952 DWORD written;
953 BOOL bret;
955 if (p->usePipe == TRUE) {
956 bret = FALSE;
957 } else {
958 buf[0] = EXP_SLAVE_KILL;
959 buf[1] = EXP_KILL_TERMINATE;
960 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
963 if (bret == FALSE) {
964 if (!TerminateProcess (p->procinfo.hProcess, 1))
965 return -1;
966 else
967 return 0;
968 } else
969 return 0;
972 /* wait for process pid to terminate and return the process status. This
973 implementation is different from the adaint.c one for Windows as it uses
974 the Win32 API instead of the C one. */
977 __gnat_tty_waitpid (struct TTY_Process* p)
979 DWORD exitcode;
980 DWORD res;
981 HANDLE proc_hand = p->procinfo.hProcess;
983 res = WaitForSingleObject (proc_hand, 0);
984 GetExitCodeProcess (proc_hand, &exitcode);
986 CloseHandle (p->procinfo.hThread);
987 CloseHandle (p->procinfo.hProcess);
989 /* No need to close the handles: they were closed on the ada side */
991 return (int) exitcode;
994 /********************************
995 ** __gnat_free_process ()
996 ********************************/
998 void
999 __gnat_free_process (struct TTY_Process** process)
1001 free (*process);
1002 *process = NULL;
1005 /* TTY handling */
1007 typedef struct {
1008 int tty_fd; /* descriptor for the tty */
1009 char tty_name[24]; /* Name of TTY device */
1010 } TTY_Handle;
1013 __gnat_tty_supported (void)
1015 return 0;
1018 /* Return the tty name associated with p */
1020 char *
1021 __gnat_tty_name (TTY_Handle* t)
1023 return t->tty_name;
1027 __gnat_tty_fd (TTY_Handle* t)
1029 return t->tty_fd;
1032 TTY_Handle*
1033 __gnat_new_tty (void)
1035 return (TTY_Handle*)0;
1038 void
1039 __gnat_reset_tty (TTY_Handle* t)
1041 return;
1044 void
1045 __gnat_close_tty (TTY_Handle* t)
1047 free (t);
1050 void
1051 __gnat_setup_winsize (void *desc, int rows, int columns)
1055 #else /* defined(_WIN32, implementatin for all UNIXes */
1057 /* First defined some macro to identify easily some systems */
1058 #if defined (__FreeBSD__) \
1059 || defined (__OpenBSD__) \
1060 || defined (__NetBSD__) \
1061 || defined (__DragonFly__)
1062 # define BSD
1063 #endif
1065 /* Include every system header we need */
1066 #define _GNU_SOURCE
1067 #include <errno.h>
1068 #include <stdio.h>
1069 #include <stdlib.h>
1071 /* On some system termio is either absent or including it will disable termios
1072 (HP-UX) */
1073 #if !defined (__hpux__) && !defined (BSD) && !defined (__APPLE__) \
1074 && !defined (__rtems__)
1075 # include <termio.h>
1076 #endif
1078 #include <sys/ioctl.h>
1079 #include <termios.h>
1080 #include <fcntl.h>
1081 #include <string.h>
1082 #include <sys/stat.h>
1083 #include <sys/types.h>
1084 #include <sys/wait.h>
1085 #include <unistd.h>
1086 #if defined (__sun__)
1087 # include <sys/stropts.h>
1088 #endif
1089 #if defined (BSD) || defined (__sun__)
1090 # include <sys/signal.h>
1091 #endif
1092 #if defined (__hpux__)
1093 # include <sys/termio.h>
1094 # include <sys/stropts.h>
1095 #endif
1097 #define CDISABLE _POSIX_VDISABLE
1099 /* On HP-UX and Sun system, there is a bzero function but with a different
1100 signature. Use memset instead */
1101 #if defined (__hpux__) || defined (__sun__) || defined (_AIX)
1102 # define bzero(s,n) memset (s,0,n)
1103 #endif
1105 /* POSIX does not specify how to open the master side of a terminal.Several
1106 methods are available (system specific):
1107 1- using a cloning device (USE_CLONE_DEVICE)
1108 2- getpt (USE_GETPT)
1109 3- openpty (USE_OPENPTY)
1111 When using the cloning device method, the macro USE_CLONE_DEVICE should
1112 contains a full path to the adequate device.
1114 When a new system is about to be supported, one of the previous macro should
1115 be set otherwise allocate_pty_desc will return an error
1118 /* Configurable part */
1119 #if defined (__APPLE__) || defined (BSD)
1120 #define USE_OPENPTY
1121 #elif defined (__linux__)
1122 #define USE_GETPT
1123 #elif defined (__sun__)
1124 #define USE_CLONE_DEVICE "/dev/ptmx"
1125 #elif defined (_AIX)
1126 #define USE_CLONE_DEVICE "/dev/ptc"
1127 #elif defined (__hpux__)
1128 /* On HP-UX we use the streamed version. Using the non streamed version is not
1129 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1130 issues to detect process terminations. */
1131 #define USE_CLONE_DEVICE "/dev/ptmx"
1132 #endif
1134 /* structure that holds information about the terminal used and the process
1135 connected on the slave side */
1136 typedef struct pty_desc_struct {
1137 int master_fd; /* fd of the master side if the terminal */
1138 int slave_fd; /* fd of the slave side */
1139 char slave_name[32]; /* filename of the slave side */
1140 int child_pid; /* PID of the child process connected to the slave side
1141 of the terminal */
1142 } pty_desc;
1144 /* allocate_pty_desc - allocate a pseudo terminal
1146 * PARAMETERS
1147 * out desc returned pointer to a pty_desc structure containing information
1148 * about the opened pseudo terminal
1149 * RETURN VALUE
1150 * -1 if failed
1151 * 0 if ok
1152 * COMMENTS
1153 * If the function is successful we should have at least the master side fd
1154 * and the slave side filename. On some system, the slave side will also be
1155 * opened. If this is not the case the slave side will be open once we are in
1156 * the child process (note that opening the slave side at this stage will
1157 * failed...).
1160 extern char* ptsname (int);
1162 static int
1163 allocate_pty_desc (pty_desc **desc) {
1165 pty_desc *result;
1166 int status = 0;
1167 int slave_fd = -1;
1168 int master_fd = -1;
1169 char *slave_name = NULL;
1171 #ifdef USE_GETPT
1172 master_fd = getpt ();
1173 #elif defined (USE_OPENPTY)
1174 status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1175 #elif defined (USE_CLONE_DEVICE)
1176 master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1177 #else
1178 printf ("[error]: terminal support is not configured\n");
1179 return -1;
1180 #endif
1182 /* at this stage we should have the master side fd and status should be 0 */
1183 if (status != 0 || master_fd < 0)
1185 /* If this is not the case close all opened files and return -1 */
1186 printf ("[error]: cannot allocate master side of the pty\n");
1187 if (master_fd >= 0) close (master_fd);
1188 if (slave_fd >= 0) close (slave_fd);
1189 *desc = NULL;
1190 return -1;
1193 /* retrieve the file name of the slave side if necessary */
1194 if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1196 /* Now we should have slave file name */
1197 if (slave_name == NULL)
1199 /* If not the case close any opened file and return - 1 */
1200 printf ("[error]: cannot allocate slave side of the pty\n");
1201 if (master_fd >= 0) close (master_fd);
1202 if (slave_fd >= 0) close (slave_fd);
1203 *desc = NULL;
1204 return -1;
1207 #if !defined(__rtems__)
1208 /* grant access to the slave side */
1209 grantpt (master_fd);
1210 /* unlock the terminal */
1211 unlockpt (master_fd);
1212 #endif
1214 /* set desc and return 0 */
1215 result = malloc (sizeof (pty_desc));
1216 result->master_fd = master_fd;
1217 result->slave_fd = slave_fd;
1218 /* the string returned by ptsname or _getpty is a static allocated string. So
1219 we should make a copy */
1220 strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1221 result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1222 result->child_pid = -1;
1223 *desc=result;
1224 return 0;
1227 /* some utility macro that make the code of child_setup_tty easier to read */
1228 #define __enable(a, b) ((a) |= (b))
1229 #define __disable(a, b) ((a) &= ~(b))
1231 /* some properties do not exist on all systems. Set their value to 0 in that
1232 case */
1233 #ifndef IUCLC
1234 #define IUCLC 0
1235 #endif
1236 #ifndef OLCUC
1237 #define OLCUC 0
1238 #endif
1239 #ifndef NLDLY
1240 #define NLDLY 0
1241 #define CRDLY 0
1242 #define TABDLY 0
1243 #define BSDLY 0
1244 #define VTDLY 0
1245 #define FFDLY 0
1246 #endif
1248 /* child_setup_tty - set terminal properties
1250 * PARAMETERS
1251 * file descriptor of the slave side of the terminal
1253 * RETURN VALUE
1254 * 0 if success, any other value if failed.
1256 * COMMENTS
1257 * None
1259 static int
1260 child_setup_tty (int fd)
1262 struct termios s;
1263 int status;
1265 /* ensure that s is filled with 0 */
1266 bzero (&s, sizeof (s));
1268 /* Get the current terminal settings */
1269 status = tcgetattr (fd, &s);
1270 if (status != 0) return -1;
1272 /* Adjust input modes */
1273 __disable (s.c_iflag, IUCLC); /* don't transform to lower case */
1274 __disable (s.c_iflag, ISTRIP); /* don't delete 8th bit */
1276 /* Adjust output modes */
1277 __enable (s.c_oflag, OPOST); /* enable postprocessing */
1278 __disable (s.c_oflag, ONLCR); /* don't map LF to CR-LF */
1279 __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1280 /* disable delays */
1281 __disable (s.c_oflag, OLCUC); /* don't transform to upper case */
1283 /* Adjust control modes */
1284 s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1286 /* Adjust local modes */
1287 __disable (s.c_lflag, ECHO); /* disable echo */
1288 __enable (s.c_lflag, ISIG); /* enable signals */
1289 __enable (s.c_lflag, ICANON); /* erase/kill/eof processing */
1291 /* Adjust control characters */
1292 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1293 otherwise send_signal_via_characters will fail */
1294 s.c_cc[VEOF] = 04; /* insure that EOF is Control-D */
1295 s.c_cc[VERASE] = CDISABLE; /* disable erase processing */
1296 s.c_cc[VKILL] = CDISABLE; /* disable kill processing */
1297 s.c_cc[VQUIT] = 28; /* Control-\ */
1298 s.c_cc[VINTR] = 03; /* Control-C */
1299 s.c_cc[VEOL] = CDISABLE;
1300 s.c_cc[VSUSP] = 26; /* Control-Z */
1302 /* push our changes */
1303 status = tcsetattr (fd, TCSADRAIN, &s);
1304 return status;
1307 /* __gnat_setup_communication - interface to the external world. Should be
1308 * called before forking. On Unixes this function only call allocate_pty_desc.
1309 * The Windows implementation (in different part of this file) is very
1310 * different.
1312 * PARAMETERS
1313 * out desc returned pointer to a pty_desc structure
1314 * RETURN VALUE
1315 * 0 if success, -1 otherwise
1317 int __gnat_setup_communication (pty_desc** desc) {
1318 return allocate_pty_desc (desc);
1321 /* __gnat_setup_parent_communication - interface to the external world. Should
1322 * be called after forking in the parent process
1324 * PARAMETERS
1325 * out in_fd
1326 out out_fd
1327 out err_fd fds corresponding to the parent side of the
1328 terminal
1329 in pid_out child process pid
1330 * RETRUN VALUE
1333 void
1334 __gnat_setup_parent_communication
1335 (pty_desc *desc,
1336 int* in_fd, /* input */
1337 int* out_fd, /* output */
1338 int* err_fd, /* error */
1339 int* pid_out)
1342 *in_fd = desc->master_fd;
1343 *out_fd= desc->master_fd;
1344 *err_fd= desc->master_fd;
1345 desc->child_pid = *pid_out;
1348 /* __gnat_setup_winsize - Sets up the size of the terminal
1349 * This lets the process know the size of the terminal
1352 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1353 #ifdef TIOCGWINSZ
1354 struct winsize s;
1355 s.ws_row = (unsigned short)rows;
1356 s.ws_col = (unsigned short)columns;
1357 s.ws_xpixel = 0;
1358 s.ws_ypixel = 0;
1359 ioctl (desc->master_fd, TIOCSWINSZ, &s);
1360 #ifdef SIGWINCH
1361 if (desc->child_pid > 0) {
1362 /* Let the process know about the change in size */
1363 kill (desc->child_pid, SIGWINCH);
1365 #endif
1366 #endif
1369 /* __gnat_setup_child_communication - interface to external world. Should be
1370 * called after forking in the child process. On Unixes, this function
1371 * first adjust the line setting, set standard output, input and error and
1372 * then spawn the program.
1374 * PARAMETERS
1375 * desc a pty_desc structure containing the pty parameters
1376 * new_argv argv of the program to be spawned
1377 * RETURN VALUE
1378 * this function should not return
1381 __gnat_setup_child_communication
1382 (pty_desc *desc,
1383 char **new_argv,
1384 int Use_Pipes)
1386 int status;
1387 int pid = getpid ();
1389 setsid ();
1391 /* open the slave side of the terminal if necessary */
1392 if (desc->slave_fd == -1)
1393 #if defined (_AIX)
1394 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1395 then we might have some processes hanging on I/O system calls. Not sure
1396 we can do that for all platforms so do it only on AIX for the moment.
1397 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1398 reading on the slave fd, in case there is no data available, if O_NDELAY
1399 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1400 that interactive programs such as GDB prefer the O_NDELAY behavior.
1401 We chose O_NONBLOCK because it allows us to make the distinction
1402 between a true EOF and an EOF returned because there is no data
1403 available to be read. */
1404 desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1405 #else
1406 desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1407 #endif
1409 #if defined (__sun__) || defined (__hpux__)
1410 /* On systems such as Solaris we are using stream. We need to push the right
1411 "modules" in order to get the expected terminal behaviors. Otherwise
1412 functionalities such as termios are not available. */
1413 ioctl (desc->slave_fd, I_PUSH, "ptem");
1414 ioctl (desc->slave_fd, I_PUSH, "ldterm");
1415 ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1416 #endif
1418 #ifdef TIOCSCTTY
1419 /* make the tty the controlling terminal */
1420 status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1421 #endif
1423 /* adjust tty settings */
1424 child_setup_tty (desc->slave_fd);
1425 __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1427 /* stdin, stdout and stderr should be now our tty */
1428 dup2 (desc->slave_fd, 0);
1429 dup2 (desc->slave_fd, 1);
1430 dup2 (desc->slave_fd, 2);
1431 if (desc->slave_fd > 2) close (desc->slave_fd);
1433 /* adjust process group settings */
1434 status = setpgid (pid, pid);
1435 status = tcsetpgrp (0, pid);
1437 /* launch the program */
1438 execvp (new_argv[0], new_argv);
1440 /* return the pid */
1441 return pid;
1444 /* send_signal_via_characters - Send a characters that will trigger a signal
1445 * in the child process.
1447 * PARAMETERS
1448 * desc a pty_desc structure containing terminal information
1449 * int a signal number
1450 * RETURN VALUE
1451 * None
1453 static void
1454 send_signal_via_characters
1455 (pty_desc *desc,
1456 int signal_number)
1458 char ctrl_c = 03;
1459 char ctrl_backslash = 28;
1460 char ctrl_Z = 26;
1462 switch (signal_number)
1464 case SIGINT:
1465 write (desc->master_fd, &ctrl_c, 1); return;
1466 case SIGQUIT:
1467 write (desc->master_fd, &ctrl_backslash, 1); return;
1468 case SIGTSTP:
1469 write (desc->master_fd, &ctrl_Z, 1); return;
1473 /* __gnat_interrupt_process - interrupt the child process
1475 * PARAMETERS
1476 * desc a pty_desc structure
1479 __gnat_interrupt_process (pty_desc *desc)
1481 send_signal_via_characters (desc, SIGINT);
1482 return 0;
1485 /* __gnat_interrupt_pid - interrupt a process group
1487 * PARAMETERS
1488 * pid pid of the process to interrupt
1491 __gnat_interrupt_pid (int pid)
1493 kill (-pid, SIGINT);
1494 return 0;
1497 /* __gnat_terminate_process - kill a child process
1499 * PARAMETERS
1500 * desc pty_desc structure
1502 int __gnat_terminate_process (pty_desc *desc)
1504 return kill (desc->child_pid, SIGKILL);
1507 /* __gnat_tty_waitpid - wait for the child process to die
1509 * PARAMETERS
1510 * desc pty_desc structure
1511 * RETURN VALUE
1512 * exit status of the child process
1515 __gnat_tty_waitpid (pty_desc *desc)
1517 int status = 0;
1518 waitpid (desc->child_pid, &status, 0);
1519 return WEXITSTATUS (status);
1522 /* __gnat_tty_supported - Are tty supported ?
1524 * RETURN VALUE
1525 * always 1 on Unix systems
1528 __gnat_tty_supported (void)
1530 return 1;
1533 /* __gnat_free_process - free a pty_desc structure
1535 * PARAMETERS
1536 * in out desc: a pty desc structure
1538 void
1539 __gnat_free_process (pty_desc** desc)
1541 free (*desc);
1542 *desc = NULL;
1545 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1546 void
1547 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1549 *ret = 0;
1552 /* __gnat_reset_tty - reset line setting
1554 * PARAMETERS
1555 * desc: a pty_desc structure
1557 void
1558 __gnat_reset_tty (pty_desc* desc)
1560 child_setup_tty (desc->master_fd);
1563 /* __gnat_new_tty - allocate a new terminal
1565 * RETURN VALUE
1566 * a pty_desc structure
1568 pty_desc *
1569 __gnat_new_tty (void)
1571 int status;
1572 pty_desc* desc;
1573 status = allocate_pty_desc (&desc);
1574 child_setup_tty (desc->master_fd);
1575 return desc;
1578 /* __gnat_close_tty - close a terminal
1580 * PARAMETERS
1581 * desc a pty_desc strucure
1583 void __gnat_close_tty (pty_desc* desc)
1585 if (desc->master_fd >= 0) close (desc->master_fd);
1586 if (desc->slave_fd >= 0) close (desc->slave_fd);
1589 /* __gnat_tty_name - return slave side device name
1591 * PARAMETERS
1592 * desc a pty_desc strucure
1593 * RETURN VALUE
1594 * a string
1596 char *
1597 __gnat_tty_name (pty_desc* desc)
1599 return desc->slave_name;
1602 /* __gnat_tty_name - return master side fd
1604 * PARAMETERS
1605 * desc a pty_desc strucure
1606 * RETURN VALUE
1607 * a fd
1610 __gnat_tty_fd (pty_desc* desc)
1612 return desc->master_fd;
1615 #endif /* WIN32 */