1 /* $MirOS: contrib/code/jupp/tty.c,v 1.22 2016/10/30 02:38:35 tg Exp $ */
3 * UNIX Tty and Process interface
5 * (C) 1992 Joseph H. Allen
7 * This file is part of JOE (Joe's Own Editor)
12 #include <sys/types.h>
13 #ifdef HAVE_SYS_STAT_H
16 #ifdef HAVE_SYS_IOCTL_H
17 #include <sys/ioctl.h>
23 #ifdef HAVE_SYS_WAIT_H
26 #ifdef HAVE_SYS_PARAM_H
27 #include <sys/param.h>
41 /* We use the defines in sys/ioctl to determine what type
42 * tty interface the system uses and what type of system
45 #ifdef HAVE_POSIX_TERMIOS
47 # ifdef HAVE_SYS_TERMIOS_H
48 # include <sys/termios.h>
51 # ifdef HAVE_SYSV_TERMIO
53 # include <sys/termio.h>
70 /* Straight from the GNU autoconf texinfo documentation */
71 #ifdef TIME_WITH_SYS_TIME
72 # include <sys/time.h>
75 # ifdef HAVE_SYS_TIME_H
76 # include <sys/time.h>
82 /* I'm not sure if SCO_UNIX and ISC have __svr4__ defined, but I think
86 #include <sys/stream.h>
104 /* JOE include files */
113 /** Aliased defines **/
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
120 #define O_NDELAY O_NONBLOCK
123 #define O_NDELAY FNDELAY
127 /* Some systems define this, some don't */
129 #define sigmask(x) (1<<((x)-1))
132 /* Some BSDs don't have TILDE */
137 /* Global configuration variables */
139 int noxon = 0; /* Set if ^S/^Q processing should be disabled */
140 int Baud = 0; /* Baud rate from joerc, cmd line or environment */
145 FILE *termout = NULL;
147 /* Original state of tty */
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 */
161 /* Output buffer, index and size */
163 unsigned char *obuf = NULL;
169 unsigned baud; /* Bits per second */
170 unsigned long upc; /* Microseconds per character */
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?)
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
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 */
200 /* TTY mode flag. 1 for open, 0 for closed */
201 static int ttymode = 0;
203 /* Signal state flag. 1 for joe, 0 for normal */
204 static int ttysig = 0;
206 /* Stuff for shell windows */
208 static pid_t kbdpid; /* PID of kbd client */
209 static int ackkbd = -1; /* Editor acks keyboard client to this */
211 static int mpxfd; /* Editor reads packets from this fd */
212 static int mpxsfd; /* Clients send packets to this fd */
215 static int tty_accept = NO_MORE_DATA; /* =-1 if we have last packet */
221 unsigned char data[1024];
226 /* Set signals for JOE */
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);
238 /* Restore signals for exiting */
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);
250 /* Open terminal and set signals */
258 /* Close terminal and restore signals */
266 static int winched = 0;
268 /* Window size interrupt handler */
269 static RETSIGTYPE winchd(int unused)
272 REINSTALL_SIGHANDLER(SIGWINCH, winchd);
280 static RETSIGTYPE dotick(int unused)
293 joe_set_signal(SIGALRM, dotick);
299 static void baud_reset(int);
305 #ifdef HAVE_POSIX_TERMIOS
306 struct termios newterm;
308 #ifdef HAVE_SYSV_TERMIO
309 struct termio newterm;
313 struct ltchars ltarg;
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");
323 joe_set_signal(SIGWINCH, winchd);
333 #ifdef HAVE_POSIX_TERMIOS
334 tcgetattr(fileno(termin), &oldterm);
338 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
340 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
342 newterm.c_cc[VMIN] = 1;
343 newterm.c_cc[VTIME] = 0;
344 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
345 bbaud = cfgetospeed(&newterm);
347 #ifdef HAVE_SYSV_TERMIO
348 ioctl(fileno(termin), TCGETA, &oldterm);
352 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR | IXON | IXOFF);
354 newterm.c_iflag &= ~(ICRNL | IGNCR | INLCR);
356 newterm.c_cc[VMIN] = 1;
357 newterm.c_cc[VTIME] = 0;
358 ioctl(fileno(termin), TCSETAW, &newterm);
359 bbaud = (newterm.c_cflag & CBAUD);
361 ioctl(fileno(termin), TIOCGETP, &arg);
362 ioctl(fileno(termin), TIOCGETC, &targ);
363 ioctl(fileno(termin), TIOCGLTC, <arg);
367 arg.sg_flags = ((arg.sg_flags & ~(ECHO | CRMOD | XTABS | ALLDELAY | TILDE)) | CBREAK);
382 ioctl(fileno(termin), TIOCSETN, &arg);
383 ioctl(fileno(termin), TIOCSETC, &targ);
384 ioctl(fileno(termin), TIOCSLTC, <arg);
385 bbaud = arg.sg_ospeed;
392 baud_reset(int bbaud)
398 while (x < NELEM(speeds))
399 if (bbaud == speeds[x]) {
400 baud = speeds[x + 1];
408 upc = DIVIDEND / baud;
414 obufsiz = 1000000 / (TIMES * upc);
420 obuf = (unsigned char *) joe_malloc(obufsiz);
439 #ifdef HAVE_POSIX_TERMIOS
440 tcsetattr(fileno(termin), TCSADRAIN, &oldterm);
442 #ifdef HAVE_SYSV_TERMIO
443 ioctl(fileno(termin), TCSETAW, &oldterm);
445 ioctl(fileno(termin), TIOCSETN, &oarg);
446 ioctl(fileno(termin), TIOCSETC, &otarg);
447 ioctl(fileno(termin), TIOCSLTC, &oltarg);
454 /* Timer interrupt handler */
457 static RETSIGTYPE dosig(int unused)
462 /* FLush output and check for typeahead */
464 #ifdef HAVE_SETITIMER
466 static void maskit(void)
471 sigaddset(&set, SIGALRM);
472 sigprocmask(SIG_SETMASK, &set, NULL);
475 static void unmaskit(void)
480 sigprocmask(SIG_SETMASK, &set, NULL);
483 static void pauseit(void)
492 static void maskit(void)
494 sigsetmask(sigmask(SIGALRM));
497 static void unmaskit(void)
502 static void pauseit(void)
514 unsigned long usec = obufp * upc; /* No. usecs this write should take */
516 #ifdef HAVE_SETITIMER
517 if (usec >= 50000 && baud < 9600) {
518 struct itimerval a, b;
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;
525 joe_set_signal(SIGALRM, dosig);
528 setitimer(ITIMER_REAL, &a, &b);
529 joe_write(fileno(termout), obuf, obufp);
534 joe_write(fileno(termout), obuf, obufp);
538 joe_write(fileno(termout), obuf, obufp);
541 if (baud < 9600 && usec / 1000)
550 /* Ack previous packet */
551 if (ackkbd != -1 && tty_accept != NO_MORE_DATA && !have) {
554 if (pack.who && pack.who->func)
555 joe_write(pack.who->ackfd, &c, 1);
557 joe_write(ackkbd, &c, 1);
558 tty_accept = NO_MORE_DATA;
561 /* Check for typeahead or next packet */
563 if (!have && !leave) {
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);
570 tty_accept = pack.ch;
572 fcntl(mpxfd, F_SETFL, 0);
574 /* Set terminal input to non-blocking */
575 fcntl(fileno(termin), F_SETFL, O_NDELAY);
578 if (read(fileno(termin), &havec, 1) == 1)
581 /* Set terminal back to blocking */
582 fcntl(fileno(termin), F_SETFL, 0);
588 /* Read next character from input */
590 void mpxdied(MPX *m);
603 new_time = time(NULL);
604 if (new_time != last_time) {
605 last_time = new_time;
621 if (!have) { /* Wait for input */
622 stat_ = read(mpxfd, &pack, sizeof(struct packet) - 1024);
624 if (pack.size && stat_ > 0) {
625 joe_read(mpxfd, pack.data, pack.size);
626 } else if (stat_ < 1) {
627 if (winched || ticked)
632 tty_accept = pack.ch;
635 if (pack.who) { /* Got bknd input */
636 if (tty_accept != NO_MORE_DATA) {
637 if (pack.who->func) {
638 pack.who->func(pack.who->object, pack.data, pack.size);
645 if (tty_accept != NO_MORE_DATA) {
659 if (read(fileno(termin), &havec, 1) < 1) {
660 if (winched || ticked)
670 /* Write string to output */
672 void ttputs(unsigned char *s)
675 obuf[obufp++] = *s++;
676 if (obufp == obufsiz)
681 /* Get window size */
683 void ttgtsz(int *x, int *y)
686 struct ttysize getit;
689 struct winsize getit;
697 if (ioctl(fileno(termout), TIOCGSIZE, &getit) != -1) {
703 if (ioctl(fileno(termout), TIOCGWINSZ, &getit) != -1) {
711 void ttshell(unsigned char *cmd)
713 int x, omode = ttymode;
718 if ((x = fork()) != 0) {
726 execl(sh, sh, "-c", cmd, NULL);
728 fprintf(stderr, "You are at the command shell. Type 'exit' to return\n");
735 /* Create keyboard task */
737 static int mpxresume(void)
745 tty_accept = NO_MORE_DATA;
747 if (!(kbdpid = fork())) {
754 sta = joe_read(fileno(termin), &c, 1);
756 pack.ch = NO_MORE_DATA;
760 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
761 } while (joe_read(fds[0], &pack, 1) == 1);
769 /* Kill keyboard task */
771 static void mpxsusp(void)
775 while (wait(NULL) < 0 && errno == EINTR)
781 /* We used to leave the keyboard copy task around during suspend, but
782 Cygwin gets confused when two processes are waiting for input and you
783 change the tty from raw to cooked (on the call to ttopnn()): the keyboard
784 process was stuck in cooked until he got a carriage return- then he
785 switched back to raw (he's supposed to switch to raw without waiting for
786 the end of line). Probably this should be done for ttshell() as well. */
796 fprintf(stderr, "You have suspended the program. Type 'fg' to return\n");
807 /* Stuff for asynchronous I/O multiplexing. We do not use streams or
808 select() because joe needs to work on versions of UNIX which predate
809 these calls. Instead, when there is multiple async sources, we use
810 helper processes which packetize data from the sources. A header on each
811 packet indicates the source. There is no guarentee that packets getting
812 written to the same pipe don't get interleaved, but you can reasonable
813 rely on it with small packets. */
815 /* This code will explode if pipe, fork, etc. fail. --mirabilos */
817 static int mpxstart(void)
828 return (mpxresume());
831 static void mpxend(void)
841 /* Get a pty/tty pair. Returns open pty in 'ptyfd' and returns tty name
842 * string in static buffer or NULL if couldn't get a pair.
855 /* Newer sgi machines can do it the __svr4__ way, but old ones can't */
857 extern char *_getpty(int *fildes, int oflag, mode_t mode, int nofork);
859 static unsigned char *getpty(int *ptyfd)
861 return (unsigned char *)_getpty(ptyfd, O_RDWR, 0600, 0);
867 /* Strange streams way */
869 extern char *ptsname(int);
871 static unsigned char *getpty(int *ptyfd)
875 *ptyfd = fdm = open("/dev/ptmx", O_RDWR);
878 return (unsigned char *)ptsname(fdm);
884 /* BSD function, present in libc5 and glibc2 and (duh) the BSDs */
886 static unsigned char *getpty(int *ptyfd)
888 static unsigned char name[32];
891 if (openpty(ptyfd, &ttyfd, name, NULL, NULL) == 0)
898 /* The normal way: for each possible pty/tty pair, try to open the pty and
899 * then the corresponding tty. If both could be opened, close them both and
900 * then re-open the pty. If that succeeded, return with the opened pty and the
903 * Logically you should only have to succeed in opening the pty- but the
904 * permissions may be set wrong on the tty, so we have to try that too.
905 * We close them both and re-open the pty because we want the forked process
906 * to open the tty- that way it gets to be the controlling tty for that
907 * process and the process gets to be the session leader.
910 static unsigned char *getpty(int *ptyfd)
913 unsigned char *orgpwd = pwd();
914 static unsigned char **ptys = NULL;
915 static unsigned char *ttydir;
916 static unsigned char *ptydir;
917 static unsigned char ttyname[32];
920 ttydir = US "/dev/pty/";
921 ptydir = US "/dev/ptym/"; /* HPUX systems */
922 if (chpwd(ptydir) || !(ptys = rexpnd(US "pty*")))
924 ttydir = ptydir = US "/dev/"; /* Everyone else */
926 ptys = rexpnd(US "pty*");
932 for (fd = 0; ptys[fd]; ++fd) {
933 strlcpy((char *)ttyname, (char *)ptydir, 32);
934 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
935 if ((*ptyfd = open((char *)ttyname, O_RDWR)) >= 0) {
937 strlcpy((char *)ttyname, (char *)ttydir, 32);
938 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
940 x = open((char *)ttyname, O_RDWR);
944 strlcpy((char *)ttyname, (char *)ptydir, 32);
945 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
946 *ptyfd = open((char *)ttyname, O_RDWR);
948 strlcpy((char *)ttyname, (char *)ttydir, 32);
949 strlcat((char *)ttyname, (char *)(ptys[fd]), 32);
963 /* Shell dies signal handler. Puts pty in non-block mode so
964 * that read returns with <1 when all data from process has
968 static RETSIGTYPE death(int unused)
970 fcntl(death_fd,F_SETFL,O_NDELAY);
976 #define SIGCHLD SIGCLD
979 /* Build a new environment, but replace one variable */
981 extern unsigned char **mainenv;
983 static unsigned char **newenv(unsigned char **old, unsigned char *s)
988 for (x = 0; old[x]; ++x) ;
989 new = (unsigned char **) joe_malloc((x + 2) * sizeof(unsigned char *));
991 for (x = 0, y = 0; old[x]; ++x) {
992 for (z = 0; s[z] != '='; ++z)
993 if (s[z] != old[x][z])
1007 /* Create a shell process */
1009 MPX *mpxmk(int *ptyfd, const unsigned char *cmd, unsigned char **args, void (*func) (/* ??? */), void *object, void (*die) (/* ??? */), void *dieobj)
1011 unsigned char buf[80];
1017 unsigned char *name;
1019 /* Get pty/tty pair */
1020 if (!(name = getpty(ptyfd)))
1023 /* Find free slot */
1024 for (x = 0; x != NPROC; ++x)
1025 if (!asyncs[x].func) {
1032 /* PID number pipe */
1036 /* Acknowledgement pipe */
1038 /* don't leak in error case */
1046 /* Fixes cygwin console bug: if you fork() with inverse video he assumes you want
1047 * ESC [ 0 m to keep it in inverse video from then on. */
1048 set_attr(maint->t,0);
1053 /* Bump no. current async inputs to joe */
1056 /* Start input multiplexer */
1066 /* Remember callback function */
1072 /* Create processes... */
1073 if (!(m->kpid = fork())) {
1074 /* This process copies data from shell to joe */
1075 /* After each packet it sends to joe it waits for
1076 an acknowledgement from joe so that it can not get
1077 too far ahead with buffering */
1079 /* Close joe side of pipes */
1083 /* Flag which indicates child died */
1086 joe_set_signal(SIGCHLD, death);
1088 if (!(pid = fork())) {
1089 /* This process becomes the shell */
1092 /* Close pty (we only need tty) */
1095 /* All of this stuff is for disassociating ourself from
1096 controlling tty (session leader) and starting a new
1097 session. This is the most non-portable part of UNIX- second
1098 only to pty/tty pair creation. */
1099 #ifndef HAVE_LOGIN_TTY
1102 x = open("/dev/tty", O_RDWR);
1103 ioctl(x, TIOCNOTTY, 0);
1106 setsid(); /* I think you do setprgp(0,0) on systems with no setsid() */
1108 /* http://mail-index.netbsd.org/pkgsrc-bugs/2011/06/13/msg043281.html */
1109 #ifndef SETPGRP_VOID
1118 for (x = 0; x != 32; ++x)
1119 close(x); /* Yes, this is quite a kludge... all in the
1120 name of portability */
1123 if ((x = open((char *)name, O_RDWR)) != -1) { /* Standard input */
1124 unsigned char **env = newenv(mainenv, US "TERM=");
1126 #ifdef HAVE_LOGIN_TTY
1130 /* This tells the fd that it's a tty (I think) */
1132 ioctl(x, I_PUSH, "ptem");
1133 ioctl(x, I_PUSH, "ldterm");
1136 /* Open stdout, stderr */
1137 if (dup(x)) {} /* standard output */
1138 if (dup(x)) {} /* standard error */
1140 * yes, stdin, stdout, and stderr must
1141 * all be open for reading and writing.
1142 * On some systems the shell assumes this.
1146 /* We could probably have a special TTY set-up for JOE, but for now
1147 * we'll just use the TTY setup for the TTY was was run on */
1148 #ifdef HAVE_POSIX_TERMIOS
1149 tcsetattr(0, TCSADRAIN, &oldterm);
1151 #ifdef HAVE_SYSV_TERMIO
1152 ioctl(0, TCSETAW, &oldterm);
1154 ioctl(0, TIOCSETN, &oarg);
1155 ioctl(0, TIOCSETC, &otarg);
1156 ioctl(0, TIOCSLTC, &oltarg);
1160 /* Execute the shell */
1161 execve((const char *)cmd, (char **)args, (char **)env);
1163 /* If shell didn't execute */
1164 joe_snprintf_1((char *)buf,sizeof(buf),"Couldn't execute shell '%s'\n",cmd);
1165 if (write(0,(char *)buf,strlen((char *)buf))) {}
1172 /* Tell JOE PID of shell */
1173 joe_write(comm[1], &pid, sizeof(pid));
1175 /* sigpipe should be ignored here. */
1177 /* This process copies data from shell to JOE until EOF. It creates a packet
1181 /* We don't really get EOF from a pty- it would just wait forever
1182 until someone else writes to the tty. So: when the shell
1183 dies, the child died signal handler death() puts pty in non-block
1184 mode. This allows us to read any remaining data- then
1185 read returns 0 and we know we're done. */
1191 /* Read data from process */
1192 pack.size = joe_read(*ptyfd, pack.data, 1024);
1194 /* On SUNOS 5.8, the very first read from the pty returns 0 for some reason */
1196 pack.size = joe_read(*ptyfd, pack.data, 1024);
1198 if (pack.size > 0) {
1199 /* Send data to JOE, wait for ack */
1200 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024 + pack.size);
1202 joe_read(fds[0], &pack, 1);
1205 /* Shell died: return */
1206 pack.ch = NO_MORE_DATA;
1208 joe_write(mpxsfd, &pack, sizeof(struct packet) - 1024);
1213 joe_read(comm[0], &m->pid, sizeof(m->pid));
1215 /* We only need comm once */
1219 /* Close other side of copy process pipe */
1224 void mpxdied(MPX *m)
1228 while (wait(NULL) < 0 && errno == EINTR)
1237 tty_xonoffbaudrst(void)
1239 #ifdef HAVE_POSIX_TERMIOS
1240 struct termios newterm;
1242 #ifdef HAVE_SYSV_TERMIO
1243 struct termio newterm;
1250 #ifdef HAVE_POSIX_TERMIOS
1251 tcgetattr(fileno(termin), &newterm);
1253 newterm.c_iflag &= ~(IXON | IXOFF);
1255 newterm.c_iflag |= (IXON | IXOFF);
1256 tcsetattr(fileno(termin), TCSADRAIN, &newterm);
1257 baud_reset(cfgetospeed(&newterm));
1259 #ifdef HAVE_SYSV_TERMIO
1260 ioctl(fileno(termin), TCGETA, &newterm);
1262 newterm.c_iflag &= ~(IXON | IXOFF);
1264 newterm.c_iflag |= (IXON | IXOFF);
1265 ioctl(fileno(termin), TCSETAW, &newterm);
1266 baud_reset(newterm.c_cflag & CBAUD);
1268 ioctl(fileno(termin), TIOCGETP, &arg);
1269 ioctl(fileno(termin), TIOCGETC, &targ);
1274 targ.t_startc = otarg.t_startc;
1275 targ.t_stopc = otarg.t_stopc;
1277 ioctl(fileno(termin), TIOCSETC, &targ);
1278 baud_reset(arg.sg_ospeed);