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