2 * UNIX Tty and Process interface
4 * (C) 1992 Joseph H. Allen
6 * This file is part of JOE (Joe's Own Editor)
11 __RCSID("$MirOS: contrib/code/jupp/tty.c,v 1.38 2018/11/11 18:20:51 tg Exp $");
13 #ifdef HAVE_SYS_PARAM_H
14 #include <sys/param.h>
17 #include <sys/ioctl.h>
32 /* We use the defines in sys/ioctl to determine what type
33 * tty interface the system uses and what type of system
36 #if defined(HAVE_POSIX_TERMIOS)
38 #elif defined(HAVE_SYSV_TERMIO)
40 #include <sys/termio.h>
41 #elif defined(HAVE_SGTTY_H)
54 /* Straight from the GNU autoconf texinfo documentation */
55 #ifdef TIME_WITH_SYS_TIME
56 # include <sys/time.h>
59 # ifdef HAVE_SYS_TIME_H
60 # include <sys/time.h>
66 /* I'm not sure if SCO_UNIX and ISC have __svr4__ defined, but I think
70 #include <sys/stream.h>
88 /* JOE include files */
97 /** Aliased defines **/
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
104 #define O_NDELAY O_NONBLOCK
107 #define O_NDELAY FNDELAY
111 /* Some systems define this, some don't */
113 #define sigmask(x) (1<<((x)-1))
116 /* Some BSDs don't have TILDE */
121 /* Global configuration variables */
123 int noxon = 0; /* Set if ^S/^Q processing should be disabled */
124 int Baud = 0; /* Baud rate from joerc, cmd line or environment */
129 FILE *termout = NULL;
131 /* Original state of tty */
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 */
145 /* Output buffer, index and size */
147 unsigned char *obuf = NULL;
153 unsigned baud; /* Bits per second */
154 unsigned long upc; /* Microseconds per character */
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?)
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
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 */
184 /* TTY mode flag. 1 for open, 0 for closed */
185 static int ttymode = 0;
187 /* Signal state flag. 1 for joe, 0 for normal */
188 static int ttysig = 0;
191 /* Stuff for shell windows */
193 static pid_t kbdpid; /* PID of kbd client */
194 static int ackkbd = -1; /* Editor acks keyboard client to this */
196 static int mpxfd; /* Editor reads packets from this fd */
197 static int mpxsfd; /* Clients send packets to this fd */
200 static int tty_accept = NO_MORE_DATA; /* =-1 if we have last packet */
206 unsigned char data[1024];
212 /* Set signals for JOE */
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);
224 /* Restore signals for exiting */
225 void signrm(int inchild)
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);
237 /* Open terminal and set signals */
245 /* Close terminal and restore signals */
253 static volatile sig_atomic_t winched = 0;
255 /* Window size interrupt handler */
256 static RETSIGTYPE winchd(int unused)
259 REINSTALL_SIGHANDLER(SIGWINCH, winchd);
265 static volatile sig_atomic_t ticked = 0;
267 static RETSIGTYPE dotick(int unused)
280 joe_set_signal(SIGALRM, dotick);
286 static void baud_reset(int);
292 #ifdef HAVE_POSIX_TERMIOS
293 struct termios newterm;
295 #ifdef HAVE_SYSV_TERMIO
296 struct termio newterm;
300 struct ltchars ltarg;
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");
310 joe_set_signal(SIGWINCH, winchd);
320 #ifdef HAVE_POSIX_TERMIOS
321 tcgetattr(fileno(termin), &oldterm);
325 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
327 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
329 newterm.c_cc[VMIN] = 1;
330 newterm.c_cc[VTIME] = 0;
331 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
332 bbaud = cfgetospeed(&newterm);
334 #ifdef HAVE_SYSV_TERMIO
335 ioctl(fileno(termin), TCGETA, &oldterm);
339 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
341 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
343 newterm.c_cc[VMIN] = 1;
344 newterm.c_cc[VTIME] = 0;
345 ioctl(fileno(termin), TCSETAW, &newterm);
346 bbaud = (newterm.c_cflag & CBAUD);
348 ioctl(fileno(termin), TIOCGETP, &arg);
349 ioctl(fileno(termin), TIOCGETC, &targ);
350 ioctl(fileno(termin), TIOCGLTC, <arg);
354 arg.sg_flags = ((arg.sg_flags & ~(ECHO | CRMOD | XTABS | ALLDELAY | TILDE)) | CBREAK);
369 ioctl(fileno(termin), TIOCSETN, &arg);
370 ioctl(fileno(termin), TIOCSETC, &targ);
371 ioctl(fileno(termin), TIOCSLTC, <arg);
372 bbaud = arg.sg_ospeed;
379 baud_reset(int bbaud)
385 while (x < NELEM(speeds))
386 if (bbaud == speeds[x]) {
387 baud = speeds[x + 1];
395 upc = DIVIDEND / baud;
398 if ((TIMES * upc) == 0)
401 obufsiz = 1000000 / (TIMES * upc);
407 obuf = malloc(obufsiz);
426 #ifdef HAVE_POSIX_TERMIOS
427 tcsetattr(fileno(termin), TCSADRAIN, &oldterm);
429 #ifdef HAVE_SYSV_TERMIO
430 ioctl(fileno(termin), TCSETAW, &oldterm);
432 ioctl(fileno(termin), TIOCSETN, &oarg);
433 ioctl(fileno(termin), TIOCSETC, &otarg);
434 ioctl(fileno(termin), TIOCSLTC, &oltarg);
441 /* Timer interrupt handler */
443 static volatile sig_atomic_t yep;
444 static RETSIGTYPE dosig(int unused)
449 /* FLush output and check for typeahead */
451 #ifdef HAVE_SETITIMER
453 static void maskit(void)
458 sigaddset(&set, SIGALRM);
459 sigprocmask(SIG_SETMASK, &set, NULL);
462 static void unmaskit(void)
467 sigprocmask(SIG_SETMASK, &set, NULL);
470 static void pauseit(void)
479 static void maskit(void)
481 sigsetmask(sigmask(SIGALRM));
484 static void unmaskit(void)
489 static void pauseit(void)
501 unsigned long usec = obufp * upc; /* No. usecs this write should take */
503 #ifdef HAVE_SETITIMER
504 if (usec >= 50000 && baud < 9600) {
505 struct itimerval a, b;
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;
512 joe_set_signal(SIGALRM, dosig);
515 setitimer(ITIMER_REAL, &a, &b);
516 joe_write(fileno(termout), obuf, obufp);
521 joe_write(fileno(termout), obuf, obufp);
525 joe_write(fileno(termout), obuf, obufp);
528 if (baud < 9600 && usec / 1000)
538 /* Ack previous packet */
539 if (ackkbd != -1 && tty_accept != NO_MORE_DATA && !have) {
542 if (pack.who && pack.who->func)
543 joe_write(pack.who->ackfd, &c, 1);
545 joe_write(ackkbd, &c, 1);
546 tty_accept = NO_MORE_DATA;
550 /* Check for typeahead or next packet */
552 if (!have && !leave) {
557 fcntl(mpxfd, F_SETFL, O_NDELAY);
558 r = read(mpxfd, &pack, 1);
559 fcntl(mpxfd, F_SETFL, 0);
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) {
567 tty_accept = pack.ch;
573 /* Set terminal input to non-blocking */
574 fcntl(fileno(termin), F_SETFL, O_NDELAY);
577 if (read(fileno(termin), &havec, 1) == 1)
580 /* Set terminal back to blocking */
581 fcntl(fileno(termin), F_SETFL, 0);
587 /* Read next character from input */
590 static void mpxdied(MPX *m);
593 static time_t last_time;
602 new_time = time(NULL);
603 if (new_time != last_time) {
604 last_time = new_time;
624 r = sizeof(struct packet) - 1024;
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)
634 tty_accept = pack.ch;
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);
647 } else if (tty_accept != NO_MORE_DATA) {
659 } else if (read(fileno(termin), &havec, 1) < 1) {
660 if (winched || ticked)
668 /* Write string to output */
670 void ttputs(unsigned char *s)
673 obuf[obufp++] = *s++;
674 if (obufp == obufsiz)
679 /* Get window size */
681 void ttgtsz(int *x, int *y)
684 struct ttysize getit;
687 struct winsize getit;
695 if (ioctl(fileno(termout), TIOCGSIZE, &getit) != -1) {
701 if (ioctl(fileno(termout), TIOCGWINSZ, &getit) != -1) {
710 /* void ttshell(char *s); Run a shell command or if 's' is zero, run a
713 static void ttshell(unsigned char *cmd);
714 static const char shmsg[] =
715 "You are at the command shell. Type 'exit' to return\n";
718 #define v_or_fork() fork()
720 #define v_or_fork() vfork()
723 static void ttshell(unsigned char *cmd)
725 int x, omode = ttymode;
730 if (!(x = v_or_fork())) {
733 execl(sh, sh, "-c", cmd, NULL);
735 write(2, shmsg, sizeof(shmsg) - 1);
748 /* Create keyboard task */
750 static int mpxresume(void)
758 tty_accept = NO_MORE_DATA;
760 if (!(kbdpid = fork())) {
767 sta = joe_read(fileno(termin), &c, 1);
769 pack.ch = NO_MORE_DATA;
773 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
774 } while (joe_read(fds[0], &pack, 1) == 1);
782 /* Kill keyboard task */
784 static void mpxsusp(void)
788 while (wait(NULL) < 0 && errno == EINTR)
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. */
812 fprintf(stderr, "You have suspended the program. Type 'fg' to return\n");
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. */
834 /* This code will explode if pipe, fork, etc. fail. --mirabilos */
836 static int mpxstart(void)
847 return (mpxresume());
850 static void mpxend(void)
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.
874 /* Newer sgi machines can do it the __svr4__ way, but old ones can't */
876 extern char *_getpty(int *fildes, int oflag, mode_t mode, int nofork);
878 static unsigned char *getpty(int *ptyfd)
880 return (unsigned char *)_getpty(ptyfd, O_RDWR, 0600, 0);
886 /* Strange streams way */
888 extern char *ptsname(int);
890 static unsigned char *getpty(int *ptyfd)
894 *ptyfd = fdm = open("/dev/ptmx", O_RDWR);
897 return (unsigned char *)ptsname(fdm);
903 /* BSD function, present in libc5 and glibc2 and (duh) the BSDs */
905 static unsigned char *getpty(int *ptyfd)
907 static unsigned char name[32];
910 if (openpty(ptyfd, &ttyfd, name, NULL, NULL) == 0)
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
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.
929 static unsigned char *getpty(int *ptyfd)
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];
940 ttydir = UC "/dev/pty/";
941 ptydir = UC "/dev/ptym/";
942 if (chpwd(ptydir) || !(ptys = rexpnd(UC "pty*")))
945 ttydir = ptydir = UC "/dev/";
947 ptys = rexpnd(UC "pty*");
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) {
958 strlcpy((char *)ttyname, (char *)ttydir, 32);
959 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
961 x = open((char *)ttyname, O_RDWR);
965 strlcpy((char *)ttyname, (char *)ptydir, 32);
966 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
967 *ptyfd = open((char *)ttyname, O_RDWR);
969 strlcpy((char *)ttyname, (char *)ttydir, 32);
970 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
984 /* Shell dies signal handler. Puts pty in non-block mode so
985 * that read returns with <1 when all data from process has
987 static volatile sig_atomic_t dead = 0;
989 static RETSIGTYPE death(int unused)
991 fcntl(death_fd,F_SETFL,O_NDELAY);
997 #define SIGCHLD SIGCLD
1000 /* Build a new environment, but replace one variable */
1002 extern unsigned char **mainenv;
1004 static unsigned char **
1005 newenv(unsigned char **old, const unsigned char *s)
1007 unsigned char **new;
1010 for (x = 0; old[x]; ++x)
1012 new = malloc((x + 2) * sizeof(unsigned char *));
1014 for (x = 0, y = 0; old[x]; ++x) {
1015 for (z = 0; s[z] != '='; ++z)
1016 if (s[z] != old[x][z])
1020 new[y++] = (void *)strdup((const char *)s);
1025 new[y++] = (void *)strdup((const char *)s);
1030 /* Create a shell process */
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)
1037 unsigned char buf[80];
1043 unsigned char *name;
1045 /* Get pty/tty pair */
1046 if (!(name = getpty(ptyfd)))
1049 /* Find free slot */
1050 for (x = 0; x != NPROC; ++x)
1051 if (!asyncs[x].func) {
1058 /* PID number pipe */
1062 /* Acknowledgement pipe */
1064 /* don't leak in error case */
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
1077 set_attr(maint->t,0);
1082 /* Bump no. current async inputs to joe */
1085 /* Start input multiplexer */
1095 /* Remember callback function */
1101 /* Create processes... */
1102 if (!(m->kpid = fork())) {
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.
1110 /* Close joe side of pipes */
1114 /* Flag which indicates child died */
1117 joe_set_signal(SIGCHLD, death);
1119 if (!(pid = fork())) {
1120 /* This process becomes the shell */
1121 unsigned char **env;
1125 /* Close pty (we only need tty) */
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.
1134 #ifndef HAVE_LOGIN_TTY
1137 x = open("/dev/tty", O_RDWR);
1138 ioctl(x, TIOCNOTTY, 0);
1141 /* I think you do setprgp(0,0) on systems with no setsid() */
1144 /* http://mail-index.netbsd.org/pkgsrc-bugs/2011/06/13/msg043281.html */
1145 #ifndef SETPGRP_VOID
1154 for (x = 0; x != 32; ++x) {
1155 /* Yes, this is quite a kludge... */
1156 /* All in the name of portability */
1160 /* Open the TTY as standard input */
1161 if ((x = open((char *)name, O_RDWR)) != -1) {
1162 env = newenv(mainenv, UC "TERM=");
1164 #ifdef HAVE_LOGIN_TTY
1168 /* This tells the fd that it's a tty (I think) */
1170 ioctl(x, I_PUSH, "ptem");
1171 ioctl(x, I_PUSH, "ldterm");
1174 /* Open stdout, stderr */
1175 if (dup(x)) {} /* standard output */
1176 if (dup(x)) {} /* standard error */
1178 * yes, stdin, stdout, and stderr must
1179 * all be open for reading and writing.
1180 * On some systems the shell assumes this.
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
1190 #ifdef HAVE_POSIX_TERMIOS
1191 tcsetattr(0, TCSADRAIN, &oldterm);
1193 #ifdef HAVE_SYSV_TERMIO
1194 ioctl(0, TCSETAW, &oldterm);
1196 ioctl(0, TIOCSETN, &oarg);
1197 ioctl(0, TIOCSETC, &otarg);
1198 ioctl(0, TIOCSLTC, &oltarg);
1202 /* Execute the shell */
1203 execve((const char *)cmd, (char **)args, (char **)env);
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))) {}
1214 /* Tell JOE PID of shell */
1215 joe_write(comm[1], &pid, sizeof(pid));
1217 /* sigpipe should be ignored here. */
1219 /* This process copies data from shell to JOE until EOF. It creates a packet
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. */
1233 /* Read data from process */
1234 pack.size = joe_read(*ptyfd, pack.data, 1024);
1236 /* On SUNOS 5.8, the very first read from the pty returns 0 for some reason */
1238 pack.size = joe_read(*ptyfd, pack.data, 1024);
1240 if (pack.size > 0) {
1241 /* Send data to JOE, wait for ack */
1242 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024 + pack.size);
1244 joe_read(fds[0], &pack, 1);
1247 /* Shell died: return */
1248 pack.ch = NO_MORE_DATA;
1250 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
1255 joe_read(comm[0], &m->pid, sizeof(m->pid));
1257 /* We only need comm once */
1261 /* Close other side of copy process pipe */
1266 static void mpxdied(MPX *m)
1270 while (wait(NULL) < 0 && errno == EINTR)
1280 tty_xonoffbaudrst(void)
1282 #ifdef HAVE_POSIX_TERMIOS
1283 struct termios newterm;
1285 #ifdef HAVE_SYSV_TERMIO
1286 struct termio newterm;
1293 #ifdef HAVE_POSIX_TERMIOS
1294 tcgetattr(fileno(termin), &newterm);
1296 newterm.c_iflag &= ~(IXON | IXOFF);
1298 newterm.c_iflag |= (IXON | IXOFF);
1299 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
1300 baud_reset(cfgetospeed(&newterm));
1302 #ifdef HAVE_SYSV_TERMIO
1303 ioctl(fileno(termin), TCGETA, &newterm);
1305 newterm.c_iflag &= ~(IXON | IXOFF);
1307 newterm.c_iflag |= (IXON | IXOFF);
1308 ioctl(fileno(termin), TCSETAW, &newterm);
1309 baud_reset(newterm.c_cflag & CBAUD);
1311 ioctl(fileno(termin), TIOCGETP, &arg);
1312 ioctl(fileno(termin), TIOCGETC, &targ);
1317 targ.t_startc = otarg.t_startc;
1318 targ.t_stopc = otarg.t_stopc;
1320 ioctl(fileno(termin), TIOCSETC, &targ);
1321 baud_reset(arg.sg_ospeed);