Annotation of src/usr.bin/mg/ttyio.c, Revision 1.36
1.36 ! bcallah 1: /* $OpenBSD: ttyio.c,v 1.35 2014/03/20 07:47:29 lum Exp $ */
1.28 kjell 2:
3: /* This file is in the public domain. */
1.13 niklas 4:
1.1 deraadt 5: /*
1.5 millert 6: * POSIX terminal I/O.
1.1 deraadt 7: *
1.23 vincent 8: * The functions in this file negotiate with the operating system for
9: * keyboard characters, and write characters to the display in a barely
10: * buffered fashion.
1.1 deraadt 11: */
12:
1.36 ! bcallah 13: #include <sys/ioctl.h>
! 14: #include <sys/queue.h>
! 15: #include <sys/time.h>
1.26 db 16: #include <sys/types.h>
1.36 ! bcallah 17: #include <errno.h>
1.26 db 18: #include <fcntl.h>
1.34 deraadt 19: #include <poll.h>
1.36 ! bcallah 20: #include <signal.h>
! 21: #include <stdio.h>
! 22: #include <stdlib.h>
! 23: #include <string.h>
! 24: #include <term.h>
1.26 db 25: #include <termios.h>
1.36 ! bcallah 26: #include <unistd.h>
! 27:
! 28: #include "def.h"
1.1 deraadt 29:
1.23 vincent 30: #define NOBUF 512 /* Output buffer size. */
1.1 deraadt 31:
1.7 millert 32: #ifndef TCSASOFT
33: #define TCSASOFT 0
34: #endif
35:
1.24 vincent 36: int ttstarted;
1.23 vincent 37: char obuf[NOBUF]; /* Output buffer. */
1.31 deraadt 38: size_t nobuf; /* Buffer count. */
1.23 vincent 39: struct termios oldtty; /* POSIX tty settings. */
1.8 millert 40: struct termios newtty;
1.23 vincent 41: int nrow; /* Terminal size, rows. */
42: int ncol; /* Terminal size, columns. */
1.1 deraadt 43:
1.8 millert 44: /*
1.10 millert 45: * This function gets called once, to set up the terminal.
1.8 millert 46: * On systems w/o TCSASOFT we turn off off flow control,
47: * which isn't really the right thing to do.
1.1 deraadt 48: */
1.8 millert 49: void
1.23 vincent 50: ttopen(void)
1.1 deraadt 51: {
1.12 millert 52: if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
1.16 mickey 53: panic("standard input and output must be a terminal");
1.12 millert 54:
1.8 millert 55: if (ttraw() == FALSE)
56: panic("aborting due to terminal initialize failure");
57: }
1.1 deraadt 58:
1.8 millert 59: /*
60: * This function sets the terminal to RAW mode, as defined for the current
61: * shell. This is called both by ttopen() above and by spawncli() to
62: * get the current terminal settings and then change them to what
63: * mg expects. Thus, tty changes done while spawncli() is in effect
64: * will be reflected in mg.
65: */
66: int
1.23 vincent 67: ttraw(void)
1.8 millert 68: {
69: if (tcgetattr(0, &oldtty) < 0) {
1.35 lum 70: dobeep();
1.8 millert 71: ewprintf("ttopen can't get terminal attributes");
1.23 vincent 72: return (FALSE);
1.8 millert 73: }
74: (void)memcpy(&newtty, &oldtty, sizeof(newtty));
75: /* Set terminal to 'raw' mode and ignore a 'break' */
76: newtty.c_cc[VMIN] = 1;
77: newtty.c_cc[VTIME] = 0;
78: newtty.c_iflag |= IGNBRK;
79: newtty.c_iflag &= ~(BRKINT | PARMRK | INLCR | IGNCR | ICRNL | IXON);
80: newtty.c_oflag &= ~OPOST;
81: newtty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
1.7 millert 82:
83: #if !TCSASOFT
1.8 millert 84: /*
85: * If we don't have TCSASOFT, force terminal to
86: * 8 bits, no parity.
87: */
88: newtty.c_iflag &= ~ISTRIP;
89: newtty.c_cflag &= ~(CSIZE | PARENB);
90: newtty.c_cflag |= CS8;
1.7 millert 91: #endif
1.8 millert 92: if (tcsetattr(0, TCSASOFT | TCSADRAIN, &newtty) < 0) {
1.35 lum 93: dobeep();
1.8 millert 94: ewprintf("ttopen can't tcsetattr");
1.23 vincent 95: return (FALSE);
1.8 millert 96: }
1.24 vincent 97: ttstarted = 1;
98:
1.23 vincent 99: return (TRUE);
1.8 millert 100: }
1.1 deraadt 101:
1.8 millert 102: /*
103: * This function gets called just before we go back home to the shell.
104: * Put all of the terminal parameters back.
105: * Under UN*X this just calls ttcooked(), but the ttclose() hook is in
106: * because vttidy() in display.c expects it for portability reasons.
107: */
108: void
1.23 vincent 109: ttclose(void)
1.8 millert 110: {
1.24 vincent 111: if (ttstarted) {
112: if (ttcooked() == FALSE)
113: panic(""); /* ttcooked() already printf'd */
114: ttstarted = 0;
115: }
1.1 deraadt 116: }
117:
118: /*
1.8 millert 119: * This function restores all terminal settings to their default values,
120: * in anticipation of exiting or suspending the editor.
1.1 deraadt 121: */
1.8 millert 122: int
1.23 vincent 123: ttcooked(void)
1.1 deraadt 124: {
125: ttflush();
1.8 millert 126: if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) < 0) {
1.35 lum 127: dobeep();
1.8 millert 128: ewprintf("ttclose can't tcsetattr");
1.23 vincent 129: return (FALSE);
1.8 millert 130: }
1.23 vincent 131: return (TRUE);
1.1 deraadt 132: }
133:
134: /*
1.8 millert 135: * Write character to the display. Characters are buffered up,
136: * to make things a little bit more efficient.
1.1 deraadt 137: */
1.11 millert 138: int
1.19 millert 139: ttputc(int c)
1.1 deraadt 140: {
141: if (nobuf >= NOBUF)
142: ttflush();
143: obuf[nobuf++] = c;
1.23 vincent 144: return (c);
1.1 deraadt 145: }
146:
147: /*
148: * Flush output.
149: */
1.8 millert 150: void
1.23 vincent 151: ttflush(void)
1.1 deraadt 152: {
1.26 db 153: ssize_t written;
154: char *buf = obuf;
1.22 deraadt 155:
1.19 millert 156: if (nobuf == 0)
157: return;
1.5 millert 158:
1.20 millert 159: while ((written = write(fileno(stdout), buf, nobuf)) != nobuf) {
1.32 reyk 160: if (written == -1) {
161: if (errno == EINTR)
162: continue;
1.8 millert 163: panic("ttflush write failed");
1.32 reyk 164: }
1.20 millert 165: buf += written;
166: nobuf -= written;
1.1 deraadt 167: }
1.21 millert 168: nobuf = 0;
1.1 deraadt 169: }
170:
171: /*
1.23 vincent 172: * Read character from terminal. All 8 bits are returned, so that you
173: * can use a multi-national terminal.
1.1 deraadt 174: */
1.8 millert 175: int
1.23 vincent 176: ttgetc(void)
1.1 deraadt 177: {
1.9 millert 178: char c;
1.30 deraadt 179: ssize_t ret;
1.5 millert 180:
1.18 deraadt 181: do {
1.27 deraadt 182: ret = read(STDIN_FILENO, &c, 1);
1.18 deraadt 183: if (ret == -1 && errno == EINTR) {
184: if (winch_flag) {
1.29 kjell 185: redraw(0, 0);
1.18 deraadt 186: winch_flag = 0;
187: }
1.33 florian 188: } else if (ret == -1 && errno == EIO)
189: panic("lost stdin");
190: else if (ret == 1)
1.18 deraadt 191: break;
192: } while (1);
1.25 vincent 193: return ((int) c) & 0xFF;
1.1 deraadt 194: }
195:
1.8 millert 196: /*
197: * Returns TRUE if there are characters waiting to be read.
198: */
199: int
1.29 kjell 200: charswaiting(void)
1.8 millert 201: {
202: int x;
203:
1.30 deraadt 204: return ((ioctl(0, FIONREAD, &x) < 0) ? 0 : x);
1.8 millert 205: }
206:
207: /*
208: * panic - just exit, as quickly as we can.
209: */
1.15 art 210: void
1.19 millert 211: panic(char *s)
1.8 millert 212: {
1.33 florian 213: static int panicking = 0;
214:
215: if (panicking)
216: return;
217: else
218: panicking = 1;
1.24 vincent 219: ttclose();
1.8 millert 220: (void) fputs("panic: ", stderr);
221: (void) fputs(s, stderr);
222: (void) fputc('\n', stderr);
1.12 millert 223: exit(1);
1.8 millert 224: }
225:
1.1 deraadt 226: /*
1.14 art 227: * This function returns FALSE if any characters have showed up on the
1.26 db 228: * tty before 'msec' milliseconds.
1.1 deraadt 229: */
1.8 millert 230: int
1.14 art 231: ttwait(int msec)
1.1 deraadt 232: {
1.34 deraadt 233: struct pollfd pfd[1];
1.5 millert 234:
1.34 deraadt 235: pfd[0].fd = 0;
236: pfd[0].events = POLLIN;
1.5 millert 237:
1.34 deraadt 238: if ((poll(pfd, 1, msec)) == 0)
1.8 millert 239: return (TRUE);
240: return (FALSE);
1.1 deraadt 241: }