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