/MirOS/dist/jupp/joe-3.1jupp30.tgz
[alioth/jupp.git] / tty.c
1 /* $MirOS: contrib/code/jupp/tty.c,v 1.22 2016/10/30 02:38:35 tg Exp $ */
2 /*
3  *      UNIX Tty and Process interface
4  *      Copyright
5  *              (C) 1992 Joseph H. Allen
6  *
7  *      This file is part of JOE (Joe's Own Editor)
8  */
9 #include "config.h"
10 #include "types.h"
11
12 #include <sys/types.h>
13 #ifdef HAVE_SYS_STAT_H
14 #include <sys/stat.h>
15 #endif
16 #ifdef HAVE_SYS_IOCTL_H
17 #include <sys/ioctl.h>
18 #endif
19 #include <stdio.h>
20 #ifdef HAVE_FCNTL_H
21 #include <fcntl.h>
22 #endif
23 #ifdef HAVE_SYS_WAIT_H
24 #include <sys/wait.h>
25 #endif
26 #ifdef HAVE_SYS_PARAM_H
27 #include <sys/param.h>
28 #endif
29
30 #include <errno.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #if HAVE_UTMP_H
36 #include <utmp.h>
37 #endif
38
39 int idleout = 1;
40
41 /* We use the defines in sys/ioctl to determine what type
42  * tty interface the system uses and what type of system
43  * we actually have.
44  */
45 #ifdef HAVE_POSIX_TERMIOS
46 #  include <termios.h>
47 # ifdef HAVE_SYS_TERMIOS_H
48 #  include <sys/termios.h>
49 # endif
50 #else
51 #  ifdef HAVE_SYSV_TERMIO
52 #    include <termio.h>
53 #    include <sys/termio.h>
54 #  else
55 #   ifdef HAVE_SGTTY_H
56 #    include <sgtty.h>
57 #   endif
58 #  endif
59 #endif
60
61 #ifdef HAVE_OPENPTY
62 #ifdef HAVE_PTY_H
63 #include <pty.h>
64 #endif
65 #ifdef HAVE_UTIL_H
66 #include <util.h>
67 #endif
68 #endif
69
70 /* Straight from the GNU autoconf texinfo documentation */
71 #ifdef TIME_WITH_SYS_TIME
72 # include <sys/time.h>
73 # include <time.h>
74 #else
75 # ifdef HAVE_SYS_TIME_H
76 #  include <sys/time.h>
77 # else
78 #  include <time.h>
79 # endif
80 #endif
81
82 /* I'm not sure if SCO_UNIX and ISC have __svr4__ defined, but I think
83    they might */
84 #ifdef M_SYS5
85 #ifndef M_XENIX
86 #include <sys/stream.h>
87 #include <sys/ptem.h>
88 #ifndef __svr4__
89 #define __svr4__ 1
90 #endif
91 #endif
92 #endif
93
94 #ifdef ISC
95 #ifndef __svr4__
96 #define __svr4__ 1
97 #endif
98 #endif
99
100 #ifdef __svr4__
101 #include <stropts.h>
102 #endif
103
104 /* JOE include files */
105
106 #include "main.h"
107 #include "path.h"
108 #include "scrn.h"
109 #include "tty.h"
110 #include "utils.h"
111 #include "ushell.h"
112
113 /** Aliased defines **/
114
115 /* O_NDELAY, O_NONBLOCK, and FNDELAY are all synonyms for placing a descriptor
116  * in non-blocking mode; we make whichever one we have look like O_NDELAY
117  */
118 #ifndef O_NDELAY
119 #ifdef O_NONBLOCK
120 #define O_NDELAY O_NONBLOCK
121 #endif
122 #ifdef FNDELAY
123 #define O_NDELAY FNDELAY
124 #endif
125 #endif
126
127 /* Some systems define this, some don't */
128 #ifndef sigmask
129 #define sigmask(x) (1<<((x)-1))
130 #endif
131
132 /* Some BSDs don't have TILDE */
133 #ifndef TILDE
134 #define TILDE 0
135 #endif
136
137 /* Global configuration variables */
138
139 int noxon = 0;                  /* Set if ^S/^Q processing should be disabled */
140 int Baud = 0;                   /* Baud rate from joerc, cmd line or environment */
141
142 /* The terminal */
143
144 FILE *termin = NULL;
145 FILE *termout = NULL;
146
147 /* Original state of tty */
148
149 #ifdef HAVE_POSIX_TERMIOS
150 struct termios oldterm;
151 #else /* HAVE_POSIX_TERMIOS */
152 #ifdef HAVE_SYSV_TERMIO
153 static struct termio oldterm;
154 #else /* HAVE_SYSV_TERMIO */
155 static struct sgttyb oarg;
156 static struct tchars otarg;
157 static struct ltchars oltarg;
158 #endif /* HAVE_SYSV_TERMIO */
159 #endif /* HAVE_POSIX_TERMIOS */
160
161 /* Output buffer, index and size */
162
163 unsigned char *obuf = NULL;
164 int obufp = 0;
165 int obufsiz;
166
167 /* The baud rate */
168
169 unsigned baud;                  /* Bits per second */
170 unsigned long upc;              /* Microseconds per character */
171
172 /* TTY Speed code to baud-rate conversion table (this is dumb- is it really
173  * too much to ask for them to just use an integer for the baud-rate?)
174  */
175
176 static int speeds[] = {
177         B50, 50, B75, 75, B110, 110, B134, 134, B150, 150, B200, 200,
178         B300, 300, B600, 600,
179         B1200, 1200, B1800, 1800, B2400, 2400, B4800, 4800, B9600, 9600
180 #ifdef EXTA
181             , EXTA, 19200
182 #endif
183 #ifdef EXTB
184             , EXTB, 38400
185 #endif
186 #ifdef B19200
187             , B19200, 19200
188 #endif
189 #ifdef B38400
190             , B38400, 38400
191 #endif
192 };
193
194 /* Input buffer */
195
196 int have = 0;                   /* Set if we have pending input */
197 static unsigned char havec;     /* Character read in during pending input check */
198 int leave = 0;                  /* When set, typeahead checking is disabled */
199
200 /* TTY mode flag.  1 for open, 0 for closed */
201 static int ttymode = 0;
202
203 /* Signal state flag.  1 for joe, 0 for normal */
204 static int ttysig = 0;
205
206 /* Stuff for shell windows */
207
208 static pid_t kbdpid;            /* PID of kbd client */
209 static int ackkbd = -1;         /* Editor acks keyboard client to this */
210
211 static int mpxfd;               /* Editor reads packets from this fd */
212 static int mpxsfd;              /* Clients send packets to this fd */
213
214 static int nmpx = 0;
215 static int tty_accept = NO_MORE_DATA;   /* =-1 if we have last packet */
216
217 struct packet {
218         MPX *who;
219         int size;
220         int ch;
221         unsigned char data[1024];
222 } pack;
223
224 MPX asyncs[NPROC];
225
226 /* Set signals for JOE */
227 void sigjoe(void)
228 {
229         if (ttysig)
230                 return;
231         ttysig = 1;
232         joe_set_signal(SIGHUP, ttsig);
233         joe_set_signal(SIGTERM, ttsig);
234         joe_set_signal(SIGINT, SIG_IGN);
235         joe_set_signal(SIGPIPE, SIG_IGN);
236 }
237
238 /* Restore signals for exiting */
239 void signrm(void)
240 {
241         if (!ttysig)
242                 return;
243         ttysig = 0;
244         joe_set_signal(SIGHUP, SIG_DFL);
245         joe_set_signal(SIGTERM, SIG_DFL);
246         joe_set_signal(SIGINT, SIG_DFL);
247         joe_set_signal(SIGPIPE, SIG_DFL);
248 }
249
250 /* Open terminal and set signals */
251
252 void ttopen(void)
253 {
254         sigjoe();
255         ttopnn();
256 }
257
258 /* Close terminal and restore signals */
259
260 void ttclose(void)
261 {
262         ttclsn();
263         signrm();
264 }
265
266 static int winched = 0;
267 #ifdef SIGWINCH
268 /* Window size interrupt handler */
269 static RETSIGTYPE winchd(int unused)
270 {
271         ++winched;
272         REINSTALL_SIGHANDLER(SIGWINCH, winchd);
273 }
274 #endif
275
276 /* Second ticker */
277
278 int ticked = 0;
279 extern int dostaupd;
280 static RETSIGTYPE dotick(int unused)
281 {
282         ticked = 1;
283 }
284
285 void tickoff(void)
286 {
287         alarm(0);
288 }
289
290 void tickon(void)
291 {
292         ticked = 0;
293         joe_set_signal(SIGALRM, dotick);
294         alarm(1);
295 }
296
297 /* Open terminal */
298
299 static void baud_reset(int);
300
301 void ttopnn(void)
302 {
303         int bbaud;
304
305 #ifdef HAVE_POSIX_TERMIOS
306         struct termios newterm;
307 #else
308 #ifdef HAVE_SYSV_TERMIO
309         struct termio newterm;
310 #else
311         struct sgttyb arg;
312         struct tchars targ;
313         struct ltchars ltarg;
314 #endif
315 #endif
316
317         if (!termin) {
318                 if (idleout ? (!(termin = stdin) || !(termout = stdout)) : (!(termin = fopen("/dev/tty", "r")) || !(termout = fopen("/dev/tty", "w")))) {
319                         fprintf(stderr, "Couldn\'t open /dev/tty\n");
320                         exit(1);
321                 } else {
322 #ifdef SIGWINCH
323                         joe_set_signal(SIGWINCH, winchd);
324 #endif
325                 }
326         }
327
328         if (ttymode)
329                 return;
330         ttymode = 1;
331         fflush(termout);
332
333 #ifdef HAVE_POSIX_TERMIOS
334         tcgetattr(fileno(termin), &oldterm);
335         newterm = oldterm;
336         newterm.c_lflag = 0;
337         if (noxon)
338                 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
339         else
340                 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
341         newterm.c_oflag = 0;
342         newterm.c_cc[VMIN] = 1;
343         newterm.c_cc[VTIME] = 0;
344         tcsetattr(fileno(termin), TCSADRAIN, &newterm);
345         bbaud = cfgetospeed(&newterm);
346 #else
347 #ifdef HAVE_SYSV_TERMIO
348         ioctl(fileno(termin), TCGETA, &oldterm);
349         newterm = oldterm;
350         newterm.c_lflag = 0;
351         if (noxon)
352                 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
353         else
354                 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
355         newterm.c_oflag = 0;
356         newterm.c_cc[VMIN] = 1;
357         newterm.c_cc[VTIME] = 0;
358         ioctl(fileno(termin), TCSETAW, &newterm);
359         bbaud = (newterm.c_cflag & CBAUD);
360 #else
361         ioctl(fileno(termin), TIOCGETP, &arg);
362         ioctl(fileno(termin), TIOCGETC, &targ);
363         ioctl(fileno(termin), TIOCGLTC, &ltarg);
364         oarg = arg;
365         otarg = targ;
366         oltarg = ltarg;
367         arg.sg_flags = ((arg.sg_flags & ~(ECHO | CRMOD | XTABS | ALLDELAY | TILDE)) | CBREAK);
368         if (noxon) {
369                 targ.t_startc = -1;
370                 targ.t_stopc = -1;
371         }
372         targ.t_intrc = -1;
373         targ.t_quitc = -1;
374         targ.t_eofc = -1;
375         targ.t_brkc = -1;
376         ltarg.t_suspc = -1;
377         ltarg.t_dsuspc = -1;
378         ltarg.t_rprntc = -1;
379         ltarg.t_flushc = -1;
380         ltarg.t_werasc = -1;
381         ltarg.t_lnextc = -1;
382         ioctl(fileno(termin), TIOCSETN, &arg);
383         ioctl(fileno(termin), TIOCSETC, &targ);
384         ioctl(fileno(termin), TIOCSLTC, &ltarg);
385         bbaud = arg.sg_ospeed;
386 #endif
387 #endif
388         baud_reset(bbaud);
389 }
390
391 static void
392 baud_reset(int bbaud)
393 {
394         size_t x = 0;
395
396         baud = 9600;
397         upc = 0;
398         while (x < NELEM(speeds))
399                 if (bbaud == speeds[x]) {
400                         baud = speeds[x + 1];
401                         break;
402                 } else
403                         x += 2;
404         if (Baud >= 50)
405                 baud = Baud;
406         else
407                 Baud = baud;
408         upc = DIVIDEND / baud;
409         if (obuf)
410                 joe_free(obuf);
411         if (!(TIMES * upc))
412                 obufsiz = 4096;
413         else {
414                 obufsiz = 1000000 / (TIMES * upc);
415                 if (obufsiz > 4096)
416                         obufsiz = 4096;
417         }
418         if (!obufsiz)
419                 obufsiz = 1;
420         obuf = (unsigned char *) joe_malloc(obufsiz);
421 }
422
423 /* Close terminal */
424
425 void ttclsn(void)
426 {
427         int oleave;
428
429         if (ttymode)
430                 ttymode = 0;
431         else
432                 return;
433
434         oleave = leave;
435         leave = 1;
436
437         ttflsh();
438
439 #ifdef HAVE_POSIX_TERMIOS
440         tcsetattr(fileno(termin), TCSADRAIN, &oldterm);
441 #else
442 #ifdef HAVE_SYSV_TERMIO
443         ioctl(fileno(termin), TCSETAW, &oldterm);
444 #else
445         ioctl(fileno(termin), TIOCSETN, &oarg);
446         ioctl(fileno(termin), TIOCSETC, &otarg);
447         ioctl(fileno(termin), TIOCSLTC, &oltarg);
448 #endif
449 #endif
450
451         leave = oleave;
452 }
453
454 /* Timer interrupt handler */
455
456 static int yep;
457 static RETSIGTYPE dosig(int unused)
458 {
459         yep = 1;
460 }
461
462 /* FLush output and check for typeahead */
463
464 #ifdef HAVE_SETITIMER
465 #ifdef SIG_SETMASK
466 static void maskit(void)
467 {
468         sigset_t set;
469
470         sigemptyset(&set);
471         sigaddset(&set, SIGALRM);
472         sigprocmask(SIG_SETMASK, &set, NULL);
473 }
474
475 static void unmaskit(void)
476 {
477         sigset_t set;
478
479         sigemptyset(&set);
480         sigprocmask(SIG_SETMASK, &set, NULL);
481 }
482
483 static void pauseit(void)
484 {
485         sigset_t set;
486
487         sigemptyset(&set);
488         sigsuspend(&set);
489 }
490
491 #else
492 static void maskit(void)
493 {
494         sigsetmask(sigmask(SIGALRM));
495 }
496
497 static void unmaskit(void)
498 {
499         sigsetmask(0);
500 }
501
502 static void pauseit(void)
503 {
504         sigpause(0);
505 }
506
507 #endif
508 #endif
509
510 int ttflsh(void)
511 {
512         /* Flush output */
513         if (obufp) {
514                 unsigned long usec = obufp * upc;       /* No. usecs this write should take */
515
516 #ifdef HAVE_SETITIMER
517                 if (usec >= 50000 && baud < 9600) {
518                         struct itimerval a, b;
519
520                         a.it_value.tv_sec = usec / 1000000;
521                         a.it_value.tv_usec = usec % 1000000;
522                         a.it_interval.tv_usec = 0;
523                         a.it_interval.tv_sec = 0;
524                         alarm(0);
525                         joe_set_signal(SIGALRM, dosig);
526                         yep = 0;
527                         maskit();
528                         setitimer(ITIMER_REAL, &a, &b);
529                         joe_write(fileno(termout), obuf, obufp);
530                         while (!yep)
531                                 pauseit();
532                         unmaskit();
533                 } else
534                         joe_write(fileno(termout), obuf, obufp);
535
536 #else
537
538                 joe_write(fileno(termout), obuf, obufp);
539
540 #ifdef FIORDCHK
541                 if (baud < 9600 && usec / 1000)
542                         nap(usec / 1000);
543 #endif
544
545 #endif
546
547                 obufp = 0;
548         }
549
550         /* Ack previous packet */
551         if (ackkbd != -1 && tty_accept != NO_MORE_DATA && !have) {
552                 unsigned char c = 0;
553
554                 if (pack.who && pack.who->func)
555                         joe_write(pack.who->ackfd, &c, 1);
556                 else
557                         joe_write(ackkbd, &c, 1);
558                 tty_accept = NO_MORE_DATA;
559         }
560
561         /* Check for typeahead or next packet */
562
563         if (!have && !leave) {
564                 if (ackkbd != -1) {
565                         fcntl(mpxfd, F_SETFL, O_NDELAY);
566                         if (read(mpxfd, &pack, sizeof(struct packet) - 1024) > 0) {
567                                 fcntl(mpxfd, F_SETFL, 0);
568                                 joe_read(mpxfd, pack.data, pack.size);
569                                 have = 1;
570                                 tty_accept = pack.ch;
571                         } else
572                                 fcntl(mpxfd, F_SETFL, 0);
573                 } else {
574                         /* Set terminal input to non-blocking */
575                         fcntl(fileno(termin), F_SETFL, O_NDELAY);
576
577                         /* Try to read */
578                         if (read(fileno(termin), &havec, 1) == 1)
579                                 have = 1;
580
581                         /* Set terminal back to blocking */
582                         fcntl(fileno(termin), F_SETFL, 0);
583                 }
584         }
585         return 0;
586 }
587
588 /* Read next character from input */
589
590 void mpxdied(MPX *m);
591
592 long last_time;
593
594 int ttgetc(void)
595 {
596         int stat_;
597         long new_time;
598
599
600         tickon();
601
602       loop:
603         new_time = time(NULL);
604         if (new_time != last_time) {
605                 last_time = new_time;
606                 dostaupd = 1;
607                 ticked = 1;
608         }
609         ttflsh();
610         while (winched) {
611                 winched = 0;
612                 edupd(1);
613                 ttflsh();
614         }
615         if (ticked) {
616                 edupd(0);
617                 ttflsh();
618                 tickon();
619         }
620         if (ackkbd != -1) {
621                 if (!have) {    /* Wait for input */
622                         stat_ = read(mpxfd, &pack, sizeof(struct packet) - 1024);
623
624                         if (pack.size && stat_ > 0) {
625                                 joe_read(mpxfd, pack.data, pack.size);
626                         } else if (stat_ < 1) {
627                                 if (winched || ticked)
628                                         goto loop;
629                                 else
630                                         ttsig(0);
631                         }
632                         tty_accept = pack.ch;
633                 }
634                 have = 0;
635                 if (pack.who) { /* Got bknd input */
636                         if (tty_accept != NO_MORE_DATA) {
637                                 if (pack.who->func) {
638                                         pack.who->func(pack.who->object, pack.data, pack.size);
639                                         edupd(1);
640                                 }
641                         } else
642                                 mpxdied(pack.who);
643                         goto loop;
644                 } else {
645                         if (tty_accept != NO_MORE_DATA) {
646                                 tickoff();
647                                 return tty_accept;
648                         }
649                         else {
650                                 tickoff();
651                                 ttsig(0);
652                                 return 0;
653                         }
654                 }
655         }
656         if (have) {
657                 have = 0;
658         } else {
659                 if (read(fileno(termin), &havec, 1) < 1) {
660                         if (winched || ticked)
661                                 goto loop;
662                         else
663                                 ttsig(0);
664                 }
665         }
666         tickoff();
667         return havec;
668 }
669
670 /* Write string to output */
671
672 void ttputs(unsigned char *s)
673 {
674         while (*s) {
675                 obuf[obufp++] = *s++;
676                 if (obufp == obufsiz)
677                         ttflsh();
678         }
679 }
680
681 /* Get window size */
682
683 void ttgtsz(int *x, int *y)
684 {
685 #ifdef TIOCGSIZE
686         struct ttysize getit;
687 #else
688 #ifdef TIOCGWINSZ
689         struct winsize getit;
690 #endif
691 #endif
692
693         *x = 0;
694         *y = 0;
695
696 #ifdef TIOCGSIZE
697         if (ioctl(fileno(termout), TIOCGSIZE, &getit) != -1) {
698                 *x = getit.ts_cols;
699                 *y = getit.ts_lines;
700         }
701 #else
702 #ifdef TIOCGWINSZ
703         if (ioctl(fileno(termout), TIOCGWINSZ, &getit) != -1) {
704                 *x = getit.ws_col;
705                 *y = getit.ws_row;
706         }
707 #endif
708 #endif
709 }
710
711 void ttshell(unsigned char *cmd)
712 {
713         int x, omode = ttymode;
714         const char *sh;
715
716         sh = getushell();
717         ttclsn();
718         if ((x = fork()) != 0) {
719                 if (x != -1)
720                         wait(NULL);
721                 if (omode)
722                         ttopnn();
723         } else {
724                 signrm();
725                 if (cmd)
726                         execl(sh, sh, "-c", cmd, NULL);
727                 else {
728                         fprintf(stderr, "You are at the command shell.  Type 'exit' to return\n");
729                         execl(sh, sh, NULL);
730                 }
731                 _exit(0);
732         }
733 }
734
735 /* Create keyboard task */
736
737 static int mpxresume(void)
738 {
739         int fds[2];
740
741         if (pipe(fds)) {
742                 ackkbd = -1;
743                 return (1);
744         }
745         tty_accept = NO_MORE_DATA;
746         have = 0;
747         if (!(kbdpid = fork())) {
748                 close(fds[1]);
749                 do {
750                         unsigned char c;
751                         int sta;
752
753                         pack.who = 0;
754                         sta = joe_read(fileno(termin), &c, 1);
755                         if (sta == 0)
756                                 pack.ch = NO_MORE_DATA;
757                         else
758                                 pack.ch = c;
759                         pack.size = 0;
760                         joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
761                 } while (joe_read(fds[0], &pack, 1) == 1);
762                 _exit(0);
763         }
764         close(fds[0]);
765         ackkbd = fds[1];
766         return (0);
767 }
768
769 /* Kill keyboard task */
770
771 static void mpxsusp(void)
772 {
773         if (ackkbd!=-1) {
774                 kill(kbdpid, 9);
775                 while (wait(NULL) < 0 && errno == EINTR)
776                         /* do nothing */;
777                 close(ackkbd);
778         }
779 }
780
781 /* We used to leave the keyboard copy task around during suspend, but
782    Cygwin gets confused when two processes are waiting for input and you
783    change the tty from raw to cooked (on the call to ttopnn()): the keyboard
784    process was stuck in cooked until he got a carriage return- then he
785    switched back to raw (he's supposed to switch to raw without waiting for
786    the end of line). Probably this should be done for ttshell() as well. */
787
788 void ttsusp(void)
789 {
790         int omode;
791
792 #ifdef SIGTSTP
793         omode = ttymode;
794         mpxsusp();
795         ttclsn();
796         fprintf(stderr, "You have suspended the program.  Type 'fg' to return\n");
797         kill(0, SIGTSTP);
798         if (omode)
799                 ttopnn();
800         if (ackkbd!= -1)
801                 mpxresume();
802 #else
803         ttshell(NULL);
804 #endif
805 }
806
807 /* Stuff for asynchronous I/O multiplexing.  We do not use streams or
808    select() because joe needs to work on versions of UNIX which predate
809    these calls.  Instead, when there is multiple async sources, we use
810    helper processes which packetize data from the sources.  A header on each
811    packet indicates the source.  There is no guarentee that packets getting
812    written to the same pipe don't get interleaved, but you can reasonable
813    rely on it with small packets. */
814
815 /* This code will explode if pipe, fork, etc. fail. --mirabilos */
816
817 static int mpxstart(void)
818 {
819         int fds[2];
820
821         if (pipe(fds)) {
822                 mpxfd = -1;
823                 mpxsfd = -1;
824                 return (1);
825         }
826         mpxfd = fds[0];
827         mpxsfd = fds[1];
828         return (mpxresume());
829 }
830
831 static void mpxend(void)
832 {
833         mpxsusp();
834         ackkbd = -1;
835         close(mpxfd);
836         close(mpxsfd);
837         if (have)
838                 havec = pack.ch;
839 }
840
841 /* Get a pty/tty pair.  Returns open pty in 'ptyfd' and returns tty name
842  * string in static buffer or NULL if couldn't get a pair.
843  */
844
845 #ifdef __svr4__
846 #define USEPTMX 1
847 #else
848 #ifdef __CYGWIN__
849 #define USEPTMX 1
850 #endif
851 #endif
852
853 #ifdef sgi
854
855 /* Newer sgi machines can do it the __svr4__ way, but old ones can't */
856
857 extern char *_getpty(int *fildes, int oflag, mode_t mode, int nofork);
858
859 static unsigned char *getpty(int *ptyfd)
860 {
861         return (unsigned char *)_getpty(ptyfd, O_RDWR, 0600, 0);
862 }
863
864 #else
865 #ifdef USEPTMX
866
867 /* Strange streams way */
868
869 extern char *ptsname(int);
870
871 static unsigned char *getpty(int *ptyfd)
872 {
873         int fdm;
874
875         *ptyfd = fdm = open("/dev/ptmx", O_RDWR);
876         grantpt(fdm);
877         unlockpt(fdm);
878         return (unsigned char *)ptsname(fdm);
879 }
880
881 #else
882 #ifdef HAVE_OPENPTY
883
884 /* BSD function, present in libc5 and glibc2 and (duh) the BSDs */
885
886 static unsigned char *getpty(int *ptyfd)
887 {
888         static unsigned char name[32];
889         int ttyfd;
890
891         if (openpty(ptyfd, &ttyfd, name, NULL, NULL) == 0)
892            return(name);
893         else
894            return (NULL);
895 }
896
897 #else
898 /* The normal way: for each possible pty/tty pair, try to open the pty and
899  * then the corresponding tty.  If both could be opened, close them both and
900  * then re-open the pty.  If that succeeded, return with the opened pty and the
901  * name of the tty.
902  *
903  * Logically you should only have to succeed in opening the pty- but the
904  * permissions may be set wrong on the tty, so we have to try that too.
905  * We close them both and re-open the pty because we want the forked process
906  * to open the tty- that way it gets to be the controlling tty for that
907  * process and the process gets to be the session leader.
908  */
909
910 static unsigned char *getpty(int *ptyfd)
911 {
912         int x, fd;
913         unsigned char *orgpwd = pwd();
914         static unsigned char **ptys = NULL;
915         static unsigned char *ttydir;
916         static unsigned char *ptydir;
917         static unsigned char ttyname[32];
918
919         if (!ptys) {
920                 ttydir = US "/dev/pty/";
921                 ptydir = US "/dev/ptym/";       /* HPUX systems */
922                 if (chpwd(ptydir) || !(ptys = rexpnd(US "pty*")))
923                         if (!ptys) {
924                                 ttydir = ptydir = US "/dev/";   /* Everyone else */
925                                 if (!chpwd(ptydir))
926                                         ptys = rexpnd(US "pty*");
927                         }
928         }
929         chpwd(orgpwd);
930
931         if (ptys)
932                 for (fd = 0; ptys[fd]; ++fd) {
933                         strlcpy((char *)ttyname, (char *)ptydir, 32);
934                         strlcat((char *)ttyname, (char  *)(ptys[fd]), 32);
935                         if ((*ptyfd = open((char *)ttyname, O_RDWR)) >= 0) {
936                                 ptys[fd][0] = 't';
937                                 strlcpy((char *)ttyname, (char *)ttydir, 32);
938                                 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
939                                 ptys[fd][0] = 'p';
940                                 x = open((char *)ttyname, O_RDWR);
941                                 if (x >= 0) {
942                                         close(x);
943                                         close(*ptyfd);
944                                         strlcpy((char *)ttyname, (char *)ptydir, 32);
945                                         strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
946                                         *ptyfd = open((char *)ttyname, O_RDWR);
947                                         ptys[fd][0] = 't';
948                                         strlcpy((char *)ttyname, (char *)ttydir, 32);
949                                         strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
950                                         ptys[fd][0] = 'p';
951                                         return ttyname;
952                                 } else
953                                         close(*ptyfd);
954                         }
955                 }
956         return NULL;
957 }
958
959 #endif
960 #endif
961 #endif
962
963 /* Shell dies signal handler.  Puts pty in non-block mode so
964  * that read returns with <1 when all data from process has
965  * been read. */
966 int dead = 0;
967 int death_fd;
968 static RETSIGTYPE death(int unused)
969 {
970         fcntl(death_fd,F_SETFL,O_NDELAY);
971         wait(NULL);
972         dead = 1;
973 }
974
975 #ifndef SIGCHLD
976 #define SIGCHLD SIGCLD
977 #endif
978
979 /* Build a new environment, but replace one variable */
980
981 extern unsigned char **mainenv;
982
983 static unsigned char **newenv(unsigned char **old, unsigned char *s)
984 {
985         unsigned char **new;
986         int x, y, z;
987
988         for (x = 0; old[x]; ++x) ;
989         new = (unsigned char **) joe_malloc((x + 2) * sizeof(unsigned char *));
990
991         for (x = 0, y = 0; old[x]; ++x) {
992                 for (z = 0; s[z] != '='; ++z)
993                         if (s[z] != old[x][z])
994                                 break;
995                 if (s[z] == '=') {
996                         if (s[z + 1])
997                                 new[y++] = s;
998                 } else
999                         new[y++] = old[x];
1000         }
1001         if (x == y)
1002                 new[y++] = s;
1003         new[y] = 0;
1004         return new;
1005 }
1006
1007 /* Create a shell process */
1008
1009 MPX *mpxmk(int *ptyfd, const unsigned char *cmd, unsigned char **args, void (*func) (/* ??? */), void *object, void (*die) (/* ??? */), void *dieobj)
1010 {
1011         unsigned char buf[80];
1012         int fds[2];
1013         int comm[2];
1014         pid_t pid;
1015         int x;
1016         MPX *m = NULL;
1017         unsigned char *name;
1018
1019         /* Get pty/tty pair */
1020         if (!(name = getpty(ptyfd)))
1021                 return NULL;
1022
1023         /* Find free slot */
1024         for (x = 0; x != NPROC; ++x)
1025                 if (!asyncs[x].func) {
1026                         m = asyncs + x;
1027                         break;
1028                 }
1029         if (x==NPROC)
1030                 return NULL;
1031
1032         /* PID number pipe */
1033         if (pipe(comm))
1034                 return (NULL);
1035
1036         /* Acknowledgement pipe */
1037         if (pipe(fds)) {
1038                 /* don't leak in error case */
1039  pipout:
1040                 close(comm[0]);
1041                 close(comm[1]);
1042                 return (NULL);
1043         }
1044         m->ackfd = fds[1];
1045
1046         /* Fixes cygwin console bug: if you fork() with inverse video he assumes you want
1047          * ESC [ 0 m to keep it in inverse video from then on. */
1048         set_attr(maint->t,0);
1049
1050         /* Flush output */
1051         ttflsh();
1052
1053         /* Bump no. current async inputs to joe */
1054         ++nmpx;
1055
1056         /* Start input multiplexer */
1057         if (ackkbd == -1)
1058                 if (mpxstart()) {
1059                         close(fds[0]);
1060                         close(fds[1]);
1061                         m->ackfd = -1;
1062                         --nmpx;
1063                         goto pipout;
1064                 }
1065
1066         /* Remember callback function */
1067         m->func = func;
1068         m->object = object;
1069         m->die = die;
1070         m->dieobj = dieobj;
1071
1072         /* Create processes... */
1073         if (!(m->kpid = fork())) {
1074                 /* This process copies data from shell to joe */
1075                 /* After each packet it sends to joe it waits for
1076                    an acknowledgement from joe so that it can not get
1077                    too far ahead with buffering */
1078
1079                 /* Close joe side of pipes */
1080                 close(fds[1]);
1081                 close(comm[0]);
1082
1083                 /* Flag which indicates child died */
1084                 dead = 0;
1085                 death_fd = *ptyfd;
1086                 joe_set_signal(SIGCHLD, death);
1087
1088                 if (!(pid = fork())) {
1089                         /* This process becomes the shell */
1090                         signrm();
1091
1092                         /* Close pty (we only need tty) */
1093                         close(*ptyfd);
1094
1095                         /* All of this stuff is for disassociating ourself from
1096                            controlling tty (session leader) and starting a new
1097                            session.  This is the most non-portable part of UNIX- second
1098                            only to pty/tty pair creation. */
1099 #ifndef HAVE_LOGIN_TTY
1100
1101 #ifdef TIOCNOTTY
1102                         x = open("/dev/tty", O_RDWR);
1103                         ioctl(x, TIOCNOTTY, 0);
1104 #endif
1105
1106                         setsid();       /* I think you do setprgp(0,0) on systems with no setsid() */
1107 #ifndef _MINIX
1108 /* http://mail-index.netbsd.org/pkgsrc-bugs/2011/06/13/msg043281.html */
1109 #ifndef SETPGRP_VOID
1110                         setpgrp(0, 0);
1111 #else
1112                         setpgrp();
1113 #endif
1114 #endif
1115
1116 #endif
1117                         /* Close all fds */
1118                         for (x = 0; x != 32; ++x)
1119                                 close(x);       /* Yes, this is quite a kludge... all in the
1120                                                    name of portability */
1121
1122                         /* Open the TTY */
1123                         if ((x = open((char *)name, O_RDWR)) != -1) {   /* Standard input */
1124                                 unsigned char **env = newenv(mainenv, US "TERM=");
1125
1126 #ifdef HAVE_LOGIN_TTY
1127                                 login_tty(x);
1128
1129 #else
1130                                 /* This tells the fd that it's a tty (I think) */
1131 #ifdef __svr4__
1132                                 ioctl(x, I_PUSH, "ptem");
1133                                 ioctl(x, I_PUSH, "ldterm");
1134 #endif
1135
1136                                 /* Open stdout, stderr */
1137                                 if (dup(x)) {}  /* standard output */
1138                                 if (dup(x)) {}  /* standard error */
1139                                 /*
1140                                  * yes, stdin, stdout, and stderr must
1141                                  * all be open for reading and writing.
1142                                  * On some systems the shell assumes this.
1143                                  */
1144 #endif
1145
1146                                 /* We could probably have a special TTY set-up for JOE, but for now
1147                                  * we'll just use the TTY setup for the TTY was was run on */
1148 #ifdef HAVE_POSIX_TERMIOS
1149                                 tcsetattr(0, TCSADRAIN, &oldterm);
1150 #else
1151 #ifdef HAVE_SYSV_TERMIO
1152                                 ioctl(0, TCSETAW, &oldterm);
1153 #else
1154                                 ioctl(0, TIOCSETN, &oarg);
1155                                 ioctl(0, TIOCSETC, &otarg);
1156                                 ioctl(0, TIOCSLTC, &oltarg);
1157 #endif
1158 #endif
1159
1160                                 /* Execute the shell */
1161                                 execve((const char *)cmd, (char **)args, (char **)env);
1162
1163                                 /* If shell didn't execute */
1164                                 joe_snprintf_1((char *)buf,sizeof(buf),"Couldn't execute shell '%s'\n",cmd);
1165                                 if (write(0,(char *)buf,strlen((char *)buf))) {}
1166                                 sleep(1);
1167                         }
1168
1169                         _exit(0);
1170                 }
1171
1172                 /* Tell JOE PID of shell */
1173                 joe_write(comm[1], &pid, sizeof(pid));
1174
1175                 /* sigpipe should be ignored here. */
1176
1177                 /* This process copies data from shell to JOE until EOF.  It creates a packet
1178                    for each data */
1179
1180
1181                 /* We don't really get EOF from a pty- it would just wait forever
1182                    until someone else writes to the tty.  So: when the shell
1183                    dies, the child died signal handler death() puts pty in non-block
1184                    mode.  This allows us to read any remaining data- then
1185                    read returns 0 and we know we're done. */
1186
1187               loop:
1188                 pack.who = m;
1189                 pack.ch = 0;
1190
1191                 /* Read data from process */
1192                 pack.size = joe_read(*ptyfd, pack.data, 1024);
1193
1194                 /* On SUNOS 5.8, the very first read from the pty returns 0 for some reason */
1195                 if (!pack.size)
1196                         pack.size = joe_read(*ptyfd, pack.data, 1024);
1197
1198                 if (pack.size > 0) {
1199                         /* Send data to JOE, wait for ack */
1200                         joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024 + pack.size);
1201
1202                         joe_read(fds[0], &pack, 1);
1203                         goto loop;
1204                 } else {
1205                         /* Shell died: return */
1206                         pack.ch = NO_MORE_DATA;
1207                         pack.size = 0;
1208                         joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
1209
1210                         _exit(0);
1211                 }
1212         }
1213         joe_read(comm[0], &m->pid, sizeof(m->pid));
1214
1215         /* We only need comm once */
1216         close(comm[0]);
1217         close(comm[1]);
1218
1219         /* Close other side of copy process pipe */
1220         close(fds[0]);
1221         return m;
1222 }
1223
1224 void mpxdied(MPX *m)
1225 {
1226         if (!--nmpx)
1227                 mpxend();
1228         while (wait(NULL) < 0 && errno == EINTR)
1229                 /* do nothing */;
1230         if (m->die)
1231                 m->die(m->dieobj);
1232         m->func = NULL;
1233         edupd(1);
1234 }
1235
1236 void
1237 tty_xonoffbaudrst(void)
1238 {
1239 #ifdef HAVE_POSIX_TERMIOS
1240         struct termios newterm;
1241 #else
1242 #ifdef HAVE_SYSV_TERMIO
1243         struct termio newterm;
1244 #else
1245         struct sgttyb arg;
1246         struct tchars targ;
1247 #endif
1248 #endif
1249
1250 #ifdef HAVE_POSIX_TERMIOS
1251         tcgetattr(fileno(termin), &newterm);
1252         if (noxon)
1253                 newterm.c_iflag &= ~(IXON | IXOFF);
1254         else
1255                 newterm.c_iflag |= (IXON | IXOFF);
1256         tcsetattr(fileno(termin), TCSADRAIN, &newterm);
1257         baud_reset(cfgetospeed(&newterm));
1258 #else
1259 #ifdef HAVE_SYSV_TERMIO
1260         ioctl(fileno(termin), TCGETA, &newterm);
1261         if (noxon)
1262                 newterm.c_iflag &= ~(IXON | IXOFF);
1263         else
1264                 newterm.c_iflag |= (IXON | IXOFF);
1265         ioctl(fileno(termin), TCSETAW, &newterm);
1266         baud_reset(newterm.c_cflag & CBAUD);
1267 #else
1268         ioctl(fileno(termin), TIOCGETP, &arg);
1269         ioctl(fileno(termin), TIOCGETC, &targ);
1270         if (noxon) {
1271                 targ.t_startc = -1;
1272                 targ.t_stopc = -1;
1273         } else {
1274                 targ.t_startc = otarg.t_startc;
1275                 targ.t_stopc = otarg.t_stopc;
1276         }
1277         ioctl(fileno(termin), TIOCSETC, &targ);
1278         baud_reset(arg.sg_ospeed);
1279 #endif
1280 #endif
1281 }