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