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