]> gcc.gnu.org Git - gcc.git/blob - gcc/ada/terminals.c
9f300514ced8d50c2e6388a1a3ef72f583ca1602
[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-2017, 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 /* First all usupported platforms. Add stubs for exported routines. */
33
34 #if defined (VMS) || defined (__vxworks) || defined (__Lynx__) \
35 || defined (__ANDROID__) || defined (__PikeOS__) || defined(__DJGPP__)
36
37 #define ATTRIBUTE_UNUSED __attribute__((unused))
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)
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
156 #define MAXPATHLEN 1024
157
158 #define NILP(x) ((x) == 0)
159 #define Qnil 0
160 #define report_file_error(x, y) fprintf (stderr, "Error: %s\n", x);
161 #define INTEGERP(x) 1
162 #define XINT(x) x
163
164 struct TTY_Process {
165 int pid; /* Number of this process */
166 PROCESS_INFORMATION procinfo;
167 HANDLE w_infd, w_outfd;
168 HANDLE w_forkin, w_forkout;
169 BOOL usePipe;
170 };
171
172 /* Control whether create_child cause the process to inherit GPS'
173 error mode setting. The default is 1, to minimize the possibility of
174 subprocesses blocking when accessing unmounted drives. */
175 static int Vw32_start_process_inherit_error_mode = 1;
176
177 /* Control whether spawnve quotes arguments as necessary to ensure
178 correct parsing by child process. Because not all uses of spawnve
179 are careful about constructing argv arrays, we make this behavior
180 conditional (off by default, since a similar operation is already done
181 in g-expect.adb by calling Normalize_Argument). */
182 static int Vw32_quote_process_args = 0;
183
184 static DWORD AbsoluteSeek(HANDLE, DWORD);
185 static VOID ReadBytes(HANDLE, LPVOID, DWORD);
186
187 #define XFER_BUFFER_SIZE 2048
188
189 /* This tell if the executable we're about to launch uses a GUI interface. */
190 /* if we can't determine it, we will return true */
191 static int
192 is_gui_app (char *exe)
193 {
194 HANDLE hImage;
195
196 DWORD bytes;
197 DWORD iSection;
198 DWORD SectionOffset;
199 DWORD CoffHeaderOffset;
200 DWORD MoreDosHeader[16];
201 CHAR *file;
202 size_t nlen;
203
204 ULONG ntSignature;
205
206 IMAGE_DOS_HEADER image_dos_header;
207 IMAGE_FILE_HEADER image_file_header;
208 IMAGE_OPTIONAL_HEADER image_optional_header;
209 IMAGE_SECTION_HEADER image_section_header;
210
211 /*
212 * Open the reference file.
213 */
214 nlen = strlen (exe);
215 file = exe;
216 if (nlen > 2) {
217 if (exe[0] == '"') {
218 /* remove quotes */
219 nlen -= 2;
220 file = malloc ((nlen + 1) * sizeof (char));
221 memcpy (file, &exe[1], nlen);
222 file [nlen] = '\0';
223 }
224 }
225 hImage = CreateFile(file,
226 GENERIC_READ,
227 FILE_SHARE_READ,
228 NULL,
229 OPEN_EXISTING,
230 FILE_ATTRIBUTE_NORMAL,
231 NULL);
232
233 if (file != exe) {
234 free (file);
235 }
236
237 if (INVALID_HANDLE_VALUE == hImage)
238 {
239 report_file_error ("Could not open exe: ", Qnil);
240 report_file_error (exe, Qnil);
241 report_file_error ("\n", Qnil);
242 CloseHandle (hImage);
243 return -1;
244 }
245
246 /*
247 * Read the MS-DOS image header.
248 */
249 ReadBytes(hImage, &image_dos_header, sizeof(IMAGE_DOS_HEADER));
250
251 if (IMAGE_DOS_SIGNATURE != image_dos_header.e_magic)
252 {
253 report_file_error("Sorry, I do not understand this file.\n", Qnil);
254 CloseHandle (hImage);
255 return -1;
256 }
257
258 /*
259 * Read more MS-DOS header. */
260 ReadBytes(hImage, MoreDosHeader, sizeof(MoreDosHeader));
261 /*
262 * Get actual COFF header.
263 */
264 CoffHeaderOffset = AbsoluteSeek(hImage, image_dos_header.e_lfanew) +
265 sizeof(ULONG);
266 if (CoffHeaderOffset < 0) {
267 CloseHandle (hImage);
268 return -1;
269 }
270
271 ReadBytes (hImage, &ntSignature, sizeof(ULONG));
272
273 if (IMAGE_NT_SIGNATURE != ntSignature)
274 {
275 report_file_error ("Missing NT signature. Unknown file type.\n", Qnil);
276 CloseHandle (hImage);
277 return -1;
278 }
279
280 SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
281 IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
282
283 ReadBytes(hImage, &image_file_header, IMAGE_SIZEOF_FILE_HEADER);
284
285 /*
286 * Read optional header.
287 */
288 ReadBytes(hImage,
289 &image_optional_header,
290 IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
291
292 CloseHandle (hImage);
293
294 switch (image_optional_header.Subsystem)
295 {
296 case IMAGE_SUBSYSTEM_UNKNOWN:
297 return 1;
298
299 case IMAGE_SUBSYSTEM_NATIVE:
300 return 1;
301
302 case IMAGE_SUBSYSTEM_WINDOWS_GUI:
303 return 1;
304
305 case IMAGE_SUBSYSTEM_WINDOWS_CUI:
306 return 0;
307
308 case IMAGE_SUBSYSTEM_OS2_CUI:
309 return 0;
310
311 case IMAGE_SUBSYSTEM_POSIX_CUI:
312 return 0;
313
314 default:
315 /* Unknown, return GUI app to be preservative: if yes, it will be
316 correctly launched, if no, it will be launched, and a console will
317 be also displayed, which is not a big deal */
318 return 1;
319 }
320
321 }
322
323 static DWORD
324 AbsoluteSeek (HANDLE hFile, DWORD offset)
325 {
326 DWORD newOffset;
327
328 newOffset = SetFilePointer (hFile, offset, NULL, FILE_BEGIN);
329
330 if (newOffset == 0xFFFFFFFF)
331 return -1;
332 else
333 return newOffset;
334 }
335
336 static VOID
337 ReadBytes (HANDLE hFile, LPVOID buffer, DWORD size)
338 {
339 DWORD bytes;
340
341 if (!ReadFile(hFile, buffer, size, &bytes, NULL))
342 {
343 size = 0;
344 return;
345 }
346 else if (size != bytes)
347 {
348 return;
349 }
350 }
351
352 static int
353 nt_spawnve (char *exe, char **argv, char *env, struct TTY_Process *process)
354 {
355 STARTUPINFO start;
356 SECURITY_ATTRIBUTES sec_attrs;
357 SECURITY_DESCRIPTOR sec_desc;
358 DWORD flags;
359 char dir[ MAXPATHLEN ];
360 int pid;
361 int is_gui, use_cmd;
362 char *cmdline, *parg, **targ;
363 int do_quoting = 0;
364 char escape_char;
365 int arglen;
366
367 /* we have to do some conjuring here to put argv and envp into the
368 form CreateProcess wants... argv needs to be a space separated/null
369 terminated list of parameters, and envp is a null
370 separated/double-null terminated list of parameters.
371
372 Additionally, zero-length args and args containing whitespace or
373 quote chars need to be wrapped in double quotes - for this to work,
374 embedded quotes need to be escaped as well. The aim is to ensure
375 the child process reconstructs the argv array we start with
376 exactly, so we treat quotes at the beginning and end of arguments
377 as embedded quotes.
378
379 Note that using backslash to escape embedded quotes requires
380 additional special handling if an embedded quote is already
381 preceded by backslash, or if an arg requiring quoting ends with
382 backslash. In such cases, the run of escape characters needs to be
383 doubled. For consistency, we apply this special handling as long
384 as the escape character is not quote.
385
386 Since we have no idea how large argv and envp are likely to be we
387 figure out list lengths on the fly and allocate them. */
388
389 if (!NILP (Vw32_quote_process_args))
390 {
391 do_quoting = 1;
392 /* Override escape char by binding w32-quote-process-args to
393 desired character, or use t for auto-selection. */
394 if (INTEGERP (Vw32_quote_process_args))
395 escape_char = XINT (Vw32_quote_process_args);
396 else
397 escape_char = '\\';
398 }
399
400 /* do argv... */
401 arglen = 0;
402 targ = argv;
403 while (*targ)
404 {
405 char *p = *targ;
406 int need_quotes = 0;
407 int escape_char_run = 0;
408
409 if (*p == 0)
410 need_quotes = 1;
411 for ( ; *p; p++)
412 {
413 if (*p == '"')
414 {
415 /* allow for embedded quotes to be escaped */
416 arglen++;
417 need_quotes = 1;
418 /* handle the case where the embedded quote is already escaped */
419 if (escape_char_run > 0)
420 {
421 /* To preserve the arg exactly, we need to double the
422 preceding escape characters (plus adding one to
423 escape the quote character itself). */
424 arglen += escape_char_run;
425 }
426 }
427 else if (*p == ' ' || *p == '\t')
428 {
429 need_quotes = 1;
430 }
431
432 if (*p == escape_char && escape_char != '"')
433 escape_char_run++;
434 else
435 escape_char_run = 0;
436 }
437 if (need_quotes)
438 {
439 arglen += 2;
440 /* handle the case where the arg ends with an escape char - we
441 must not let the enclosing quote be escaped. */
442 if (escape_char_run > 0)
443 arglen += escape_char_run;
444 }
445 arglen += strlen (*targ) + 1;
446 targ++;
447 }
448
449 is_gui = is_gui_app (argv[0]);
450 use_cmd = FALSE;
451
452 if (is_gui == -1) {
453 /* could not determine application type. Try launching with "cmd /c" */
454 is_gui = FALSE;
455 arglen += 7;
456 use_cmd = TRUE;
457 }
458
459 cmdline = (char*)malloc (arglen + 1);
460 targ = argv;
461 parg = cmdline;
462
463 if (use_cmd == TRUE) {
464 strcpy (parg, "cmd /c ");
465 parg += 7;
466 }
467
468 while (*targ)
469 {
470 char * p = *targ;
471 int need_quotes = 0;
472
473 if (*p == 0)
474 need_quotes = 1;
475
476 if (do_quoting)
477 {
478 for ( ; *p; p++)
479 if (*p == ' ' || *p == '\t' || *p == '"')
480 need_quotes = 1;
481 }
482 if (need_quotes)
483 {
484 int escape_char_run = 0;
485 char * first;
486 char * last;
487
488 p = *targ;
489 first = p;
490 last = p + strlen (p) - 1;
491 *parg++ = '"';
492 for ( ; *p; p++)
493 {
494 if (*p == '"')
495 {
496 /* double preceding escape chars if any */
497 while (escape_char_run > 0)
498 {
499 *parg++ = escape_char;
500 escape_char_run--;
501 }
502 /* escape all quote chars, even at beginning or end */
503 *parg++ = escape_char;
504 }
505 *parg++ = *p;
506
507 if (*p == escape_char && escape_char != '"')
508 escape_char_run++;
509 else
510 escape_char_run = 0;
511 }
512 /* double escape chars before enclosing quote */
513 while (escape_char_run > 0)
514 {
515 *parg++ = escape_char;
516 escape_char_run--;
517 }
518 *parg++ = '"';
519 }
520 else
521 {
522 strcpy (parg, *targ);
523 parg += strlen (*targ);
524 }
525 *parg++ = ' ';
526 targ++;
527 }
528 *--parg = '\0';
529
530 memset (&start, 0, sizeof (start));
531 start.cb = sizeof (start);
532
533 if (process->usePipe == TRUE) {
534 start.dwFlags = STARTF_USESTDHANDLES;
535 start.hStdInput = process->w_forkin;
536 start.hStdOutput = process->w_forkout;
537 /* child's stderr is always redirected to outfd */
538 start.hStdError = process->w_forkout;
539 } else {
540 start.dwFlags = STARTF_USESTDHANDLES;
541 /* We only need to redirect stderr/stdout here. Stdin will be forced to
542 the spawned process console by explaunch */
543 start.hStdInput = NULL;
544 start.hStdOutput = process->w_forkout;
545 start.hStdError = process->w_forkout;
546 }
547
548 /* Explicitly specify no security */
549 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
550 goto EH_Fail;
551 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
552 goto EH_Fail;
553 sec_attrs.nLength = sizeof (sec_attrs);
554 sec_attrs.lpSecurityDescriptor = &sec_desc;
555 sec_attrs.bInheritHandle = FALSE;
556
557 /* creating a new console allow easier close. Do not use
558 CREATE_NEW_PROCESS_GROUP as this results in disabling Ctrl+C */
559 flags = CREATE_NEW_CONSOLE;
560 if (NILP (Vw32_start_process_inherit_error_mode))
561 flags |= CREATE_DEFAULT_ERROR_MODE;
562
563 /* if app is not a gui application, hide the console */
564 if (is_gui == FALSE) {
565 start.dwFlags |= STARTF_USESHOWWINDOW;
566 start.wShowWindow = SW_HIDE;
567 }
568
569 /* Set initial directory to null character to use current directory */
570 if (!CreateProcess (NULL, cmdline, &sec_attrs, NULL, TRUE,
571 flags, env, NULL, &start, &process->procinfo))
572 goto EH_Fail;
573
574 pid = (int) process->procinfo.hProcess;
575 process->pid=pid;
576
577 return pid;
578
579 EH_Fail:
580 return -1;
581 }
582
583 /*************************
584 ** __gnat_send_header ()
585 *************************/
586
587 #define EXP_SLAVE_CREATE 'c'
588 #define EXP_SLAVE_KEY 'k'
589 #define EXP_SLAVE_MOUSE 'm'
590 #define EXP_SLAVE_WRITE 'w'
591 #define EXP_SLAVE_KILL 'x'
592
593 #define EXP_KILL_TERMINATE 0x1
594 #define EXP_KILL_CTRL_C 0x2
595 #define EXP_KILL_CTRL_BREAK 0x4
596
597 void
598 __gnat_send_header (struct TTY_Process* p, char header[5], int size, int *ret)
599 {
600 if (p->usePipe == FALSE) {
601 header[0] = EXP_SLAVE_WRITE;
602 header[1] = size & 0xff;
603 header[2] = (size & 0xff00) >> 8;
604 header[3] = (size & 0xff0000) >> 16;
605 header[4] = (size & 0xff000000) >> 24;
606 *ret = 1;
607 } else {
608 *ret = 0;
609 }
610 }
611
612 /**********************************
613 ** __gnat_setup_communication ()
614 **********************************/
615
616 int
617 __gnat_setup_communication (struct TTY_Process** process_out) /* output param */
618 {
619 struct TTY_Process* process;
620
621 process = (struct TTY_Process*)malloc (sizeof (struct TTY_Process));
622 ZeroMemory (process, sizeof (struct TTY_Process));
623 *process_out = process;
624
625 return 0;
626 }
627
628 #define EXP_PIPE_BASENAME "\\\\.\\pipe\\ExpectPipe"
629
630 int
631 __gnat_setup_child_communication
632 (struct TTY_Process* process,
633 char** argv,
634 int Use_Pipes)
635 {
636 int cpid;
637 HANDLE parent;
638 SECURITY_ATTRIBUTES sec_attrs;
639 char slavePath [MAX_PATH];
640 char **nargv;
641 int argc;
642 int i;
643 char pipeNameIn[100];
644 HANDLE hSlaveInDrv = NULL; /* Handle to communicate with slave driver */
645
646 parent = GetCurrentProcess ();
647
648 /* Set inheritance for the pipe handles */
649 sec_attrs.nLength = sizeof (SECURITY_ATTRIBUTES);
650 sec_attrs.bInheritHandle = TRUE;
651 sec_attrs.lpSecurityDescriptor = NULL;
652
653 if (Use_Pipes) {
654 /* Create in and out pipes */
655 if (!CreatePipe (&process->w_forkin, &process->w_infd, &sec_attrs, 0))
656 report_file_error ("Creation of child's IN handle", Qnil);
657 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
658 report_file_error ("Creation of child's OUT handle", Qnil);
659
660 /* Do not inherit the parent's side of the pipes */
661 SetHandleInformation (&process->w_infd, HANDLE_FLAG_INHERIT, 0);
662 SetHandleInformation (&process->w_outfd, HANDLE_FLAG_INHERIT, 0);
663
664 /* use native argv */
665 nargv = argv;
666 process->usePipe = TRUE;
667
668 } else {
669 static int pipeNameId = 0;
670
671 process->w_infd = NULL;
672
673 /* We create a named pipe for Input, as we handle input by sending special
674 commands to the explaunch process, that uses it to feed the actual input
675 of the process */
676 sprintf(pipeNameIn, "%sIn%08x_%08x", EXP_PIPE_BASENAME,
677 GetCurrentProcessId(), pipeNameId);
678 pipeNameId++;
679
680 hSlaveInDrv = CreateNamedPipe(pipeNameIn,
681 PIPE_ACCESS_OUTBOUND,
682 PIPE_TYPE_BYTE | PIPE_WAIT, 1, 8192, 8192,
683 20000, NULL);
684 if (hSlaveInDrv == NULL) goto end;
685
686 if (!CreatePipe (&process->w_outfd, &process->w_forkout, &sec_attrs, 0))
687 report_file_error ("Creation of child's OUT handle", Qnil);
688
689 if (SearchPath (NULL, "explaunch.exe", NULL,
690 MAX_PATH, slavePath, NULL) == 0) goto end;
691
692 for (argc=0; argv[argc] != NULL; argc++) ;
693 nargv = (char **) malloc (sizeof (char*) * (argc + 3));
694 nargv[0] = slavePath;
695 nargv[1] = pipeNameIn;
696
697 for (i = 0; i <= argc; i++) nargv[i + 2] = argv[i];
698 process->usePipe = FALSE;
699 }
700
701 /* Spawn the child. */
702 cpid = nt_spawnve (nargv[0], nargv, NULL, process);
703
704 /* close the duplicated handles passed to the child */
705 CloseHandle (process->w_forkout);
706
707 if (process->usePipe == TRUE) {
708 CloseHandle (process->w_forkin);
709
710 } else {
711 UCHAR buf[8]; /* enough space for child status info */
712 DWORD count;
713 BOOL bRet;
714 DWORD dwRet;
715
716 /*
717 * Wait for connection with the slave driver
718 */
719 bRet = ConnectNamedPipe(hSlaveInDrv, NULL);
720 if (bRet == FALSE) {
721 dwRet = GetLastError();
722 if (dwRet == ERROR_PIPE_CONNECTED) {
723 ;
724 } else {
725 goto end;
726 }
727 }
728
729 process->w_infd = hSlaveInDrv;
730
731 /*
732 * wait for slave driver to initialize before allowing user to send to it
733 */
734 bRet = ReadFile(process->w_outfd, buf, 8, &count, NULL);
735 if (bRet == FALSE) {
736 cpid = -1;
737 }
738
739 dwRet = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
740 if (dwRet != 0) {
741 cpid = -1;
742 }
743
744 cpid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24);
745 process->pid = cpid;
746 }
747
748 if (cpid == -1)
749 /* An error occurred while trying to spawn the process. */
750 report_file_error ("Spawning child process", Qnil);
751
752 return cpid;
753 end:
754 if (hSlaveInDrv != NULL)
755 CloseHandle (hSlaveInDrv);
756 return -1;
757 }
758
759 void
760 __gnat_setup_parent_communication
761 (struct TTY_Process* process,
762 int* in,
763 int* out,
764 int* err,
765 int* pid)
766 {
767 *in = _open_osfhandle ((long) process->w_infd, 0);
768 *out = _open_osfhandle ((long) process->w_outfd, 0);
769 /* child's stderr is always redirected to outfd */
770 *err = *out;
771 *pid = process->pid;
772 }
773
774 typedef struct _child_process
775 {
776 HWND hwnd;
777 PROCESS_INFORMATION *procinfo;
778 } child_process;
779
780 /* The major and minor versions of NT. */
781 static int w32_major_version;
782 static int w32_minor_version;
783
784 /* Distinguish between Windows NT and Windows 95. */
785 static enum {OS_UNKNOWN, OS_WIN95, OS_NT} os_subtype = OS_UNKNOWN;
786
787 /* Cache information describing the NT system for later use. */
788 static void
789 cache_system_info (void)
790 {
791 union
792 {
793 struct info
794 {
795 char major;
796 char minor;
797 short platform;
798 } info;
799 DWORD data;
800 } version;
801
802 /* Cache the version of the operating system. */
803 version.data = GetVersion ();
804 w32_major_version = version.info.major;
805 w32_minor_version = version.info.minor;
806
807 if (version.info.platform & 0x8000)
808 os_subtype = OS_WIN95;
809 else
810 os_subtype = OS_NT;
811 }
812
813 static BOOL CALLBACK
814 find_child_console (HWND hwnd, child_process * cp)
815 {
816 DWORD thread_id;
817 DWORD process_id;
818
819 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
820 if (process_id == cp->procinfo->dwProcessId)
821 {
822 char window_class[32];
823
824 GetClassName (hwnd, window_class, sizeof (window_class));
825 if (strcmp (window_class,
826 (os_subtype == OS_WIN95)
827 ? "tty"
828 : "ConsoleWindowClass") == 0)
829 {
830 cp->hwnd = hwnd;
831 return FALSE;
832 }
833 }
834 /* keep looking */
835 return TRUE;
836 }
837
838 int
839 __gnat_interrupt_process (struct TTY_Process* p)
840 {
841 char buf[2];
842 DWORD written;
843 BOOL bret;
844
845 if (p->usePipe == TRUE) {
846 bret = FALSE;
847 } else {
848 buf[0] = EXP_SLAVE_KILL;
849 buf[1] = EXP_KILL_CTRL_C;
850 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
851 }
852
853 if (bret == FALSE) {
854 return __gnat_interrupt_pid (p->procinfo.dwProcessId);
855 }
856 return 0;
857 }
858
859 int
860 __gnat_interrupt_pid (int pid)
861 {
862 volatile child_process cp;
863 int rc = 0;
864
865 cp.procinfo = (LPPROCESS_INFORMATION) malloc (sizeof (PROCESS_INFORMATION));
866 cp.procinfo->dwProcessId = pid;
867
868 if (os_subtype == OS_UNKNOWN)
869 cache_system_info ();
870
871 /* Try to locate console window for process. */
872 EnumWindows ((WNDENUMPROC) find_child_console, (LPARAM) &cp);
873
874 if (cp.hwnd)
875 {
876 BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
877 /* Retrieve Ctrl-C scancode */
878 BYTE vk_break_code = 'C';
879 BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
880 HWND foreground_window;
881
882 foreground_window = GetForegroundWindow ();
883 if (foreground_window)
884 {
885 /* NT 5.0, and apparently also Windows 98, will not allow
886 a Window to be set to foreground directly without the
887 user's involvement. The workaround is to attach
888 ourselves to the thread that owns the foreground
889 window, since that is the only thread that can set the
890 foreground window. */
891 DWORD foreground_thread, child_thread;
892
893 foreground_thread =
894 GetWindowThreadProcessId (foreground_window, NULL);
895 if (foreground_thread == GetCurrentThreadId ()
896 || !AttachThreadInput (GetCurrentThreadId (),
897 foreground_thread, TRUE))
898 foreground_thread = 0;
899
900 child_thread = GetWindowThreadProcessId (cp.hwnd, NULL);
901 if (child_thread == GetCurrentThreadId ()
902 || !AttachThreadInput (GetCurrentThreadId (),
903 child_thread, TRUE))
904 child_thread = 0;
905
906 /* Set the foreground window to the child. */
907 if (SetForegroundWindow (cp.hwnd))
908 {
909 /* Generate keystrokes as if user had typed Ctrl-Break or
910 Ctrl-C. */
911 keybd_event (VK_CONTROL, control_scan_code, 0, 0);
912 keybd_event (vk_break_code, break_scan_code,
913 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
914 keybd_event (vk_break_code, break_scan_code,
915 (vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
916 | KEYEVENTF_KEYUP, 0);
917 keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
918
919 /* Sleep for a bit to give time for the main frame to respond
920 to focus change events. */
921 Sleep (100);
922
923 SetForegroundWindow (foreground_window);
924 }
925 /* Detach from the foreground and child threads now that
926 the foreground switching is over. */
927 if (foreground_thread)
928 AttachThreadInput (GetCurrentThreadId (), foreground_thread, FALSE);
929 if (child_thread)
930 AttachThreadInput (GetCurrentThreadId (), child_thread, FALSE);
931 }
932 }
933 /* Ctrl-Break is NT equivalent of SIGINT. */
934 else if (!GenerateConsoleCtrlEvent
935 (CTRL_BREAK_EVENT, cp.procinfo->dwProcessId))
936 {
937 errno = EINVAL;
938 rc = -1;
939 }
940
941 free (cp.procinfo);
942 return rc;
943 }
944
945 /* kill a process, as this implementation use CreateProcess on Win32 we need
946 to use Win32 TerminateProcess API */
947 int
948 __gnat_terminate_process (struct TTY_Process* p)
949 {
950 char buf[2];
951 DWORD written;
952 BOOL bret;
953
954 if (p->usePipe == TRUE) {
955 bret = FALSE;
956 } else {
957 buf[0] = EXP_SLAVE_KILL;
958 buf[1] = EXP_KILL_TERMINATE;
959 bret = WriteFile (p->w_infd, buf, 2, &written, NULL);
960 }
961
962 if (bret == FALSE) {
963 if (!TerminateProcess (p->procinfo.hProcess, 1))
964 return -1;
965 else
966 return 0;
967 } else
968 return 0;
969 }
970
971 typedef struct {
972 DWORD dwProcessId;
973 HANDLE hwnd;
974 } pid_struct;
975
976 static BOOL CALLBACK
977 find_process_handle (HWND hwnd, pid_struct * ps)
978 {
979 DWORD thread_id;
980 DWORD process_id;
981
982 thread_id = GetWindowThreadProcessId (hwnd, &process_id);
983 if (process_id == ps->dwProcessId)
984 {
985 ps->hwnd = hwnd;
986 return FALSE;
987 }
988 /* keep looking */
989 return TRUE;
990 }
991
992 int
993 __gnat_terminate_pid (int pid)
994 {
995 pid_struct ps;
996
997 ps.dwProcessId = pid;
998 ps.hwnd = 0;
999 EnumWindows ((WNDENUMPROC) find_process_handle, (LPARAM) &ps);
1000
1001 if (ps.hwnd)
1002 {
1003 if (!TerminateProcess (ps.hwnd, 1))
1004 return -1;
1005 else
1006 return 0;
1007 }
1008
1009 return -1;
1010 }
1011
1012 /* wait for process pid to terminate and return the process status. This
1013 implementation is different from the adaint.c one for Windows as it uses
1014 the Win32 API instead of the C one. */
1015
1016 int
1017 __gnat_tty_waitpid (struct TTY_Process* p)
1018 {
1019 DWORD exitcode;
1020 DWORD res;
1021 HANDLE proc_hand = p->procinfo.hProcess;
1022
1023 res = WaitForSingleObject (proc_hand, 0);
1024 GetExitCodeProcess (proc_hand, &exitcode);
1025
1026 CloseHandle (p->procinfo.hThread);
1027 CloseHandle (p->procinfo.hProcess);
1028
1029 /* No need to close the handles: they were closed on the ada side */
1030
1031 return (int) exitcode;
1032 }
1033
1034 /********************************
1035 ** __gnat_free_process ()
1036 ********************************/
1037
1038 void
1039 __gnat_free_process (struct TTY_Process** process)
1040 {
1041 free (*process);
1042 *process = NULL;
1043 }
1044
1045 /* TTY handling */
1046
1047 typedef struct {
1048 int tty_fd; /* descriptor for the tty */
1049 char tty_name[24]; /* Name of TTY device */
1050 } TTY_Handle;
1051
1052 int
1053 __gnat_tty_supported (void)
1054 {
1055 return 0;
1056 }
1057
1058 /* Return the tty name associated with p */
1059
1060 char *
1061 __gnat_tty_name (TTY_Handle* t)
1062 {
1063 return t->tty_name;
1064 }
1065
1066 int
1067 __gnat_tty_fd (TTY_Handle* t)
1068 {
1069 return t->tty_fd;
1070 }
1071
1072 TTY_Handle*
1073 __gnat_new_tty (void)
1074 {
1075 return (TTY_Handle*)0;
1076 }
1077
1078 void
1079 __gnat_reset_tty (TTY_Handle* t)
1080 {
1081 return;
1082 }
1083
1084 void
1085 __gnat_close_tty (TTY_Handle* t)
1086 {
1087 free (t);
1088 }
1089
1090 void
1091 __gnat_setup_winsize (void *desc, int rows, int columns)
1092 {
1093 }
1094
1095 #else /* defined(_WIN32, implementatin for all UNIXes */
1096
1097 /* First defined some macro to identify easily some systems */
1098 #if defined (__FreeBSD__) \
1099 || defined (__OpenBSD__) \
1100 || defined (__NetBSD__) \
1101 || defined (__DragonFly__)
1102 # define BSD
1103 #endif
1104
1105 /* Include every system header we need */
1106 #define _GNU_SOURCE
1107 #include <errno.h>
1108 #include <stdio.h>
1109 #include <stdlib.h>
1110
1111 /* On some system termio is either absent or including it will disable termios
1112 (HP-UX) */
1113 #if !defined (__hpux__) && !defined (BSD) && !defined (__APPLE__) \
1114 && !defined (__rtems__) && !defined (__QNXNTO__)
1115 # include <termio.h>
1116 #endif
1117
1118 #include <sys/ioctl.h>
1119 #include <termios.h>
1120 #include <fcntl.h>
1121 #include <string.h>
1122 #include <sys/stat.h>
1123 #include <sys/types.h>
1124 #include <sys/wait.h>
1125 #include <unistd.h>
1126 #if defined (__sun__)
1127 # include <sys/stropts.h>
1128 #endif
1129 #if defined (BSD) || defined (__sun__)
1130 # include <sys/signal.h>
1131 #endif
1132 #if defined (__hpux__)
1133 # include <sys/termio.h>
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)
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 return -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 /* return the pid */
1484 return pid;
1485 }
1486
1487 /* send_signal_via_characters - Send a characters that will trigger a signal
1488 * in the child process.
1489 *
1490 * PARAMETERS
1491 * desc a pty_desc structure containing terminal information
1492 * int a signal number
1493 * RETURN VALUE
1494 * None
1495 */
1496 static void
1497 send_signal_via_characters
1498 (pty_desc *desc,
1499 int signal_number)
1500 {
1501 char ctrl_c = 03;
1502 char ctrl_backslash = 28;
1503 char ctrl_Z = 26;
1504
1505 switch (signal_number)
1506 {
1507 case SIGINT:
1508 write (desc->master_fd, &ctrl_c, 1); return;
1509 case SIGQUIT:
1510 write (desc->master_fd, &ctrl_backslash, 1); return;
1511 case SIGTSTP:
1512 write (desc->master_fd, &ctrl_Z, 1); return;
1513 }
1514 }
1515
1516 /* __gnat_interrupt_process - interrupt the child process
1517 *
1518 * PARAMETERS
1519 * desc a pty_desc structure
1520 */
1521 int
1522 __gnat_interrupt_process (pty_desc *desc)
1523 {
1524 send_signal_via_characters (desc, SIGINT);
1525 return 0;
1526 }
1527
1528 /* __gnat_interrupt_pid - interrupt a process group
1529 *
1530 * PARAMETERS
1531 * pid pid of the process to interrupt
1532 */
1533 int
1534 __gnat_interrupt_pid (int pid)
1535 {
1536 kill (-pid, SIGINT);
1537 return 0;
1538 }
1539
1540 /* __gnat_terminate_process - kill a child process
1541 *
1542 * PARAMETERS
1543 * desc pty_desc structure
1544 */
1545 int __gnat_terminate_process (pty_desc *desc)
1546 {
1547 return kill (desc->child_pid, SIGKILL);
1548 }
1549
1550 /* __gnat_terminate_pid - kill a process
1551 *
1552 * PARAMETERS
1553 * pid unix process id
1554 */
1555 int
1556 __gnat_terminate_pid (int pid)
1557 {
1558 return kill (pid, SIGKILL);
1559 }
1560
1561 /* __gnat_tty_waitpid - wait for the child process to die
1562 *
1563 * PARAMETERS
1564 * desc pty_desc structure
1565 * RETURN VALUE
1566 * exit status of the child process
1567 */
1568 int
1569 __gnat_tty_waitpid (pty_desc *desc)
1570 {
1571 int status = 0;
1572 waitpid (desc->child_pid, &status, 0);
1573 return WEXITSTATUS (status);
1574 }
1575
1576 /* __gnat_tty_supported - Are tty supported ?
1577 *
1578 * RETURN VALUE
1579 * always 1 on Unix systems
1580 */
1581 int
1582 __gnat_tty_supported (void)
1583 {
1584 return 1;
1585 }
1586
1587 /* __gnat_free_process - free a pty_desc structure
1588 *
1589 * PARAMETERS
1590 * in out desc: a pty desc structure
1591 */
1592 void
1593 __gnat_free_process (pty_desc** desc)
1594 {
1595 free (*desc);
1596 *desc = NULL;
1597 }
1598
1599 /* __gnat_send_header - dummy function. this interface is only used on Windows */
1600 void
1601 __gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1602 {
1603 *ret = 0;
1604 }
1605
1606 /* __gnat_reset_tty - reset line setting
1607 *
1608 * PARAMETERS
1609 * desc: a pty_desc structure
1610 */
1611 void
1612 __gnat_reset_tty (pty_desc* desc)
1613 {
1614 child_setup_tty (desc->master_fd);
1615 }
1616
1617 /* __gnat_new_tty - allocate a new terminal
1618 *
1619 * RETURN VALUE
1620 * a pty_desc structure
1621 */
1622 pty_desc *
1623 __gnat_new_tty (void)
1624 {
1625 int status;
1626 pty_desc* desc = NULL;
1627 if ((status = allocate_pty_desc (&desc)))
1628 child_setup_tty (desc->master_fd);
1629 return desc;
1630 }
1631
1632 /* __gnat_close_tty - close a terminal
1633 *
1634 * PARAMETERS
1635 * desc a pty_desc strucure
1636 */
1637 void __gnat_close_tty (pty_desc* desc)
1638 {
1639 if (desc->master_fd >= 0) close (desc->master_fd);
1640 if (desc->slave_fd >= 0) close (desc->slave_fd);
1641 }
1642
1643 /* __gnat_tty_name - return slave side device name
1644 *
1645 * PARAMETERS
1646 * desc a pty_desc strucure
1647 * RETURN VALUE
1648 * a string
1649 */
1650 char *
1651 __gnat_tty_name (pty_desc* desc)
1652 {
1653 return desc->slave_name;
1654 }
1655
1656 /* __gnat_tty_name - return master side fd
1657 *
1658 * PARAMETERS
1659 * desc a pty_desc strucure
1660 * RETURN VALUE
1661 * a fd
1662 */
1663 int
1664 __gnat_tty_fd (pty_desc* desc)
1665 {
1666 return desc->master_fd;
1667 }
1668
1669 #endif /* WIN32 */
This page took 0.104783 seconds and 4 git commands to generate.