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
|
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#ifdef MSWIN
# include <windows.h>
#else
# include <unistd.h>
#endif
#define is_terminal(stream) (uv_guess_handle(fileno(stream)) == UV_TTY)
#define BUF_SIZE 0xfff
#define CTRL_C 0x03
uv_tty_t tty;
uv_tty_t tty_out;
bool owns_tty(void); // silence -Wmissing-prototypes
bool owns_tty(void)
{
#ifdef MSWIN
// XXX: We need to make proper detect owns tty
// HWND consoleWnd = GetConsoleWindow();
// DWORD dwProcessId;
// GetWindowThreadProcessId(consoleWnd, &dwProcessId);
// return GetCurrentProcessId() == dwProcessId;
return true;
#else
return getsid(0) == getpid();
#endif
}
static void walk_cb(uv_handle_t *handle, void *arg)
{
if (!uv_is_closing(handle)) {
#ifdef MSWIN
uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
#endif
uv_close(handle, NULL);
}
}
#ifndef MSWIN
static void sig_handler(int signum)
{
switch (signum) {
case SIGWINCH: {
int width, height;
uv_tty_get_winsize(&tty, &width, &height);
fprintf(stderr, "rows: %d, cols: %d\n", height, width);
return;
}
case SIGHUP:
exit(42); // arbitrary exit code to test against
return;
default:
return;
}
}
#endif
#ifdef MSWIN
static void sigwinch_cb(uv_signal_t *handle, int signum)
{
int width, height;
uv_tty_get_winsize(&tty_out, &width, &height);
fprintf(stderr, "rows: %d, cols: %d\n", height, width);
}
#endif
static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf)
{
buf->len = BUF_SIZE;
buf->base = malloc(BUF_SIZE);
}
static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
{
if (cnt <= 0) {
uv_read_stop(stream);
return;
}
int *interrupted = stream->data;
for (int i = 0; i < cnt; i++) {
if (buf->base[i] == CTRL_C) {
(*interrupted)++;
}
}
uv_loop_t write_loop;
uv_loop_init(&write_loop);
uv_tty_t out;
uv_tty_init(&write_loop, &out, fileno(stdout), 0);
uv_write_t req;
uv_buf_t b = {
.base = buf->base,
#ifdef MSWIN
.len = (ULONG)cnt
#else
.len = (size_t)cnt
#endif
};
uv_write(&req, (uv_stream_t *)&out, &b, 1, NULL);
uv_run(&write_loop, UV_RUN_DEFAULT);
uv_close((uv_handle_t *)&out, NULL);
uv_run(&write_loop, UV_RUN_DEFAULT);
if (uv_loop_close(&write_loop)) {
abort();
}
free(buf->base);
if (*interrupted >= 2) {
uv_walk(uv_default_loop(), walk_cb, NULL);
} else if (*interrupted == 1) {
fprintf(stderr, "interrupt received, press again to exit\n");
}
}
static void prepare_cb(uv_prepare_t *handle)
{
fprintf(stderr, "tty ready\n");
uv_prepare_stop(handle);
}
int main(int argc, char **argv)
{
if (!owns_tty()) {
fprintf(stderr, "process does not own the terminal\n");
exit(2);
}
if (!is_terminal(stdin)) {
fprintf(stderr, "stdin is not a terminal\n");
exit(2);
}
if (!is_terminal(stdout)) {
fprintf(stderr, "stdout is not a terminal\n");
exit(2);
}
if (!is_terminal(stderr)) {
fprintf(stderr, "stderr is not a terminal\n");
exit(2);
}
if (argc > 1) {
errno = 0;
int count = (int)strtol(argv[1], NULL, 10);
if (errno != 0) {
abort();
}
count = (count < 0 || count > 99999) ? 0 : count;
for (int i = 0; i < count; i++) {
printf("line%d\n", i);
}
fflush(stdout);
return 0;
}
int interrupted = 0;
uv_prepare_t prepare;
uv_prepare_init(uv_default_loop(), &prepare);
uv_prepare_start(&prepare, prepare_cb);
#ifndef MSWIN
uv_tty_init(uv_default_loop(), &tty, fileno(stderr), 1);
#else
uv_tty_init(uv_default_loop(), &tty, fileno(stdin), 1);
uv_tty_init(uv_default_loop(), &tty_out, fileno(stdout), 0);
int width, height;
uv_tty_get_winsize(&tty_out, &width, &height);
#endif
uv_tty_set_mode(&tty, UV_TTY_MODE_RAW);
tty.data = &interrupted;
uv_read_start((uv_stream_t *)&tty, alloc_cb, read_cb);
#ifndef MSWIN
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sig_handler;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGWINCH, &sa, NULL);
#else
uv_signal_t sigwinch_watcher;
uv_signal_init(uv_default_loop(), &sigwinch_watcher);
uv_signal_start(&sigwinch_watcher, sigwinch_cb, SIGWINCH);
#endif
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
#ifndef MSWIN
// XXX: Without this the SIGHUP handler is skipped on some systems.
sleep(100);
#endif
return 0;
}
|