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
|
#include "termin.h"
#include "c23-fallback.h"
#include <termios.h>
#include <stdio.h>
#include <stdbool.h>
#ifndef __STDC_NO_THREADS__
# include <threads.h>
#endif
#include <ctype.h>
#include <string.h>
/* Store the flags as they were set before termin_unbuffered. */
static tcflag_t termin_echo;
/* The flags that we manipulate. */
#define TERMIN_FLAGS (ECHO | ECHONL | ICANON)
void termin_reset(void) {
struct termios term;
tcgetattr(0, &term);
term.c_lflag |= termin_echo;
tcsetattr(0, TCSAFLUSH, &term);
}
void termin_unbuffered(void) {
// have stdin unbuffered on stdio level
setvbuf(stdin, 0, _IONBF, 0);
// store the terminal settings with respect to the flags
struct termios term;
tcgetattr(0, &term);
termin_echo = term.c_lflag & TERMIN_FLAGS;
// install exit handlers
atexit(termin_reset);
at_quick_exit(termin_reset);
// change the terminal settings to raw mode
term.c_lflag &= ~TERMIN_FLAGS;
tcsetattr(0, TCSAFLUSH, &term);
}
static unsigned minc = 0;
static unsigned maxc = UCHAR_MAX;
static
void term_init(void) {
while (!termin_trans[minc]) ++minc;
while (!termin_trans[maxc]) --maxc;
}
#ifndef __STDC_NO_THREADS__
static once_flag term_once = ONCE_FLAG_INIT;
#endif
static
bool termin_read_csi(size_t len, char command[len]) {
for (char *p = command, *stop = command+len-1; p < stop; ++p) {
int c = getchar();
// catch all: EOF, 0, control characters
if (c < ' ') return false;
p[0] = c;
if ('@' <= c && c <= '~') {
p[1] = 0;
return true;
}
}
return false;
}
char const* termin_read_esc(size_t len, char command[len]) {
command[1] = getchar();
switch (command[1]) {
case '[':
return
termin_read_csi(len-2, command+2)
? command
: 0;
case 'N':
case 'O':
case 'Z':
case '%':
command[2] = getchar();
command[3] = 0;
return command;
default:
ungetc(command[1], stdin);
return nullptr;
}
}
char termin_translate(char const* command) {
call_once(&term_once, term_init);
for (unsigned c = minc; c <= maxc; ++c) {
if (termin_trans[c] && !strcmp(command, termin_trans[c])) {
return c;
}
}
return 0;
}
|