Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[oweals/cde.git] / cde / lib / DtSvc / DtEncap / pty.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  * $TOG: pty.c /main/10 1999/10/14 15:06:11 mgreess $
25  * Language:     C
26  *
27  * (c) Copyright 1996 Digital Equipment Corporation.
28  * (c) Copyright 1988,1993,1994,1996 Hewlett-Packard Company.
29  * (c) Copyright 1993,1994,1996 International Business Machines Corp.
30  * (c) Copyright 1993,1994,1996 Sun Microsystems, Inc.
31  * (c) Copyright 1993,1994,1996 Novell, Inc. 
32  * (c) Copyright 1996 FUJITSU LIMITED.
33  * (c) Copyright 1996 Hitachi.
34  */
35
36 #define __need_fd_set
37
38 #if  defined(hpux) || defined(_hpux) || defined(__hpux) || defined(hp)
39 #define __hpux_pty
40 #endif
41
42 #ifdef __hpux_pty
43 #define __need_timeval  /* need struct timeval */
44 #endif
45
46 #include <bms/sbport.h> /* NOTE: sbport.h must be the first include. */
47 #include <errno.h>
48 #include <stdlib.h>
49 #include <signal.h>
50 #include <sys/types.h>
51 #include <sys/time.h>
52
53 #ifdef SVR4
54 #include <sys/filio.h>
55 #else
56 #include <sys/ioctl.h>
57 #endif
58
59 #ifdef __hpux_pty
60 #include <time.h>
61 #include <sys/ptyio.h>
62 #endif
63
64 #ifdef __bsd
65 #include <sys/file.h>
66 #include <sgtty.h>
67 #endif
68
69
70 /*
71 #ifdef __sun
72 #include <sys/ttycom.h>
73 #endif 
74 */
75
76 #include <SPC/spcP.h>
77 #include "DtSvcLock.h"
78
79 /* External declarations */
80
81 /*
82  * Pseudo-Terminal device file definitions:
83  * Switch _1 and _2 around if BSD or SYSV conventions are more likely
84  */
85
86 #if defined (SVR4) || defined(AIX)
87 #define MPREFIX_1       "/dev/pty"      /* BSD convention ?? */
88 #define SPREFIX_1       "/dev/tty"
89
90 #define MPREFIX_2       "/dev/ptym/pty" /* AT&T convention ?? */
91 #define SPREFIX_2       "/dev/pty/tty"
92
93 #else
94 #define MPREFIX_1       "/dev/ptym/pty" /* AT&T convention ?? */
95 #define SPREFIX_1       "/dev/pty/tty"
96
97 #define MPREFIX_2       "/dev/pty"      /* BSD convention ?? */
98 #define SPREFIX_2       "/dev/tty"
99 #endif
100
101 #ifdef HAVE_PTMS
102 #include <sys/ptms.h>
103 static char *MASTER_PATH = "/dev/ptmx";
104 #endif
105
106 static char *MASTER_NAMES = "pqrstuvwabcefghijklmnoxyz";
107
108 #define SCANBITS(sfds,step)                                     \
109    {int __i;                                                    \
110     for(__i=0; __i<FD_SETSIZE; __i++)                           \
111       if(FD_ISSET(__i, sfds))                                   \
112         step(__i);                                              \
113         }
114     
115 #define IS_FD_SET(fdarr, res)                                   \
116    {int __i;                                                    \
117     res=0;                                                      \
118     for(__i=0; __i<FD_SETSIZE; __i++)                           \
119       if(FD_ISSET(__i, fdarr)) {                                \
120         res = 1;                                                \
121         break;                                                  \
122       }}
123
124 static int send_eof_pty_channel_object(SPC_Channel_Ptr channel);
125
126 void pty_channel_class_init(object_clasp t)
127 {
128
129   pty_channel_clasp c = (pty_channel_clasp) t;
130   
131   c->new_obj    = alloc_channel_object;
132   
133   c->open       = open_pty_channel_object;
134   c->close      = close_local_channel_object;
135   c->read       = read_pty_channel_object;
136   c->write      = write_local_channel_object;
137   c->reset      = reset_pty_channel_object;
138   c->pre_fork   = pre_fork_pty_channel_object;
139   c->post_fork  = post_fork_pty_channel_object;
140   c->exec_proc  = exec_proc_local_channel_object;
141   c->signal     = signal_local_channel_object;
142   c->wait_for_termination=local_channel_object_wait_for_termination;
143   c->attach     = attach_pty_channel_object;
144   c->add_input  = add_input_pty_channel_object;
145   c->input      = local_channel_object_input_handler;
146   c->remove_logfile = remove_logfile_local_channel_object;
147
148   /* New B.00 methods */
149
150   c->send_eof = send_eof_pty_channel_object;
151   c->set_termio = set_termio_pty_channel_object;
152    
153 }
154
155 static struct pty_channel_class pty_channel_class_struct = {
156   (channel_clasp) &channel_class, /* base class pointer */
157   "pty_channel",           /* class name */
158   pty_channel_class_init,  /* class initialize function */
159   sizeof(SPC_Channel),      /* size */
160   0
161   };
162
163 pty_channel_clasp pty_channel_class = &pty_channel_class_struct;
164
165 /* Local variable */
166 static XeChar *hexdigits = "0123456789abcdef";
167
168 #ifdef __hpux_pty
169 /*----------------------------------------------------------------------+*/
170 static SPC_Disable_Trapping(int fd)
171 /*----------------------------------------------------------------------+*/
172 {
173   int flag=0;
174   int disable=0;
175   struct request_info req_info;
176
177   /* Disable trapping */
178   ioctl(fd, TIOCTRAP, &disable);
179
180   /* Just in case, flush any queued requests */
181   
182   while((ioctl(fd, TIOCTRAPSTATUS, &flag) != ERROR) && flag) {
183     ioctl(fd, TIOCREQGET, &req_info);
184     ioctl(fd, TIOCREQSET, &req_info);
185   }
186   return(TRUE);
187 }
188 #endif /* __hpux_pty */
189
190 /*
191  * Routines for opening pty master/slave devices
192  */
193
194 #ifdef HAVE_PTMS
195
196 /*----------------------------------------------------------------------+*/
197 static int getspec1170ptypair(Wire *wire)
198 /*----------------------------------------------------------------------+*/
199 {
200   char *slaveName;
201   struct sigaction newAction, oldAction;
202
203   strcpy(wire->master_name, MASTER_PATH);
204
205   if ((wire->fd[MASTER_SIDE] = open(wire->master_name, O_RDWR)) < OK)
206   {
207     /* open(master) failed. */
208     return FALSE;
209   }
210
211   /* SIGCHLD handler, if any, must be disabled during grantpt! */
212   sigaction(SIGCHLD, (struct sigaction *)NULL, &oldAction);
213   if (oldAction.sa_handler != SIG_DFL)
214   {
215     newAction = oldAction;
216     newAction.sa_handler = SIG_DFL;
217     sigaction(SIGCHLD, &newAction, (struct sigaction *)NULL);
218   }
219
220   if (grantpt(wire->fd[MASTER_SIDE]) != OK)
221   {
222     /* cannot access the slave pty. */
223     close(wire->fd[MASTER_SIDE]);
224     return FALSE;
225   }
226
227   /* Restore SIGCHLD handler. */
228   if (oldAction.sa_handler != SIG_DFL)
229     sigaction(SIGCHLD, &oldAction, (struct sigaction *)NULL);
230
231   if ((unlockpt(wire->fd[MASTER_SIDE]) != OK) ||
232       ((slaveName = ptsname(wire->fd[MASTER_SIDE])) == (char *)NULL) ||
233       (access(slaveName, R_OK | W_OK) == ERROR))
234   {
235     /* cannot access the slave pty. */
236     close(wire->fd[MASTER_SIDE]);
237     return FALSE;
238   }
239
240   /* we have opened a master with a slave we can access */
241   strcpy(wire->slave_name, slaveName);
242   return TRUE;
243 }
244
245 #endif
246
247 /* This is not exactly what should be done.  We really should open the
248    directory which contains "suspected" ptys.  Then read the directory
249    to get filenames, then check each file name to tell whether it is a
250    master pty.  However, for the time being, we will use the old way.
251 */   
252    
253
254 /*----------------------------------------------------------------------+*/
255 static int getptypair(Wire *wire)
256 /*----------------------------------------------------------------------+*/
257 {
258   /* Attempt to open the master/slave pair of preset pty */
259
260   XeString master=wire->master_name;
261   XeString slave =wire->slave_name;
262   
263   int c;
264   XeChar d;
265   XeString mptr;
266   XeString sptr;        /* Point to end of preset prefixes */
267   int c_len = strlen (MASTER_NAMES);
268
269   /* Set things up to quickly alter last 2 chars of path strings */
270   mptr = master + strlen(master);
271   *(mptr+2) = (XeChar)'\0';
272
273   sptr = slave + strlen(slave);
274   *(sptr+2) = (XeChar)'\0';
275
276   for (c = 0; c < c_len; c++) { /* 1st char */
277
278     /* Get the next character in order */
279     *(mptr) = *(sptr) = MASTER_NAMES[c];
280
281     for (d = 0; d < 15; d++) {  /* 2nd char:  0..9,a..f */
282
283       /* Get the next hex number in order */
284       *(mptr+1) = hexdigits[d];
285
286       /* Attempt to open this master side of pty */
287
288       if ((wire->fd[MASTER_SIDE] = open(master, O_RDWR)) < OK) {
289         /* open(master) failed, try next master... */
290         continue;
291       }
292
293       *(sptr+1) = hexdigits[d];
294
295       /* check that we can eventually open the slave side, using
296          the access system call */
297       if(access(slave, R_OK | W_OK) == ERROR) {
298         /* cannot access the slave pty.  Close master and try next one */
299         close(wire->fd[MASTER_SIDE]);
300         continue;
301       }
302
303       /* we have opened a master with a slave we can access */
304       
305       return TRUE;
306     }                           /* End for 2nd char */
307   }                             /* End for 1st char */
308
309   return FALSE;
310 }
311
312 /*----------------------------------------------------------------------+*/
313 static int initpty(Wire *wire)
314 /*----------------------------------------------------------------------+*/
315 {
316   /* Find first available master/slave pair */
317
318   /* set both sides of wire to -1 (unitialized convention) */
319   wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1;
320
321 #ifdef HAVE_PTMS
322   if (!getspec1170ptypair(wire))
323 #endif
324   {
325     /* Start with convention 1 */
326     strcpy(wire->master_name, MPREFIX_1);
327     strcpy(wire->slave_name, SPREFIX_1);
328
329     if (!getptypair(wire)) {
330       /* Cannot get that pair, so try convention 2 */
331       strcpy(wire->master_name, MPREFIX_2);
332       strcpy(wire->slave_name, SPREFIX_2);
333       if (!getptypair(wire)) {
334         /* No available pty's ?? */
335         wire->master_name[0] = wire->slave_name[0] = (XeChar)'\0';
336         wire->fd[MASTER_SIDE] = wire->fd[SLAVE_SIDE] = -1;
337         SPC_Error(SPC_No_Pty);
338         return(SPC_ERROR);
339       }
340     }
341   }
342
343   return(TRUE);
344 }
345
346 /*----------------------------------------------------------------------+*/
347 static int set_pty_state(int fd, struct termios *term_state)
348 /*----------------------------------------------------------------------+*/
349 {
350
351   if (fd < 0 || !isatty(fd))
352     return(TRUE);
353
354   /* Go to cooked mode (modify terminal state) */
355   if(tcsetattr(fd, TCSANOW, term_state)==ERROR) {
356     SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
357     return(SPC_ERROR);
358   }
359 #ifdef __sun
360   {
361     struct winsize size;
362     size.ws_row = 0;
363     size.ws_col = 0;
364     size.ws_xpixel = 0;
365     size.ws_ypixel = 0;
366
367     if(ioctl(fd, TIOCSWINSZ, &size) == ERROR) {
368       SPC_Error(SPC_Bad_tc_Call,(XeString)"tc_setwinsize");
369       return(SPC_ERROR);
370     }
371
372   }
373 #endif  
374   return(TRUE);
375 }
376
377 /*----------------------------------------------------------------------+*/
378 int master_pty(int fd, struct termios *state)
379 /*----------------------------------------------------------------------+*/
380 {
381   /* Make any special circumstances required on master side of pty */
382   int enable = 1;
383
384   if (fd < 0)
385     return(TRUE);
386   
387 #ifdef __hpux_pty
388   /* Enable trapping of ioctl/open/close (we care about close()) */
389   if(ioctl(fd, TIOCTRAP, &enable)==ERROR) {
390     SPC_Error(SPC_Bad_Ioctl);
391     return(SPC_ERROR);
392   }
393 #endif /* __hpux_pty */  
394
395   set_pty_state(fd, state);
396
397
398   return(TRUE);
399 }
400
401
402 /*----------------------------------------------------------------------+*/
403 static Wire *getpty(Wire *prevwire)
404 /*----------------------------------------------------------------------+*/
405 {
406   Wire *wire_ptr=get_new_wire();
407
408   if(!wire_ptr)
409     return(SPC_ERROR);
410
411   if(initpty(wire_ptr)==SPC_ERROR) {
412     free_wire(wire_ptr);
413     return(SPC_ERROR);
414   }
415   
416   wire_ptr->next=prevwire;
417   return(wire_ptr);
418 }
419
420 static void init_termio(struct termios *term_state)
421 {
422         
423   /* Basically, we want the pty channel to act like like a pipe
424      (perhaps later we should get more fancy).  This means that we
425      do not do any input processing for KILL / ERASE characters, we don't
426      echo the data, and reads will return when there is at least one character
427      in the buffer to be read. */
428         
429   term_state->c_iflag = 0;
430   term_state->c_oflag = 0;
431   term_state->c_cflag = CS8 | CREAD | HUPCL;
432   term_state->c_lflag = 0;
433
434   cfsetispeed(term_state, B9600);
435   cfsetospeed(term_state, B9600);
436         
437   term_state->c_cc[VMIN]=1;
438   term_state->c_cc[VTIME]=0;
439 }
440
441 /*
442  ***
443  *** Method definitions
444  ***
445 */
446
447 /*----------------------------------------------------------------------+*/
448 SPC_Channel_Ptr open_pty_channel_object(SPC_Channel_Ptr channel,
449                                         int iomode,
450                                         XeString hostname)
451 /*----------------------------------------------------------------------+*/
452 {
453
454   Wire *tmpwire, *newwire;
455   SPC_Channel_Ptr result;
456   
457   call_parent_method(channel, open, (channel, iomode, hostname), result);
458
459   if(result==SPC_ERROR)
460     return(SPC_ERROR);
461
462   /* We know that we are going to use one of STDIN, STDOUT, or STDERR
463      (or else we would be a NOIO channel), so allocate at least one
464      pty pair */
465
466   if(!(tmpwire=getpty(NULL)))
467     return(SPC_ERROR);
468
469   if (IS_SPCIO_STDIN(iomode)) {
470     channel->wires[STDIN]=tmpwire;
471   }
472   
473   if (IS_SPCIO_STDOUT(iomode)) {
474     channel->wires[STDOUT]=tmpwire;
475   }
476   
477   if (IS_SPCIO_SEPARATE(iomode)) {
478     if(!(newwire=getpty(tmpwire))) {
479       spc_close(tmpwire->fd[MASTER_SIDE]);
480       free_wire(tmpwire);
481       return(SPC_ERROR);
482     } else
483       tmpwire=newwire;
484   }
485   
486   if (IS_SPCIO_STDERR(iomode)) {
487     channel->wires[STDERR]=tmpwire;
488   }
489   
490   channel->wire_list=tmpwire;
491   
492   /* set up the channel file descriptors */
493
494   channel->file_descs[STDIN]  = (channel->wires[STDIN]) ->fd[MASTER_SIDE];
495   channel->file_descs[STDOUT] = (channel->wires[STDOUT])->fd[MASTER_SIDE];
496   channel->file_descs[STDERR] = (channel->wires[STDERR])->fd[MASTER_SIDE];
497
498   for(tmpwire=channel->wire_list; tmpwire; tmpwire=tmpwire->next) {
499     init_termio(&tmpwire->master_termio);
500     init_termio(&tmpwire->slave_termio);
501   }
502   
503   return(channel);
504
505 }
506
507 /*----------------------------------------------------------------------+*/
508 int read_pty_channel_object(SPC_Channel_Ptr channel,
509                             int connector,           /* STDOUT or STDERR */
510                             XeString buffer,
511                             int nbytes)
512 /*----------------------------------------------------------------------+*/
513 #ifdef __hpux_pty
514 {
515   
516   int result, select_value;
517   struct fd_set read_mask, except_mask;
518   int fd=channel->file_descs[connector];
519   struct request_info req_info;
520   struct timeval timeout, *timeptr;
521   int i;
522  
523   call_parent_method(channel,
524                      read,
525                      (channel, connector, buffer, nbytes),
526                      result);
527   
528   if(result==SPC_ERROR)
529     return(SPC_ERROR);
530
531   if(!IS_SPCIO_DATA(channel->wires[connector]->flags))
532     return(0);
533
534   FD_ZERO(&read_mask);
535   FD_ZERO(&except_mask);
536
537   FD_SET(fd, &read_mask);
538   FD_SET(fd, &except_mask);
539   
540   if(channel->close_timeout) {
541     timeout.tv_sec=channel->close_timeout;
542     timeout.tv_usec=0;
543     timeptr = (&timeout);
544   } else
545     timeptr=NULL;
546
547   do
548     select_value=select(fd+1, &read_mask, NULL, &except_mask, timeptr);
549   while(select_value==ERROR && errno==EINTR);
550   
551   if(select_value==ERROR) {
552     SPC_Error(SPC_Bad_Select);
553     return(SPC_ERROR);
554   }
555
556   /* If there is anything to read, read it & return */
557   IS_FD_SET(&read_mask, result);
558   if(result) {
559     do {
560       result = read(fd, buffer, nbytes);
561     } while (result<0 && errno == EINTR);
562     if(result==ERROR) {
563       SPC_Error(SPC_Reading);
564       return(SPC_ERROR);
565     }
566     return(result);
567   }
568   
569   /* Nothing to read.  We either timed out or got an exception. */
570   
571   if(select_value != 0) {
572
573     /* We got an exception */
574     ioctl(fd, TIOCREQGET, &req_info);
575     
576     /* Clear the request (Not really necessary in the case of a close,
577        but do it anyway) */
578     
579     ioctl(fd, TIOCREQSET, &req_info);
580   }
581
582   if((select_value == 0) || (req_info.request == TIOCCLOSE)) {
583
584     /* Close, disable trapping on this fd & return EOF.  We regard
585        a timeout as being the same as a close. */
586
587     SPC_Disable_Trapping(fd);
588     SPC_Change_State(channel, connector, 0, -1);
589     return(0);
590
591   } else
592
593     /* Otherwise (open or IOCTL), return -1 */
594
595     return(EXCEPT_FLAG);
596 }
597 #else /* not __hpux_pty */
598 {
599   int result;
600   int fd=channel->file_descs[connector];
601   long numbytes;
602   fd_set read_mask;
603   struct timeval tv={0, 50000};
604
605   result=ioctl(fd, FIONREAD, &numbytes);
606   if(numbytes == 0)
607     do {
608       result = kill(channel->pid, 0);
609       if((result == -1) && errno == ESRCH) {
610         SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
611         SPC_Change_State(channel, connector, 0, -1);
612         return(0);
613       }
614       FD_ZERO(&read_mask);
615       FD_SET(fd, &read_mask);
616       /*
617        **
618        ** This call to select doesn't have the cast to (int*), because
619        ** this clause of the ifdef is not compiled on HPUX.
620        **
621       */ 
622       result=select(fd+1, &read_mask, NULL, NULL, &tv);
623       if((result == -1) && (errno != EINTR)) {
624         SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
625         SPC_Change_State(channel, connector, 0, -1);
626         return(0);
627       }
628     } while((result <= 0));
629         
630   do {
631     result = read(fd, buffer, nbytes);
632   } while (result<0 && errno == EINTR);
633
634   if(result == ERROR) {
635     if(errno == EIO) {
636       SPC_XtRemoveInput(&channel->wires[connector]->read_toolkit_id, SPC_Input);
637       SPC_Change_State(channel, connector, 0, -1);
638       return(0);
639     } else {
640       XeString connection_hostname = (channel->connection ?
641                                       CONNECTION_HOSTNAME(channel->connection) :
642                                       Xestrdup(XeString_Empty));
643       SPC_Error(SPC_Reading, connection_hostname);
644       XeFree(connection_hostname);
645       return(SPC_ERROR);
646     }
647   }
648
649   return(result);
650 }
651 #endif /* __hpux_pty */
652
653 /*----------------------------------------------------------------------+*/
654 int pre_fork_pty_channel_object(SPC_Channel_Ptr channel)
655 /*----------------------------------------------------------------------+*/
656 {
657
658   int result;
659   Wire *wirelist;
660   
661   call_parent_method(channel, pre_fork, (channel), result);
662   
663   if(result==SPC_ERROR)
664     return(SPC_ERROR);
665
666   result=TRUE;
667   
668   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
669     if(master_pty(wirelist->fd[MASTER_SIDE], &wirelist->master_termio)
670        == SPC_ERROR)
671       result=SPC_ERROR;
672   }
673
674 #ifndef __hpux_pty
675   if(pipe(channel->sync_pipe) < 0) {
676     SPC_Error(SPC_No_Pipe);
677     return(SPC_ERROR);
678   }
679 #endif /* __hpux_pty */
680
681   return(result);
682 }
683
684 #ifdef __hpux_pty
685 /*----------------------------------------------------------------------+*/
686 /* clear_trap */
687 /*----------------------------------------------------------------------+*/
688
689 /* I am not particularly enamored of this macro.  However, the style of
690    the SCANBITS macro kinda forces me to write it this way.  In particular,
691    I am a bit worried about the reference to except_mask, which is a
692    "nonlocal reference" */
693
694 #define clear_trap(fd)  {struct request_info req_info;        \
695                          int my_fd=(fd);                      \
696                          ioctl(my_fd, TIOCREQGET, &req_info); \
697                            if(req_info.request != TIOCOPEN) { \
698                              SPC_Error(SPC_Bad_Ioctl);        \
699                              return(SPC_ERROR);               \
700                             }                                 \
701                          ioctl(my_fd, TIOCREQSET, &req_info); \
702                          FD_CLR(my_fd, &except_mask);         \
703                          }
704 #endif /* __hpux_pty */
705
706 /*----------------------------------------------------------------------+*/
707 int post_fork_pty_channel_object(SPC_Channel_Ptr channel,
708                                  int parentp)
709 /*----------------------------------------------------------------------+*/
710
711
712 {
713   int result;
714   int iomode=channel->IOMode;
715   int fd=channel->file_descs[STDIN];
716   int stdinfd, stdoutfd, stderrfd;
717 #ifdef __hpux_pty
718   struct fd_set except_mask, temp_mask;
719 #endif
720   int pid;
721   char c;
722       
723   call_parent_method(channel, post_fork, (channel, parentp), result);
724   
725   if(result==SPC_ERROR)
726     return(SPC_ERROR);
727   
728   if (parentp) {                /* Master process */
729 #ifdef __hpux_pty
730     { int i;
731       int select_value;
732       
733       stdinfd  = channel->wires[STDIN]->fd[MASTER_SIDE];
734       stdoutfd = channel->wires[STDOUT]->fd[MASTER_SIDE];
735       stderrfd = channel->wires[STDERR]->fd[MASTER_SIDE];
736
737       FD_ZERO(&except_mask);
738       
739       if(stdinfd >= 0)
740         FD_SET(stdinfd, &except_mask);
741       if(stdoutfd >= 0)
742         FD_SET(stdoutfd, &except_mask);
743       if(stderrfd >= 0)
744         FD_SET(stderrfd, &except_mask);
745       
746       IS_FD_SET(&except_mask, result);
747       while (result) {
748         temp_mask = except_mask;
749         select_value=select(max_fds, NULL, NULL, &temp_mask, NULL);
750         SCANBITS(&temp_mask, clear_trap);
751         IS_FD_SET(&except_mask, result);
752       }
753     }
754 #else                           /* not __hpux_pty */
755     close(channel->sync_pipe[WRITE_SIDE]);
756     read(channel->sync_pipe[READ_SIDE], &c, 1);
757     close(channel->sync_pipe[READ_SIDE]);
758     channel->sync_pipe[READ_SIDE] = -1;
759     channel->sync_pipe[WRITE_SIDE] = -1;
760     XeSPCAddInput(channel, NULL, NULL);    
761 #endif                          /* __hpux_pty */
762   
763   } else {                      /* Slave process */
764
765     /* Open the slave pty. Do it up to three times to set up
766        stdin, stdout, stderr */
767     
768     stdinfd =(-1);
769     stdoutfd=(-1);
770     stderrfd=(-1);
771
772
773     setsid();
774     pid = getpid();
775     
776     if(IS_SPCIO_STDIN(iomode)) {
777       if((stdinfd=open(channel->wires[STDIN]->slave_name, O_RDWR))<0) {
778         SPC_Error(SPC_Cannot_Open_Slave,
779                   channel->wires[STDIN]->slave_name);
780         return(SPC_ERROR);
781       }
782
783     }
784     
785     if(IS_SPCIO_STDOUT(iomode)) {
786       /* We will always share the file descriptor with STDIN,
787          if there is any */
788       if(stdinfd != -1)
789         stdoutfd=stdinfd;
790       else {
791         if((stdoutfd=open(channel->wires[STDOUT]->slave_name, O_RDWR))<0) {
792           SPC_Error(SPC_Cannot_Open_Slave,
793                     channel->wires[STDOUT]->slave_name);
794           spc_close(stdinfd);
795           return(SPC_ERROR);
796         } 
797       }
798     }
799     
800     if(IS_SPCIO_STDERR(iomode)) {
801       /* If we want separate STDOUT/STDERR, open a new FD */
802       if(IS_SPCIO_SEPARATE(iomode)) {
803         if((stderrfd=open(channel->wires[STDERR]->slave_name, O_RDWR))<0) {
804           SPC_Error(SPC_Cannot_Open_Slave,
805                     channel->wires[STDIN]->slave_name);
806           spc_close(stdinfd);
807           spc_close(stdoutfd);
808           return(SPC_ERROR);
809         }
810       } else
811         stderrfd=stdoutfd;
812     }
813
814 #ifndef __hpux_pty
815     /* The pty trapping stuff handles EOF for us.  Use the "sync" pipe */
816     /* to inform the other side when we don't have that code.          */
817     c=040;
818     write(channel->sync_pipe[WRITE_SIDE], &c, 1);
819     close(channel->sync_pipe[READ_SIDE]);
820     close(channel->sync_pipe[WRITE_SIDE]);
821 #endif                          /* __hpux_pty */
822
823     /* Duplicate these file descriptors to 3, 4, 5 so we don't have to
824        worry about any of std[in|out|err]fd being 0, 1, or 2. */
825     
826     spc_dup2(stdinfd,  3);
827     spc_dup2(stdoutfd, 4);
828     spc_dup2(stderrfd, 5);
829     
830     spc_dup2(3, STDIN);
831     spc_dup2(4, STDOUT);
832     spc_dup2(5, STDERR);
833
834     if(IS_SPCIO_STDIN(iomode))
835       set_pty_state(STDIN, &(channel->wires[STDIN]->slave_termio));
836     if(IS_SPCIO_STDOUT(iomode))
837       set_pty_state(STDOUT, &(channel->wires[STDOUT]->slave_termio));
838     if(IS_SPCIO_STDERR(iomode))
839       set_pty_state(STDERR, &(channel->wires[STDERR]->slave_termio));
840     
841     /* Close any other open file descriptors in the child */
842     
843     SPC_Close_Unused();
844   }
845   return(TRUE);
846 }
847
848 /*----------------------------------------------------------------------+*/
849 int reset_pty_channel_object(SPC_Channel_Ptr channel)
850 /*----------------------------------------------------------------------+*/
851 {
852   int result;
853   Wire *wirelist;
854   
855   call_parent_method(channel, reset, (channel), result);
856
857   if(result==SPC_ERROR)
858     return(SPC_ERROR);
859
860   result=TRUE;
861   
862   /* Make any special circumstances required on master side of pty */
863   
864   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
865     
866 #ifdef __hpux_pty
867   {
868     int fd=wirelist->fd[MASTER_SIDE];
869     /* Disable trapping of ioctl/open/close */
870     if(SPC_Disable_Trapping(fd) == SPC_ERROR)
871         result=SPC_ERROR;
872   }
873 #endif /* __hpux_pty */    
874
875     wirelist->flags &= ~SPCIO_DATA;
876   }
877
878   return(result);
879 }
880
881 /*----------------------------------------------------------------------+*/
882 int attach_pty_channel_object(SPC_Channel_Ptr channel, int pid)
883 /*----------------------------------------------------------------------+*/
884 {
885   set_pty_state(channel->file_descs[STDIN],
886                 &(channel->wires[STDIN]->master_termio));
887   set_pty_state(channel->file_descs[STDOUT],
888                 &(channel->wires[STDOUT]->master_termio));
889   set_pty_state(channel->file_descs[STDERR],
890                 &(channel->wires[STDERR]->master_termio));
891   
892   XeSPCReset(channel);
893
894   if(!mempf0(channel, pre_fork))
895     return(SPC_ERROR);
896   channel->pid = pid;
897   return(TRUE);
898 }
899
900 /*----------------------------------------------------------------------+*/
901 int add_input_pty_channel_object(SPC_Channel_Ptr channel,
902                                  SbInputHandlerProc handler,
903                                  void *data)
904 /*----------------------------------------------------------------------+*/
905 {
906   int result, fd;
907   Wire *wirelist, *stdinwire;
908   
909   call_parent_method(channel, add_input, (channel, handler, data), result);
910
911   if(result==SPC_ERROR)
912     return(SPC_ERROR);
913   
914   stdinwire=channel->wires[STDIN];
915
916   for(wirelist=channel->wire_list; wirelist; wirelist=wirelist->next) {
917
918     if((wirelist->read_toolkit_id   != -1) ||
919        (wirelist->except_toolkit_id != -1))
920       continue;
921     
922     fd=wirelist->fd[READ_SIDE];
923     SPC_XtAddInput(channel,
924                    &wirelist->read_toolkit_id,
925                    fd,
926                    channel->class_ptr->input,
927                    SPC_Input);
928 #ifdef __hpux_pty
929     SPC_XtAddInput(channel,
930                    &wirelist->except_toolkit_id,
931                    fd,
932                    channel->class_ptr->input,
933                    SPC_Exception);
934 #endif /* __hpux_pty */
935   }
936   
937   return(TRUE);
938   
939 }
940
941 Wire *setpgrp_wire = NULL;
942
943 struct termios *XeTermioStruct = NULL;
944 struct termios XeDefaultTermioStruct;
945
946 /*----------------------------------------------------------------------+*/
947 void InitDefaultTermioStruct(void)
948 /*----------------------------------------------------------------------+*/
949 {
950     int i;
951     
952     XeDefaultTermioStruct.c_iflag = BRKINT | IGNPAR | ICRNL | IXON;
953     XeDefaultTermioStruct.c_oflag = OPOST | ONLCR;
954     XeDefaultTermioStruct.c_cflag = CS8 | CREAD | CLOCAL;
955     XeDefaultTermioStruct.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK;
956
957     cfsetispeed(&XeDefaultTermioStruct, B9600);
958     cfsetospeed(&XeDefaultTermioStruct, B9600);
959     
960     for(i=0; i<NCCS; i++) 
961         XeDefaultTermioStruct.c_cc[i] = 0;
962     
963     XeDefaultTermioStruct.c_cc[VEOF]   = 04;            /* ^d */
964     XeDefaultTermioStruct.c_cc[VEOL]   = 0;             /* no extra eol char */
965     XeDefaultTermioStruct.c_cc[VERASE] = 010;           /* ^h */
966     XeDefaultTermioStruct.c_cc[VINTR]  = 03;            /* ^c */
967     XeDefaultTermioStruct.c_cc[VKILL]  = 025;           /* ^u */
968     XeDefaultTermioStruct.c_cc[VQUIT]  = 034;           /* ^\ */
969     XeDefaultTermioStruct.c_cc[VSTART] = 021;           /* ^q */
970     XeDefaultTermioStruct.c_cc[VSTOP]  = 023;           /* ^s */
971     XeDefaultTermioStruct.c_cc[VSUSP]  = 032;           /* ^z */
972
973     /* MIN and TIME are not needed in canonical ("line" or "cooked") mode */
974
975 }
976
977
978 /*----------------------------------------------------------------------+*/
979 struct termios *SPC_Get_Current_Termio(void)
980 /*----------------------------------------------------------------------+*/
981 {
982
983   struct termios *termio_struct;
984   int tty_fd, retval;
985   static Boolean default_is_initialized = FALSE;
986
987   _DtSvcProcessLock();
988   if (!default_is_initialized)
989   {
990       default_is_initialized = TRUE;
991       InitDefaultTermioStruct();
992   }
993   _DtSvcProcessUnlock();
994
995   termio_struct = (struct termios *)XeMalloc(sizeof(struct termios));
996
997   /* See if we can open /dev/tty go get default settings for this system */
998 #ifdef DEBUG
999   tty_fd = -1;
1000 #else
1001   tty_fd = open("/dev/tty", O_RDWR);
1002 #endif     
1003
1004   if (tty_fd >= 0)
1005   {
1006       /* retval=ioctl(tty_fd, TCGETA, termio_struct); */
1007       retval = tcgetattr(tty_fd, termio_struct);
1008       spc_close(tty_fd);
1009
1010       if(retval == ERROR) {
1011           SPC_Error(SPC_Bad_tc_Call,(XeString)"tcgetattr");
1012
1013           /* Fall through and use default settings */
1014       }
1015       else
1016           return(termio_struct);
1017   }
1018   
1019   /* We get here if we can't open /dev/tty or the tcgetattr() call failed */
1020
1021   memcpy(termio_struct, &XeDefaultTermioStruct, sizeof(struct termios));
1022   return(termio_struct);
1023 }
1024
1025 /*----------------------------------------------------------------------+*/
1026 int SPC_Setpgrp(int read_current_termio)
1027 /*----------------------------------------------------------------------+*/
1028 {
1029   _DtSvcProcessLock();
1030   if(setpgrp_wire == NULL)
1031     setpgrp_wire = get_new_wire();
1032   
1033   if(read_current_termio || XeTermioStruct == NULL) {
1034     
1035     if(XeTermioStruct)
1036       free((char *)XeTermioStruct);
1037     
1038     if((XeTermioStruct=SPC_Get_Current_Termio()) == SPC_ERROR) {
1039       _DtSvcProcessUnlock();
1040       return(SPC_ERROR);
1041     }
1042   }    
1043   
1044   spc_close(setpgrp_wire->fd[MASTER_SIDE]);
1045   spc_close(setpgrp_wire->fd[SLAVE_SIDE]);
1046   
1047   if((initpty(setpgrp_wire)) == SPC_ERROR) {
1048     spc_close(setpgrp_wire->fd[MASTER_SIDE]);
1049     spc_close(setpgrp_wire->fd[SLAVE_SIDE]);
1050     _DtSvcProcessUnlock();
1051     return(SPC_ERROR);
1052   }
1053   
1054   /* Point of no return */
1055   
1056   setsid();
1057   
1058   if((setpgrp_wire->fd[SLAVE_SIDE]=open(setpgrp_wire->slave_name, O_RDWR)) < 0) {
1059     _DtSvcProcessUnlock();
1060     return(SPC_ERROR);
1061   }
1062
1063   if(tcsetattr(setpgrp_wire->fd[SLAVE_SIDE], TCSANOW, XeTermioStruct)==ERROR) {
1064     SPC_Error(SPC_Bad_tc_Call,(XeString)"tcsetattr");
1065     _DtSvcProcessUnlock();
1066     return(SPC_ERROR);
1067   }
1068
1069   _DtSvcProcessUnlock();
1070   return(TRUE);  
1071 }
1072
1073 /*
1074  **
1075  ** New B.00 methods
1076  **
1077 */
1078
1079 int set_termio_pty_channel_object(SPC_Channel_Ptr channel,
1080                                   int connection,
1081                                   int side,
1082                                   struct termios *termio)
1083 {
1084   struct termios *old_termio;
1085
1086   if(side == MASTER_SIDE)
1087     old_termio = &channel->wires[connection]->master_termio;
1088   else
1089     old_termio = &channel->wires[connection]->slave_termio;
1090
1091   memcpy(old_termio, termio, sizeof(struct termios));
1092
1093   return(TRUE);
1094   
1095 }
1096
1097
1098 static int send_eof_pty_channel_object(SPC_Channel_Ptr channel)
1099 {
1100   Wire *wire = channel->wires[STDIN];
1101   char output_char;
1102   int fd, ret;
1103
1104   if(wire == NULL)
1105     return(TRUE);
1106
1107   if((wire->slave_termio.c_lflag & ~ICANON) == 0)
1108     return(FALSE);
1109   
1110   output_char = wire->slave_termio.c_cc[VEOF];
1111   fd = channel->file_descs[STDIN];
1112   
1113   /* Write twice -- once to flush output, and once to have 0 bytes sent. */
1114   
1115   ret = write(fd, &output_char, 1);
1116   ret = write(fd, &output_char, 1);
1117
1118   return(TRUE);
1119 }