OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [gcc/] [ada/] [terminals.c] - Blame information for rev 827

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 706 jeremybenn
/****************************************************************************
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-2011, 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 (__alpha__) && defined (__osf__)
980
#   define OSF1
981
#endif
982
#if defined (__mips) && defined (__sgi)
983
#   define IRIX
984
#endif
985
 
986
/* Include every system header we need */
987
#define _GNU_SOURCE
988
#include <errno.h>
989
#include <stdio.h>
990
#include <stdlib.h>
991
 
992
/* On some system termio is either absent or including it will disable termios
993
   (HP-UX) */
994
#if ! defined (__hpux__) && ! defined (FREEBSD) && \
995
    ! defined (__APPLE__) && ! defined(__rtems__)
996
#   include <termio.h>
997
#endif
998
 
999
#include <sys/ioctl.h>
1000
#include <termios.h>
1001
#include <fcntl.h>
1002
#include <string.h>
1003
#include <sys/stat.h>
1004
#include <sys/types.h>
1005
#include <sys/wait.h>
1006
#include <unistd.h>
1007
#if defined (sun)
1008
#   include <sys/stropts.h>
1009
#endif
1010
#if defined (FREEBSD) || defined (sun)
1011
#   include <sys/signal.h>
1012
#endif
1013
#if defined (__hpux__)
1014
#   include <sys/termio.h>
1015
#   include <sys/stropts.h>
1016
#endif
1017
 
1018
#define CDISABLE _POSIX_VDISABLE
1019
 
1020
/* On HP-UX and Sun system, there is a bzero function but with a different
1021
   signature. Use memset instead */
1022
#if defined (__hpux__) || defined (sun) || defined (_AIX)
1023
#   define bzero(s,n) memset (s,0,n)
1024
#endif
1025
 
1026
/* POSIX does not specify how to open the master side of a terminal.Several
1027
   methods are available (system specific):
1028
      1- using a cloning device (USE_CLONE_DEVICE)
1029
      2- getpt                  (USE_GETPT)
1030
      3- openpty                (USE_OPENPTY)
1031
      4- _getpty                (USE_GETPTY)
1032
 
1033
   When using the cloning device method, the macro USE_CLONE_DEVICE should
1034
   contains a full path to the adequate device.
1035
 
1036
   When a new system is about to be supported, one of the previous macro should
1037
   be set otherwise allocate_pty_desc will return an error
1038
*/
1039
 
1040
/* Configurable part */
1041
#if defined (__APPLE__) || defined (FREEBSD)
1042
#define USE_OPENPTY
1043
#elif defined (IRIX)
1044
#define USE_GETPTY
1045
#elif defined (linux)
1046
#define USE_GETPT
1047
#elif defined (sun)
1048
#define USE_CLONE_DEVICE "/dev/ptmx"
1049
#elif defined (_AIX)
1050
#define USE_CLONE_DEVICE "/dev/ptc"
1051
#elif defined (OSF1)
1052
/* On Tru64, the systems offers various interfaces to open a terminal:
1053
    - /dev/ptmx: this the system V driver (stream based),
1054
    - /dev/ptmx_bsd: the non stream based clone device,
1055
    - the openpty function which use BSD interface.
1056
 
1057
   Using directly /dev/ptmx_bsd on Tru64 5.1B seems to consume all the
1058
   available slave ptys (why ?). When using openpty it seems that the function
1059
   handles the creation of entries in /dev/pts when necessary and so avoid this
1060
   starvation issue. The pty man entry suggests also to use openpty.
1061
*/
1062
#define USE_OPENPTY
1063
#elif defined (__hpux__)
1064
/* On HP-UX we use the streamed version. Using the non streamed version is not
1065
   recommanded (through "/dev/ptym/clone"). Indeed it seems that there are
1066
   issues to detect process terminations. */
1067
#define USE_CLONE_DEVICE "/dev/ptmx"
1068
#endif
1069
 
1070
/* structure that holds information about the terminal used and the process
1071
   connected on the slave side */
1072
typedef struct pty_desc_struct {
1073
   int  master_fd;     /* fd of the master side if the terminal */
1074
   int  slave_fd;      /* fd of the slave side */
1075
   char slave_name[32];   /* filename of the slave side */
1076
   int  child_pid;     /* PID of the child process connected to the slave side
1077
                         of the terminal */
1078
} pty_desc;
1079
 
1080
/* allocate_pty_desc - allocate a pseudo terminal
1081
 *
1082
 * PARAMETERS
1083
 *   out desc  returned pointer to a pty_desc structure containing information
1084
 *             about the opened pseudo terminal
1085
 * RETURN VALUE
1086
 *   -1        if failed
1087
 *    0        if ok
1088
 * COMMENTS
1089
 *   If the function is successful we should have at least the master side fd
1090
 *   and the slave side filename. On some system, the slave side will also be
1091
 *   opened. If this is not the case the slave side will be open once we are in
1092
 *   the child process (note that opening the slave side at this stage will
1093
 *   failed...).
1094
 */
1095
 
1096
extern char* ptsname (int);
1097
 
1098
static int
1099
allocate_pty_desc (pty_desc **desc) {
1100
 
1101
   pty_desc *result;
1102
   int  status      =  0;
1103
   int  slave_fd    = -1;
1104
   int  master_fd   = -1;
1105
   char *slave_name = NULL;
1106
 
1107
#ifdef USE_GETPT
1108
  master_fd = getpt ();
1109
#elif defined (USE_OPENPTY)
1110
  status = openpty (&master_fd, &slave_fd, NULL, NULL, NULL);
1111
#elif defined (USE_GETPTY)
1112
  slave_name = _getpty (&master_fd, O_RDWR | O_NDELAY, 0600, 0);
1113
  if (slave_name == NULL) status = -1;
1114
#elif defined (USE_CLONE_DEVICE)
1115
  master_fd = open (USE_CLONE_DEVICE, O_RDWR | O_NONBLOCK, 0);
1116
#else
1117
  printf ("[error]: terminal support is not configured\n");
1118
  return -1;
1119
#endif
1120
 
1121
  /* at this stage we should have the master side fd and status should be 0 */
1122
  if (status != 0 || master_fd < 0)
1123
    {
1124
      /* If this is not the case close all opened files and return -1 */
1125
      printf ("[error]: cannot allocate master side of the pty\n");
1126
      if (master_fd >= 0) close (master_fd);
1127
      if (slave_fd  >= 0) close (slave_fd);
1128
      *desc = NULL;
1129
      return -1;
1130
    }
1131
 
1132
  /* retrieve the file name of the slave side if necessary */
1133
  if (slave_name == NULL) slave_name = (char *) ptsname (master_fd);
1134
 
1135
  /* Now we should have slave file name */
1136
  if (slave_name == NULL)
1137
    {
1138
      /* If not the case close any opened file and return - 1 */
1139
      printf ("[error]: cannot allocate slave side of the pty\n");
1140
      if (master_fd >= 0) close (master_fd);
1141
      if (slave_fd  >= 0) close (slave_fd);
1142
      *desc = NULL;
1143
      return -1;
1144
    }
1145
 
1146
#if !defined(__rtems__)
1147
  /* grant access to the slave side */
1148
  grantpt (master_fd);
1149
  /* unlock the terminal */
1150
  unlockpt (master_fd);
1151
#endif
1152
 
1153
  /* set desc and return 0 */
1154
  result = malloc (sizeof (pty_desc));
1155
  result->master_fd  = master_fd;
1156
  result->slave_fd   = slave_fd;
1157
  /* the string returned by ptsname or _getpty is a static allocated string. So
1158
     we should make a copy */
1159
  strncpy (result->slave_name, slave_name, sizeof (result->slave_name));
1160
  result->slave_name[sizeof (result->slave_name) - 1] = '\0';
1161
  result->child_pid  = -1;
1162
  *desc=result;
1163
  return 0;
1164
}
1165
 
1166
/* some utility macro that make the code of child_setup_tty easier to read */
1167
#define __enable(a, b) ((a) |= (b))
1168
#define __disable(a, b) ((a) &= ~(b))
1169
 
1170
/* some properties do not exist on all systems. Set their value to 0 in that
1171
   case */
1172
#ifndef IUCLC
1173
#define IUCLC 0
1174
#endif
1175
#ifndef OLCUC
1176
#define OLCUC 0
1177
#endif
1178
#ifndef NLDLY
1179
#define NLDLY 0
1180
#define CRDLY 0
1181
#define TABDLY 0
1182
#define BSDLY 0
1183
#define VTDLY 0
1184
#define FFDLY 0
1185
#endif
1186
 
1187
/* child_setup_tty - set terminal properties
1188
 *
1189
 * PARAMETERS
1190
 *   file descriptor of the slave side of the terminal
1191
 *
1192
 * RETURN VALUE
1193
 *   0 if success, any other value if failed.
1194
 *
1195
 * COMMENTS
1196
 *   None
1197
 */
1198
static int
1199
child_setup_tty (int fd)
1200
{
1201
  struct termios s;
1202
  int    status;
1203
 
1204
  /* ensure that s is filled with 0 */
1205
  bzero (&s, sizeof (&s));
1206
 
1207
  /* Get the current terminal settings */
1208
  status = tcgetattr (fd, &s);
1209
  if (status != 0) return -1;
1210
 
1211
  /* Adjust input modes */
1212
  __disable (s.c_iflag, IUCLC);    /* don't transform to lower case */
1213
  __disable (s.c_iflag, ISTRIP);   /* don't delete 8th bit */
1214
 
1215
  /* Adjust output modes */
1216
  __enable  (s.c_oflag, OPOST);    /* enable postprocessing */
1217
  __disable (s.c_oflag, ONLCR);    /* don't map LF to CR-LF */
1218
  __disable (s.c_oflag, NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
1219
                                   /* disable delays */
1220
  __disable (s.c_oflag, OLCUC);    /* don't transform to upper case */
1221
 
1222
  /* Adjust control modes */
1223
  s.c_cflag = (s.c_cflag & ~CSIZE) | CS8; /* Don't strip 8th bit */
1224
 
1225
  /* Adjust local modes */
1226
  __disable (s.c_lflag, ECHO);     /* disable echo */
1227
  __enable  (s.c_lflag, ISIG);     /* enable signals */
1228
  __enable  (s.c_lflag, ICANON);   /* erase/kill/eof processing */
1229
 
1230
  /* Adjust control characters */
1231
  /* IMPORTANT: we need to ensure that Ctrl-C will trigger an interrupt signal
1232
     otherwise send_signal_via_characters will fail */
1233
  s.c_cc[VEOF]   = 04;         /* insure that EOF is Control-D */
1234
  s.c_cc[VERASE] = CDISABLE;   /* disable erase processing */
1235
  s.c_cc[VKILL]  = CDISABLE;   /* disable kill processing */
1236
  s.c_cc[VQUIT]  = 28;         /* Control-\ */
1237
  s.c_cc[VINTR]  = 03;         /* Control-C */
1238
  s.c_cc[VEOL]   = CDISABLE;
1239
  s.c_cc[VSUSP]  = 26;         /* Control-Z */
1240
 
1241
  /* push our changes */
1242
  status = tcsetattr (fd, TCSADRAIN, &s);
1243
  return status;
1244
}
1245
 
1246
/* __gnat_setup_communication - interface to the external world. Should be
1247
 * called before forking. On Unixes this function only call allocate_pty_desc.
1248
 * The Windows implementation (in different part of this file) is very
1249
 * different.
1250
 *
1251
 * PARAMETERS
1252
 *  out desc   returned pointer to a pty_desc structure
1253
 * RETURN VALUE
1254
 *  0 if success, -1 otherwise
1255
 */
1256
int __gnat_setup_communication (pty_desc** desc) {
1257
  return allocate_pty_desc (desc);
1258
}
1259
 
1260
/* __gnat_setup_parent_communication - interface to the external world. Should
1261
 * be called after forking in the parent process
1262
 *
1263
 * PARAMETERS
1264
 *   out in_fd
1265
     out out_fd
1266
     out err_fd fds corresponding to the parent side of the
1267
                terminal
1268
     in pid_out child process pid
1269
 * RETRUN VALUE
1270
 *  0
1271
 */
1272
void
1273
__gnat_setup_parent_communication
1274
  (pty_desc *desc,
1275
   int*     in_fd,  /* input */
1276
   int*     out_fd, /* output */
1277
   int*     err_fd, /* error */
1278
   int*     pid_out)
1279
{
1280
 
1281
  *in_fd = desc->master_fd;
1282
  *out_fd= desc->master_fd;
1283
  *err_fd= desc->master_fd;
1284
  desc->child_pid = *pid_out;
1285
}
1286
 
1287
/* __gnat_setup_winsize - Sets up the size of the terminal
1288
 * This lets the process know the size of the terminal
1289
 */
1290
 
1291
void __gnat_setup_winsize (pty_desc *desc, int rows, int columns) {
1292
#ifdef TIOCGWINSZ
1293
  struct winsize s;
1294
  s.ws_row = (unsigned short)rows;
1295
  s.ws_col = (unsigned short)columns;
1296
  s.ws_xpixel = 0;
1297
  s.ws_ypixel = 0;
1298
  ioctl (desc->master_fd, TIOCSWINSZ, &s);
1299
#ifdef SIGWINCH
1300
  if (desc->child_pid > 0) {
1301
     /* Let the process know about the change in size */
1302
     kill (desc->child_pid, SIGWINCH);
1303
  }
1304
#endif
1305
#endif
1306
}
1307
 
1308
/* __gnat_setup_child_communication - interface to external world. Should be
1309
 * called after forking in the child process. On Unixes, this function
1310
 * first adjust the line setting, set standard output, input and error and
1311
 * then spawn the program.
1312
 *
1313
 * PARAMETERS
1314
 *   desc      a pty_desc structure containing the pty parameters
1315
 *   new_argv  argv of the program to be spawned
1316
 * RETURN VALUE
1317
 *   this function should not return
1318
 */
1319
int
1320
__gnat_setup_child_communication
1321
   (pty_desc *desc,
1322
    char **new_argv,
1323
    int Use_Pipes)
1324
{
1325
  int status;
1326
  int pid = getpid ();
1327
 
1328
  setsid ();
1329
 
1330
  /* open the slave side of the terminal if necessary */
1331
  if (desc->slave_fd == -1)
1332
#if defined (_AIX)
1333
    /* On AIX, if the slave process is not opened with O_NDELAY or O_NONBLOCK
1334
       then we might have some processes hanging on I/O system calls. Not sure
1335
       we can do that for all platforms so do it only on AIX for the moment.
1336
       On AIX O_NONBLOCK and O_NDELAY have slightly different meanings. When
1337
       reading on the slave fd, in case there is no data available, if O_NDELAY
1338
       is set then 0 is returned. If O_NON_BLOCK is -1 is returned. It seems
1339
       that interactive programs such as GDB prefer the O_NDELAY behavior.
1340
       We chose O_NONBLOCK because it allows us to make the distinction
1341
       between a true EOF and an EOF returned because there is no data
1342
       available to be read.  */
1343
    desc->slave_fd = open (desc->slave_name, O_RDWR | O_NONBLOCK, 0);
1344
#else
1345
    desc->slave_fd = open (desc->slave_name, O_RDWR, 0);
1346
#endif
1347
 
1348
#if defined (sun) || defined (__hpux__)
1349
  /* On systems such as Solaris we are using stream. We need to push the right
1350
     "modules" in order to get the expected terminal behaviors. Otherwise
1351
     functionalities such as termios are not available.  */
1352
  ioctl (desc->slave_fd, I_PUSH, "ptem");
1353
  ioctl (desc->slave_fd, I_PUSH, "ldterm");
1354
  ioctl (desc->slave_fd, I_PUSH, "ttcompat");
1355
#endif
1356
 
1357
#ifdef TIOCSCTTY
1358
  /* make the tty the controling terminal */
1359
  status = ioctl (desc->slave_fd, TIOCSCTTY, 0);
1360
#endif
1361
 
1362
  /* adjust tty settings */
1363
  child_setup_tty (desc->slave_fd);
1364
  __gnat_setup_winsize (desc, 24, 80); /* To prevent errors in some shells */
1365
 
1366
  /* stdin, stdout and stderr should be now our tty */
1367
  dup2 (desc->slave_fd, 0);
1368
  dup2 (desc->slave_fd, 1);
1369
  dup2 (desc->slave_fd, 2);
1370
  if (desc->slave_fd > 2) close (desc->slave_fd);
1371
 
1372
  /* adjust process group settings */
1373
  status = setpgid (pid, pid);
1374
  status = tcsetpgrp (0, pid);
1375
 
1376
  /* launch the program */
1377
  execvp (new_argv[0], new_argv);
1378
 
1379
  /* return the pid */
1380
  return pid;
1381
}
1382
 
1383
/* send_signal_via_characters - Send a characters that will trigger a signal
1384
 * in the child process.
1385
 *
1386
 * PARAMETERS
1387
 *  desc  a pty_desc structure containing terminal information
1388
 *  int   a signal number
1389
 * RETURN VALUE
1390
 *  None
1391
 */
1392
static void
1393
send_signal_via_characters
1394
  (pty_desc *desc,
1395
   int signal_number)
1396
{
1397
  char ctrl_c         = 03;
1398
  char ctrl_backslash = 28;
1399
  char ctrl_Z         = 26;
1400
 
1401
  switch (signal_number)
1402
    {
1403
      case SIGINT:
1404
        write (desc->master_fd, &ctrl_c, 1); return;
1405
      case SIGQUIT:
1406
        write (desc->master_fd, &ctrl_backslash, 1); return;
1407
      case SIGTSTP:
1408
        write (desc->master_fd, &ctrl_Z, 1); return;
1409
    }
1410
}
1411
 
1412
/* __gnat_interrupt_process - interrupt the child process
1413
 *
1414
 * PARAMETERS
1415
 *   desc a pty_desc structure
1416
 */
1417
int
1418
__gnat_interrupt_process (pty_desc *desc)
1419
{
1420
  send_signal_via_characters (desc, SIGINT);
1421
  return 0;
1422
}
1423
 
1424
/* __gnat_interrupt_pid - interrupt a process group
1425
 *
1426
 * PARAMETERS
1427
 *   pid  pid of the process to interrupt
1428
 */
1429
int
1430
__gnat_interrupt_pid (int pid)
1431
{
1432
  kill (-pid, SIGINT);
1433
  return 0;
1434
}
1435
 
1436
/* __gnat_terminate_process - kill a child process
1437
 *
1438
 * PARAMETERS
1439
 *   desc pty_desc structure
1440
 */
1441
int __gnat_terminate_process (pty_desc *desc)
1442
{
1443
  return kill (desc->child_pid, SIGKILL);
1444
}
1445
 
1446
/* __gnat_tty_waitpid - wait for the child proces to die
1447
 *
1448
 * PARAMETERS
1449
 *   desc pty_desc structure
1450
 * RETURN VALUE
1451
 *   exit status of the child process
1452
 */
1453
int
1454
__gnat_tty_waitpid (pty_desc *desc)
1455
{
1456
  int status = 0;
1457
  waitpid (desc->child_pid, &status, 0);
1458
  return WEXITSTATUS (status);
1459
}
1460
 
1461
/* __gnat_tty_supported - Are tty supported ?
1462
 *
1463
 * RETURN VALUE
1464
 *   always 1 on Unix systems
1465
 */
1466
int
1467
__gnat_tty_supported (void)
1468
{
1469
  return 1;
1470
}
1471
 
1472
/* __gnat_free_process - free a pty_desc structure
1473
 *
1474
 * PARAMETERS
1475
 *   in out desc: a pty desc structure
1476
 */
1477
void
1478
__gnat_free_process (pty_desc** desc)
1479
{
1480
  free (*desc);
1481
  *desc = NULL;
1482
}
1483
 
1484
/* __gnat_send_header - dummy function. this interface is only used on Windows */
1485
void
1486
__gnat_send_header (pty_desc* desc, char header[5], int size, int *ret)
1487
{
1488
  *ret = 0;
1489
}
1490
 
1491
/* __gnat_reset_tty - reset line setting
1492
 *
1493
 * PARAMETERS
1494
 *   desc: a pty_desc structure
1495
 */
1496
void
1497
__gnat_reset_tty (pty_desc* desc)
1498
{
1499
  child_setup_tty (desc->master_fd);
1500
}
1501
 
1502
/* __gnat_new_tty - allocate a new terminal
1503
 *
1504
 * RETURN VALUE
1505
 *   a pty_desc structure
1506
 */
1507
pty_desc *
1508
__gnat_new_tty (void)
1509
{
1510
  int status;
1511
  pty_desc* desc;
1512
  status = allocate_pty_desc (&desc);
1513
  child_setup_tty (desc->master_fd);
1514
  return desc;
1515
}
1516
 
1517
/* __gnat_close_tty - close a terminal
1518
 *
1519
 * PARAMETERS
1520
 *   desc  a pty_desc strucure
1521
 */
1522
void __gnat_close_tty (pty_desc* desc)
1523
{
1524
  if (desc->master_fd >= 0) close (desc->master_fd);
1525
  if (desc->slave_fd  >= 0) close (desc->slave_fd);
1526
}
1527
 
1528
/* __gnat_tty_name - return slave side device name
1529
 *
1530
 * PARAMETERS
1531
 *   desc  a pty_desc strucure
1532
 * RETURN VALUE
1533
 *   a string
1534
 */
1535
char *
1536
__gnat_tty_name (pty_desc* desc)
1537
{
1538
  return desc->slave_name;
1539
}
1540
 
1541
/* __gnat_tty_name - return master side fd
1542
 *
1543
 * PARAMETERS
1544
 *   desc  a pty_desc strucure
1545
 * RETURN VALUE
1546
 *   a fd
1547
 */
1548
int
1549
__gnat_tty_fd (pty_desc* desc)
1550
{
1551
  return desc->master_fd;
1552
}
1553
 
1554
#endif /* WIN32 */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.