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