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