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