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