PR c/79855: add full stop to store merging param descriptions
[official-gcc.git] / gcc / ada / terminals.c
blob35cd7430bb8a967bd659f5a4b386a2585684c973
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-2016, 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__) || defined(__DJGPP__)
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;
293 case IMAGE_SUBSYSTEM_NATIVE:
294 return 1;
296 case IMAGE_SUBSYSTEM_WINDOWS_GUI:
297 return 1;
299 case IMAGE_SUBSYSTEM_WINDOWS_CUI:
300 return 0;
302 case IMAGE_SUBSYSTEM_OS2_CUI:
303 return 0;
305 case IMAGE_SUBSYSTEM_POSIX_CUI:
306 return 0;
308 default:
309 /* Unknown, return GUI app to be preservative: if yes, it will be
310 correctly launched, if no, it will be launched, and a console will
311 be also displayed, which is not a big deal */
312 return 1;
317 static DWORD
318 AbsoluteSeek (HANDLE hFile, DWORD offset)
320 DWORD newOffset;
322 newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
324 if (newOffset == 0xFFFFFFFF)
325 return -1;
326 else
327 return newOffset;
330 static VOID
331 ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
333 DWORD bytes;
335 if (!ReadFile(hFile, buffer, size, &bytes, NULL))
337 size = 0;
338 return;
340 else if (size != bytes)
342 return;
346 static int
347 nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
349 STARTUPINFO start;
350 SECURITY_ATTRIBUTES sec_attrs;
351 SECURITY_DESCRIPTOR sec_desc;
352 DWORD flags;
353 char dir[ MAXPATHLEN ];
354 int pid;
355 int is_gui, use_cmd;
356 char *cmdline, *parg, **targ;
357 int do_quoting = 0;
358 char escape_char;
359 int arglen;
361 /* we have to do some conjuring here to put argv and envp into the
362 form CreateProcess wants... argv needs to be a space separated/null
363 terminated list of parameters, and envp is a null
364 separated/double-null terminated list of parameters.
366 Additionally, zero-length args and args containing whitespace or
367 quote chars need to be wrapped in double quotes - for this to work,
368 embedded quotes need to be escaped as well. The aim is to ensure
369 the child process reconstructs the argv array we start with
370 exactly, so we treat quotes at the beginning and end of arguments
371 as embedded quotes.
373 Note that using backslash to escape embedded quotes requires
374 additional special handling if an embedded quote is already
375 preceded by backslash, or if an arg requiring quoting ends with
376 backslash. In such cases, the run of escape characters needs to be
377 doubled. For consistency, we apply this special handling as long
378 as the escape character is not quote.
380 Since we have no idea how large argv and envp are likely to be we
381 figure out list lengths on the fly and allocate them. */
383 if (!NILP (Vw32_quote_process_args))
385 do_quoting = 1;
386 /* Override escape char by binding w32-quote-process-args to
387 desired character, or use t for auto-selection. */
388 if (INTEGERP (Vw32_quote_process_args))
389 escape_char = XINT (Vw32_quote_process_args);
390 else
391 escape_char = '\\';
394 /* do argv... */
395 arglen = 0;
396 targ = argv;
397 while (*targ)
399 char *p = *targ;
400 int need_quotes = 0;
401 int escape_char_run = 0;
403 if (*p == 0)
404 need_quotes = 1;
405 for ( ; *p; p++)
407 if (*p == '"')
409 /* allow for embedded quotes to be escaped */
410 arglen++;
411 need_quotes = 1;
412 /* handle the case where the embedded quote is already escaped */
413 if (escape_char_run > 0)
415 /* To preserve the arg exactly, we need to double the
416 preceding escape characters (plus adding one to
417 escape the quote character itself). */
418 arglen += escape_char_run;
421 else if (*p == ' ' || *p == '\t')
423 need_quotes = 1;
426 if (*p == escape_char && escape_char != '"')
427 escape_char_run++;
428 else
429 escape_char_run = 0;
431 if (need_quotes)
433 arglen += 2;
434 /* handle the case where the arg ends with an escape char - we
435 must not let the enclosing quote be escaped. */
436 if (escape_char_run > 0)
437 arglen += escape_char_run;
439 arglen += strlen (*targ) + 1;
440 targ++;
443 is_gui = is_gui_app (argv[0]);
444 use_cmd = FALSE;
446 if (is_gui == -1) {
447 /* could not determine application type. Try launching with "cmd /c" */
448 is_gui = FALSE;
449 arglen += 7;
450 use_cmd = TRUE;
453 cmdline = (char*)malloc (arglen + 1);
454 targ = argv;
455 parg = cmdline;
457 if (use_cmd == TRUE) {
458 strcpy (parg, "cmd /c ");
459 parg += 7;
462 while (*targ)
464 char * p = *targ;
465 int need_quotes = 0;
467 if (*p == 0)
468 need_quotes = 1;
470 if (do_quoting)
472 for ( ; *p; p++)
473 if (*p == ' ' || *p == '\t' || *p == '"')
474 need_quotes = 1;
476 if (need_quotes)
478 int escape_char_run = 0;
479 char * first;
480 char * last;
482 p = *targ;
483 first = p;
484 last = p + strlen (p) - 1;
485 *parg++ = '"';
486 for ( ; *p; p++)
488 if (*p == '"')
490 /* double preceding escape chars if any */
491 while (escape_char_run > 0)
493 *parg++ = escape_char;
494 escape_char_run--;
496 /* escape all quote chars, even at beginning or end */
497 *parg++ = escape_char;
499 *parg++ = *p;
501 if (*p == escape_char && escape_char != '"')
502 escape_char_run++;
503 else
504 escape_char_run = 0;
506 /* double escape chars before enclosing quote */
507 while (escape_char_run > 0)
509 *parg++ = escape_char;
510 escape_char_run--;
512 *parg++ = '"';
514 else
516 strcpy (parg, *targ);
517 parg += strlen (*targ);
519 *parg++ = ' ';
520 targ++;
522 *--parg = '\0';
524 memset (&start, 0, sizeof (start));
525 start.cb = sizeof (start);
527 if (process->usePipe == TRUE) {
528 start.dwFlags = STARTF_USESTDHANDLES;
529 start.hStdInput = process->w_forkin;
530 start.hStdOutput = process->w_forkout;
531 /* child's stderr is always redirected to outfd */
532 start.hStdError = process->w_forkout;
533 } else {
534 start.dwFlags = STARTF_USESTDHANDLES;
535 /* We only need to redirect stderr/stdout here. Stdin will be forced to
536 the spawned process console by explaunch */
537 start.hStdInput = NULL;
538 start.hStdOutput = process->w_forkout;
539 start.hStdError = process->w_forkout;
542 /* Explicitly specify no security */
543 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
544 goto EH_Fail;
545 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
546 goto EH_Fail;
547 sec_attrs.nLength = sizeof (sec_attrs);
548 sec_attrs.lpSecurityDescriptor = &sec_desc;
549 sec_attrs.bInheritHandle = FALSE;
551 /* creating a new console allow easier close. Do not use
552 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
553 flags = CREATE_NEW_CONSOLE;
554 if (NILP (Vw32_start_process_inherit_error_mode))
555 flags |= CREATE_DEFAULT_ERROR_MODE;
557 /* if app is not a gui application, hide the console */
558 if (is_gui == FALSE) {
559 start.dwFlags |= STARTF_USESHOWWINDOW;
560 start.wShowWindow = SW_HIDE;
563 /* Set initial directory to null character to use current directory */
564 if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
565 flags, env, NULL, &start, &process->procinfo))
566 goto EH_Fail;
568 pid = (int) process->procinfo.hProcess;
569 process->pid=pid;
571 return pid;
573 EH_Fail:
574 return -1;
577 /*************************
578 ** __gnat_send_header ()
579 *************************/
581 #define EXP_SLAVE_CREATE 'c'
582 #define EXP_SLAVE_KEY 'k'
583 #define EXP_SLAVE_MOUSE 'm'
584 #define EXP_SLAVE_WRITE 'w'
585 #define EXP_SLAVE_KILL 'x'
587 #define EXP_KILL_TERMINATE 0x1
588 #define EXP_KILL_CTRL_C 0x2
589 #define EXP_KILL_CTRL_BREAK 0x4
591 void
592 __gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
594 if (p->usePipe == FALSE) {
595 header[0] = EXP_SLAVE_WRITE;
596 header[1] = size & 0xff;
597 header[2] = (size & 0xff00) >> 8;
598 header[3] = (size & 0xff0000) >> 16;
599 header[4] = (size & 0xff000000) >> 24;
600 *ret = 1;
601 } else {
602 *ret = 0;
606 /**********************************
607 ** __gnat_setup_communication ()
608 **********************************/
611 __gnat_setup_communication (struct TTY_Process** process_out) /* output param */
613 struct TTY_Process* process;
615 process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
616 ZeroMemory (process, sizeof (struct TTY_Process));
617 *process_out = process;
619 return 0;
622 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
625 __gnat_setup_child_communication
626 (struct TTY_Process* process,
627 char** argv,
628 int Use_Pipes)
630 int cpid;
631 HANDLE parent;
632 SECURITY_ATTRIBUTES sec_attrs;
633 char slavePath [MAX_PATH];
634 char **nargv;
635 int argc;
636 int i;
637 char pipeNameIn[100];
638 HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
640 parent = GetCurrentProcess ();
642 /* Set inheritance for the pipe handles */
643 sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
644 sec_attrs.bInheritHandle = TRUE;
645 sec_attrs.lpSecurityDescriptor = NULL;
647 if (Use_Pipes) {
648 /* Create in and out pipes */
649 if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
650 report_file_error ("Creation of child's IN handle", Qnil);
651 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
652 report_file_error ("Creation of child's OUT handle", Qnil);
654 /* Do not inherit the parent's side of the pipes */
655 SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
656 SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
658 /* use native argv */
659 nargv = argv;
660 process->usePipe = TRUE;
662 } else {
663 static int pipeNameId = 0;
665 process->w_infd = NULL;
667 /* We create a named pipe for Input, as we handle input by sending special
668 commands to the explaunch process, that uses it to feed the actual input
669 of the process */
670 sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
671 GetCurrentProcessId(), pipeNameId);
672 pipeNameId++;
674 hSlaveInDrv = CreateNamedPipe(pipeNameIn,
675 PIPE_ACCESS_OUTBOUND,
676 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
677 20000, NULL);
678 if (hSlaveInDrv == NULL) goto end;
680 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
681 report_file_error ("Creation of child's OUT handle", Qnil);
683 if (SearchPath (NULL, "explaunch.exe", NULL,
684 MAX_PATH, slavePath, NULL) == 0) goto end;
686 for (argc=0; argv[argc] != NULL; argc++) ;
687 nargv = (char **) malloc (sizeof (char*) * (argc + 3));
688 nargv[0] = slavePath;
689 nargv[1] = pipeNameIn;
691 for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
692 process->usePipe = FALSE;
695 /* Spawn the child. */
696 cpid = nt_spawnve (nargv[0], nargv, NULL, process);
698 /* close the duplicated handles passed to the child */
699 CloseHandle (process->w_forkout);
701 if (process->usePipe == TRUE) {
702 CloseHandle (process->w_forkin);
704 } else {
705 UCHAR buf[8]; /* enough space for child status info */
706 DWORD count;
707 BOOL bRet;
708 DWORD dwRet;
711 * Wait for connection with the slave driver
713 bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
714 if (bRet == FALSE) {
715 dwRet = GetLastError();
716 if (dwRet == ERROR_PIPE_CONNECTED) {
718 } else {
719 goto end;
723 process->w_infd = hSlaveInDrv;
726 * wait for slave driver to initialize before allowing user to send to it
728 bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
729 if (bRet == FALSE) {
730 cpid = -1;
733 dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
734 if (dwRet != 0) {
735 cpid = -1;
738 cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
739 process->pid = cpid;
742 if (cpid == -1)
743 /* An error occurred while trying to spawn the process. */
744 report_file_error ("Spawning child process", Qnil);
746 return cpid;
747 end:
748 if (hSlaveInDrv != NULL)
749 CloseHandle (hSlaveInDrv);
750 return -1;
753 void
754 __gnat_setup_parent_communication
755 (struct TTY_Process* process,
756 int* in,
757 int* out,
758 int* err,
759 int* pid)
761 *in = _open_osfhandle ((long) process->w_infd, 0);
762 *out = _open_osfhandle ((long) process->w_outfd, 0);
763 /* child's stderr is always redirected to outfd */
764 *err = *out;
765 *pid = process->pid;
768 typedef struct _child_process
770 HWND hwnd;
771 PROCESS_INFORMATION *procinfo;
772 } child_process;
774 /* The major and minor versions of NT. */
775 static int w32_major_version;
776 static int w32_minor_version;
778 /* Distinguish between Windows NT and Windows 95. */
779 static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
781 /* Cache information describing the NT system for later use. */
782 static void
783 cache_system_info (void)
785 union
787 struct info
789 char major;
790 char minor;
791 short platform;
792 } info;
793 DWORD data;
794 } version;
796 /* Cache the version of the operating system. */
797 version.data = GetVersion ();
798 w32_major_version = version.info.major;
799 w32_minor_version = version.info.minor;
801 if (version.info.platform & 0x8000)
802 os_subtype = OS_WIN95;
803 else
804 os_subtype = OS_NT;
807 static BOOL CALLBACK
808 find_child_console (HWND hwnd, child_process * cp)
810 DWORD thread_id;
811 DWORD process_id;
813 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
814 if (process_id == cp->procinfo->dwProcessId)
816 char window_class[32];
818 GetClassName (hwnd, window_class, sizeof (window_class));
819 if (strcmp (window_class,
820 (os_subtype == OS_WIN95)
821 ? "tty"
822 : "ConsoleWindowClass") == 0)
824 cp->hwnd = hwnd;
825 return FALSE;
828 /* keep looking */
829 return TRUE;
833 __gnat_interrupt_process (struct TTY_Process* p)
835 char buf[2];
836 DWORD written;
837 BOOL bret;
839 if (p->usePipe == TRUE) {
840 bret = FALSE;
841 } else {
842 buf[0] = EXP_SLAVE_KILL;
843 buf[1] = EXP_KILL_CTRL_C;
844 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
847 if (bret == FALSE) {
848 return __gnat_interrupt_pid (p->procinfo.dwProcessId);
850 return 0;
854 __gnat_interrupt_pid (int pid)
856 volatile child_process cp;
857 int rc = 0;
859 cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
860 cp.procinfo->dwProcessId = pid;
862 if (os_subtype == OS_UNKNOWN)
863 cache_system_info ();
865 /* Try to locate console window for process. */
866 EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
868 if (cp.hwnd)
870 BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
871 /* Retrieve Ctrl-C scancode */
872 BYTE vk_break_code = 'C';
873 BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
874 HWND foreground_window;
876 foreground_window = GetForegroundWindow ();
877 if (foreground_window)
879 /* NT 5.0, and apparently also Windows 98, will not allow
880 a Window to be set to foreground directly without the
881 user's involvement. The workaround is to attach
882 ourselves to the thread that owns the foreground
883 window, since that is the only thread that can set the
884 foreground window. */
885 DWORD foreground_thread, child_thread;
887 foreground_thread =
888 GetWindowThreadProcessId (foreground_window, NULL);
889 if (foreground_thread == GetCurrentThreadId ()
890 || !AttachThreadInput (GetCurrentThreadId (),
891 foreground_thread, TRUE))
892 foreground_thread = 0;
894 child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
895 if (child_thread == GetCurrentThreadId ()
896 || !AttachThreadInput (GetCurrentThreadId (),
897 child_thread, TRUE))
898 child_thread = 0;
900 /* Set the foreground window to the child. */
901 if (SetForegroundWindow (cp.hwnd))
903 /* Generate keystrokes as if user had typed Ctrl-Break or
904 Ctrl-C. */
905 keybd_event (VK_CONTROL, control_scan_code, 0, 0);
906 keybd_event (vk_break_code, break_scan_code,
907 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
908 keybd_event (vk_break_code, break_scan_code,
909 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
910 | KEYEVENTF_KEYUP, 0);
911 keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
913 /* Sleep for a bit to give time for the main frame to respond
914 to focus change events. */
915 Sleep (100);
917 SetForegroundWindow (foreground_window);
919 /* Detach from the foreground and child threads now that
920 the foreground switching is over. */
921 if (foreground_thread)
922 AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
923 if (child_thread)
924 AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
927 /* Ctrl-Break is NT equivalent of SIGINT. */
928 else if (!GenerateConsoleCtrlEvent
929 (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
931 errno = EINVAL;
932 rc = -1;
935 free (cp.procinfo);
936 return rc;
939 /* kill a process, as this implementation use CreateProcess on Win32 we need
940 to use Win32 TerminateProcess API */
942 __gnat_terminate_process (struct TTY_Process* p)
944 char buf[2];
945 DWORD written;
946 BOOL bret;
948 if (p->usePipe == TRUE) {
949 bret = FALSE;
950 } else {
951 buf[0] = EXP_SLAVE_KILL;
952 buf[1] = EXP_KILL_TERMINATE;
953 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
956 if (bret == FALSE) {
957 if (!TerminateProcess (p->procinfo.hProcess, 1))
958 return -1;
959 else
960 return 0;
961 } else
962 return 0;
965 /* wait for process pid to terminate and return the process status. This
966 implementation is different from the adaint.c one for Windows as it uses
967 the Win32 API instead of the C one. */
970 __gnat_tty_waitpid (struct TTY_Process* p)
972 DWORD exitcode;
973 DWORD res;
974 HANDLE proc_hand = p->procinfo.hProcess;
976 res = WaitForSingleObject (proc_hand, 0);
977 GetExitCodeProcess (proc_hand, &exitcode);
979 CloseHandle (p->procinfo.hThread);
980 CloseHandle (p->procinfo.hProcess);
982 /* No need to close the handles: they were closed on the ada side */
984 return (int) exitcode;
987 /********************************
988 ** __gnat_free_process ()
989 ********************************/
991 void
992 __gnat_free_process (struct TTY_Process** process)
994 free (*process);
995 *process = NULL;
998 /* TTY handling */
1000 typedef struct {
1001 int tty_fd; /* descriptor for the tty */
1002 char tty_name[24]; /* Name of TTY device */
1003 } TTY_Handle;
1006 __gnat_tty_supported (void)
1008 return 0;
1011 /* Return the tty name associated with p */
1013 char *
1014 __gnat_tty_name (TTY_Handle* t)
1016 return t->tty_name;
1020 __gnat_tty_fd (TTY_Handle* t)
1022 return t->tty_fd;
1025 TTY_Handle*
1026 __gnat_new_tty (void)
1028 return (TTY_Handle*)0;
1031 void
1032 __gnat_reset_tty (TTY_Handle* t)
1034 return;
1037 void
1038 __gnat_close_tty (TTY_Handle* t)
1040 free (t);
1043 void
1044 __gnat_setup_winsize (void *desc, int rows, int columns)
1048 #else /* defined(_WIN32, implementatin for all UNIXes */
1050 /* First defined some macro to identify easily some systems */
1051 #if defined (__FreeBSD__) \
1052 || defined (__OpenBSD__) \
1053 || defined (__NetBSD__) \
1054 || defined (__DragonFly__)
1055 # define BSD
1056 #endif
1058 /* Include every system header we need */
1059 #define _GNU_SOURCE
1060 #include <errno.h>
1061 #include <stdio.h>
1062 #include <stdlib.h>
1064 /* On some system termio is either absent or including it will disable termios
1065 (HP-UX) */
1066 #if !defined (__hpux__) && !defined (BSD) && !defined (__APPLE__) \
1067 && !defined (__rtems__)
1068 # include <termio.h>
1069 #endif
1071 #include <sys/ioctl.h>
1072 #include <termios.h>
1073 #include <fcntl.h>
1074 #include <string.h>
1075 #include <sys/stat.h>
1076 #include <sys/types.h>
1077 #include <sys/wait.h>
1078 #include <unistd.h>
1079 #if defined (__sun__)
1080 # include <sys/stropts.h>
1081 #endif
1082 #if defined (BSD) || defined (__sun__)
1083 # include <sys/signal.h>
1084 #endif
1085 #if defined (__hpux__)
1086 # include <sys/termio.h>
1087 # include <sys/stropts.h>
1088 #endif
1090 #define CDISABLE _POSIX_VDISABLE
1092 /* On HP-UX and Sun system, there is a bzero function but with a different
1093 signature. Use memset instead */
1094 #if defined (__hpux__) || defined (__sun__) || defined (_AIX)
1095 # define bzero(s,n) memset (s,0,n)
1096 #endif
1098 /* POSIX does not specify how to open the master side of a terminal.Several
1099 methods are available (system specific):
1100 1- using a cloning device (USE_CLONE_DEVICE)
1101 2- getpt (USE_GETPT)
1102 3- openpty (USE_OPENPTY)
1104 When using the cloning device method, the macro USE_CLONE_DEVICE should
1105 contains a full path to the adequate device.
1107 When a new system is about to be supported, one of the previous macro should
1108 be set otherwise allocate_pty_desc will return an error
1111 /* Configurable part */
1112 #if defined (__APPLE__) || defined (BSD)
1113 #define USE_OPENPTY
1114 #elif defined (__linux__)
1115 #define USE_GETPT
1116 #elif defined (__sun__)
1117 #define USE_CLONE_DEVICE "/dev/ptmx"
1118 #elif defined (_AIX)
1119 #define USE_CLONE_DEVICE "/dev/ptc"
1120 #elif defined (__hpux__)
1121 /* On HP-UX we use the streamed version. Using the non streamed version is not
1122 recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1123 issues to detect process terminations. */
1124 #define USE_CLONE_DEVICE "/dev/ptmx"
1125 #endif
1127 /* structure that holds information about the terminal used and the process
1128 connected on the slave side */
1129 typedef struct pty_desc_struct {
1130 int master_fd; /* fd of the master side if the terminal */
1131 int slave_fd; /* fd of the slave side */
1132 char slave_name[32]; /* filename of the slave side */
1133 int child_pid; /* PID of the child process connected to the slave side
1134 of the terminal */
1135 } pty_desc;
1137 /* allocate_pty_desc - allocate a pseudo terminal
1139 * PARAMETERS
1140 * out desc returned pointer to a pty_desc structure containing information
1141 * about the opened pseudo terminal
1142 * RETURN VALUE
1143 * -1 if failed
1144 * 0 if ok
1145 * COMMENTS
1146 * If the function is successful we should have at least the master side fd
1147 * and the slave side filename. On some system, the slave side will also be
1148 * opened. If this is not the case the slave side will be open once we are in
1149 * the child process (note that opening the slave side at this stage will
1150 * failed...).
1153 extern char* ptsname (int);
1155 static int
1156 allocate_pty_desc (pty_desc **desc) {
1158 pty_desc *result;
1159 int status = 0;
1160 int slave_fd = -1;
1161 int master_fd = -1;
1162 char *slave_name = NULL;
1164 #ifdef USE_GETPT
1165 master_fd = getpt ();
1166 #elif defined (USE_OPENPTY)
1167 status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1168 #elif defined (USE_CLONE_DEVICE)
1169 master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1170 #else
1171 printf ("[error]: terminal support is not configured\n");
1172 return -1;
1173 #endif
1175 /* at this stage we should have the master side fd and status should be 0 */
1176 if (status != 0 || master_fd < 0)
1178 /* If this is not the case close all opened files and return -1 */
1179 printf ("[error]: cannot allocate master side of the pty\n");
1180 if (master_fd >= 0) close (master_fd);
1181 if (slave_fd >= 0) close (slave_fd);
1182 *desc = NULL;
1183 return -1;
1186 /* retrieve the file name of the slave side if necessary */
1187 if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1189 /* Now we should have slave file name */
1190 if (slave_name == NULL)
1192 /* If not the case close any opened file and return - 1 */
1193 printf ("[error]: cannot allocate slave side of the pty\n");
1194 if (master_fd >= 0) close (master_fd);
1195 if (slave_fd >= 0) close (slave_fd);
1196 *desc = NULL;
1197 return -1;
1200 #if !defined(__rtems__)
1201 /* grant access to the slave side */
1202 grantpt (master_fd);
1203 /* unlock the terminal */
1204 unlockpt (master_fd);
1205 #endif
1207 /* set desc and return 0 */
1208 result = malloc (sizeof (pty_desc));
1209 result->master_fd = master_fd;
1210 result->slave_fd = slave_fd;
1211 /* the string returned by ptsname or _getpty is a static allocated string. So
1212 we should make a copy */
1213 strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1214 result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1215 result->child_pid = -1;
1216 *desc=result;
1217 return 0;
1220 /* some utility macro that make the code of child_setup_tty easier to read */
1221 #define __enable(a, b) ((a) |= (b))
1222 #define __disable(a, b) ((a) &= ~(b))
1224 /* some properties do not exist on all systems. Set their value to 0 in that
1225 case */
1226 #ifndef IUCLC
1227 #define IUCLC 0
1228 #endif
1229 #ifndef OLCUC
1230 #define OLCUC 0
1231 #endif
1232 #ifndef NLDLY
1233 #define NLDLY 0
1234 #define CRDLY 0
1235 #define TABDLY 0
1236 #define BSDLY 0
1237 #define VTDLY 0
1238 #define FFDLY 0
1239 #endif
1241 /* child_setup_tty - set terminal properties
1243 * PARAMETERS
1244 * file descriptor of the slave side of the terminal
1246 * RETURN VALUE
1247 * 0 if success, any other value if failed.
1249 * COMMENTS
1250 * None
1252 static int
1253 child_setup_tty (int fd)
1255 struct termios s;
1256 int status;
1258 /* ensure that s is filled with 0 */
1259 bzero (&s, sizeof (s));
1261 /* Get the current terminal settings */
1262 status = tcgetattr (fd, &s);
1263 if (status != 0) return -1;
1265 /* Adjust input modes */
1266 __disable (s.c_iflag, IUCLC); /* don't transform to lower case */
1267 __disable (s.c_iflag, ISTRIP); /* don't delete 8th bit */
1269 /* Adjust output modes */
1270 __enable (s.c_oflag, OPOST); /* enable postprocessing */
1271 __disable (s.c_oflag, ONLCR); /* don't map LF to CR-LF */
1272 __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1273 /* disable delays */
1274 __disable (s.c_oflag, OLCUC); /* don't transform to upper case */
1276 /* Adjust control modes */
1277 s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1279 /* Adjust local modes */
1280 __disable (s.c_lflag, ECHO); /* disable echo */
1281 __enable (s.c_lflag, ISIG); /* enable signals */
1282 __enable (s.c_lflag, ICANON); /* erase/kill/eof processing */
1284 /* Adjust control characters */
1285 /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1286 otherwise send_signal_via_characters will fail */
1287 s.c_cc[VEOF] = 04; /* insure that EOF is Control-D */
1288 s.c_cc[VERASE] = CDISABLE; /* disable erase processing */
1289 s.c_cc[VKILL] = CDISABLE; /* disable kill processing */
1290 s.c_cc[VQUIT] = 28; /* Control-\ */
1291 s.c_cc[VINTR] = 03; /* Control-C */
1292 s.c_cc[VEOL] = CDISABLE;
1293 s.c_cc[VSUSP] = 26; /* Control-Z */
1295 /* push our changes */
1296 status = tcsetattr (fd, TCSADRAIN, &s);
1297 return status;
1300 /* __gnat_setup_communication - interface to the external world. Should be
1301 * called before forking. On Unixes this function only call allocate_pty_desc.
1302 * The Windows implementation (in different part of this file) is very
1303 * different.
1305 * PARAMETERS
1306 * out desc returned pointer to a pty_desc structure
1307 * RETURN VALUE
1308 * 0 if success, -1 otherwise
1310 int __gnat_setup_communication (pty_desc** desc) {
1311 return allocate_pty_desc (desc);
1314 /* __gnat_setup_parent_communication - interface to the external world. Should
1315 * be called after forking in the parent process
1317 * PARAMETERS
1318 * out in_fd
1319 out out_fd
1320 out err_fd fds corresponding to the parent side of the
1321 terminal
1322 in pid_out child process pid
1323 * RETRUN VALUE
1326 void
1327 __gnat_setup_parent_communication
1328 (pty_desc *desc,
1329 int* in_fd, /* input */
1330 int* out_fd, /* output */
1331 int* err_fd, /* error */
1332 int* pid_out)
1335 *in_fd = desc->master_fd;
1336 *out_fd= desc->master_fd;
1337 *err_fd= desc->master_fd;
1338 desc->child_pid = *pid_out;
1341 /* __gnat_setup_winsize - Sets up the size of the terminal
1342 * This lets the process know the size of the terminal
1345 void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1346 #ifdef TIOCGWINSZ
1347 struct winsize s;
1348 s.ws_row = (unsigned short)rows;
1349 s.ws_col = (unsigned short)columns;
1350 s.ws_xpixel = 0;
1351 s.ws_ypixel = 0;
1352 ioctl (desc->master_fd, TIOCSWINSZ, &s);
1353 #ifdef SIGWINCH
1354 if (desc->child_pid > 0) {
1355 /* Let the process know about the change in size */
1356 kill (desc->child_pid, SIGWINCH);
1358 #endif
1359 #endif
1362 /* __gnat_setup_child_communication - interface to external world. Should be
1363 * called after forking in the child process. On Unixes, this function
1364 * first adjust the line setting, set standard output, input and error and
1365 * then spawn the program.
1367 * PARAMETERS
1368 * desc a pty_desc structure containing the pty parameters
1369 * new_argv argv of the program to be spawned
1370 * RETURN VALUE
1371 * this function should not return
1374 __gnat_setup_child_communication
1375 (pty_desc *desc,
1376 char **new_argv,
1377 int Use_Pipes)
1379 int status;
1380 int pid = getpid ();
1382 setsid ();
1384 /* open the slave side of the terminal if necessary */
1385 if (desc->slave_fd == -1)
1386 #if defined (_AIX)
1387 /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1388 then we might have some processes hanging on I/O system calls. Not sure
1389 we can do that for all platforms so do it only on AIX for the moment.
1390 On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1391 reading on the slave fd, in case there is no data available, if O_NDELAY
1392 is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1393 that interactive programs such as GDB prefer the O_NDELAY behavior.
1394 We chose O_NONBLOCK because it allows us to make the distinction
1395 between a true EOF and an EOF returned because there is no data
1396 available to be read. */
1397 desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1398 #else
1399 desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1400 #endif
1402 #if defined (__sun__) || defined (__hpux__)
1403 /* On systems such as Solaris we are using stream. We need to push the right
1404 "modules" in order to get the expected terminal behaviors. Otherwise
1405 functionalities such as termios are not available. */
1406 ioctl (desc->slave_fd, I_PUSH, "ptem");
1407 ioctl (desc->slave_fd, I_PUSH, "ldterm");
1408 ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1409 #endif
1411 #ifdef TIOCSCTTY
1412 /* make the tty the controlling terminal */
1413 if ((status = ioctl (desc->slave_fd, TIOCSCTTY, 0)) == -1)
1414 return -1;
1415 #endif
1417 /* adjust tty settings */
1418 child_setup_tty (desc->slave_fd);
1419 __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1421 /* stdin, stdout and stderr should be now our tty */
1422 dup2 (desc->slave_fd, 0);
1423 dup2 (desc->slave_fd, 1);
1424 dup2 (desc->slave_fd, 2);
1425 if (desc->slave_fd > 2) close (desc->slave_fd);
1427 /* adjust process group settings */
1428 /* ignore failures of the following two commands as the context might not
1429 * allow making those changes. */
1430 setpgid (pid, pid);
1431 tcsetpgrp (0, pid);
1433 /* launch the program */
1434 execvp (new_argv[0], new_argv);
1436 /* return the pid */
1437 return pid;
1440 /* send_signal_via_characters - Send a characters that will trigger a signal
1441 * in the child process.
1443 * PARAMETERS
1444 * desc a pty_desc structure containing terminal information
1445 * int a signal number
1446 * RETURN VALUE
1447 * None
1449 static void
1450 send_signal_via_characters
1451 (pty_desc *desc,
1452 int signal_number)
1454 char ctrl_c = 03;
1455 char ctrl_backslash = 28;
1456 char ctrl_Z = 26;
1458 switch (signal_number)
1460 case SIGINT:
1461 write (desc->master_fd, &ctrl_c, 1); return;
1462 case SIGQUIT:
1463 write (desc->master_fd, &ctrl_backslash, 1); return;
1464 case SIGTSTP:
1465 write (desc->master_fd, &ctrl_Z, 1); return;
1469 /* __gnat_interrupt_process - interrupt the child process
1471 * PARAMETERS
1472 * desc a pty_desc structure
1475 __gnat_interrupt_process (pty_desc *desc)
1477 send_signal_via_characters (desc, SIGINT);
1478 return 0;
1481 /* __gnat_interrupt_pid - interrupt a process group
1483 * PARAMETERS
1484 * pid pid of the process to interrupt
1487 __gnat_interrupt_pid (int pid)
1489 kill (-pid, SIGINT);
1490 return 0;
1493 /* __gnat_terminate_process - kill a child process
1495 * PARAMETERS
1496 * desc pty_desc structure
1498 int __gnat_terminate_process (pty_desc *desc)
1500 return kill (desc->child_pid, SIGKILL);
1503 /* __gnat_tty_waitpid - wait for the child process to die
1505 * PARAMETERS
1506 * desc pty_desc structure
1507 * RETURN VALUE
1508 * exit status of the child process
1511 __gnat_tty_waitpid (pty_desc *desc)
1513 int status = 0;
1514 waitpid (desc->child_pid, &status, 0);
1515 return WEXITSTATUS (status);
1518 /* __gnat_tty_supported - Are tty supported ?
1520 * RETURN VALUE
1521 * always 1 on Unix systems
1524 __gnat_tty_supported (void)
1526 return 1;
1529 /* __gnat_free_process - free a pty_desc structure
1531 * PARAMETERS
1532 * in out desc: a pty desc structure
1534 void
1535 __gnat_free_process (pty_desc** desc)
1537 free (*desc);
1538 *desc = NULL;
1541 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1542 void
1543 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1545 *ret = 0;
1548 /* __gnat_reset_tty - reset line setting
1550 * PARAMETERS
1551 * desc: a pty_desc structure
1553 void
1554 __gnat_reset_tty (pty_desc* desc)
1556 child_setup_tty (desc->master_fd);
1559 /* __gnat_new_tty - allocate a new terminal
1561 * RETURN VALUE
1562 * a pty_desc structure
1564 pty_desc *
1565 __gnat_new_tty (void)
1567 int status;
1568 pty_desc* desc = NULL;
1569 if ((status = allocate_pty_desc (&desc)))
1570 child_setup_tty (desc->master_fd);
1571 return desc;
1574 /* __gnat_close_tty - close a terminal
1576 * PARAMETERS
1577 * desc a pty_desc strucure
1579 void __gnat_close_tty (pty_desc* desc)
1581 if (desc->master_fd >= 0) close (desc->master_fd);
1582 if (desc->slave_fd >= 0) close (desc->slave_fd);
1585 /* __gnat_tty_name - return slave side device name
1587 * PARAMETERS
1588 * desc a pty_desc strucure
1589 * RETURN VALUE
1590 * a string
1592 char *
1593 __gnat_tty_name (pty_desc* desc)
1595 return desc->slave_name;
1598 /* __gnat_tty_name - return master side fd
1600 * PARAMETERS
1601 * desc a pty_desc strucure
1602 * RETURN VALUE
1603 * a fd
1606 __gnat_tty_fd (pty_desc* desc)
1608 return desc->master_fd;
1611 #endif /* WIN32 */