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