#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <math.h>
#include <fcntl.h>
#ifdef _WIN32
#include <windows.h>
#include <conio.h>
#include <io.h>
#else
#include <signal.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#endif
#include "readline_tty.h"
static int ctrl_c_pressed;
#ifdef _WIN32
#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
static BOOL WINAPI ctrl_handler(DWORD type)
{
if (type == CTRL_C_EVENT) {
ctrl_c_pressed++;
if (ctrl_c_pressed >= 4) {
return FALSE;
} else {
return TRUE;
}
} else {
return FALSE;
}
}
int readline_tty_init(void)
{
HANDLE handle;
CONSOLE_SCREEN_BUFFER_INFO info;
int n_cols;
handle = (HANDLE)_get_osfhandle(0);
SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT);
_setmode(0, _O_BINARY);
handle = (HANDLE)_get_osfhandle(1);
SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING);
SetConsoleCtrlHandler(ctrl_handler, TRUE);
n_cols = 80;
if (GetConsoleScreenBufferInfo(handle, &info)) {
n_cols = info.dwSize.X;
}
return n_cols;
}
static void set_processed_input(BOOL enable)
{
DWORD mode;
HANDLE handle;
handle = (HANDLE)_get_osfhandle(0);
if (!GetConsoleMode(handle, &mode))
return;
if (enable)
mode |= ENABLE_PROCESSED_INPUT;
else
mode &= ~ENABLE_PROCESSED_INPUT;
SetConsoleMode(handle, mode);
}
#else
static struct termios oldtty;
static int old_fd0_flags;
static void term_exit(void)
{
tcsetattr (0, TCSANOW, &oldtty);
fcntl(0, F_SETFL, old_fd0_flags);
}
static void sigint_handler(int signo)
{
ctrl_c_pressed++;
if (ctrl_c_pressed >= 4) {
signal(SIGINT, SIG_DFL);
}
}
int readline_tty_init(void)
{
struct termios tty;
struct sigaction sa;
struct winsize ws;
int n_cols;
tcgetattr (0, &tty);
oldtty = tty;
old_fd0_flags = fcntl(0, F_GETFL);
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
tty.c_oflag |= OPOST;
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
tty.c_cflag &= ~(CSIZE|PARENB);
tty.c_cflag |= CS8;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
tcsetattr (0, TCSANOW, &tty);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sigint_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
atexit(term_exit);
n_cols = 80;
if (ioctl(0, TIOCGWINSZ, &ws) == 0 &&
ws.ws_col >= 4 && ws.ws_row >= 4) {
n_cols = ws.ws_col;
}
return n_cols;
}
#endif
void term_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
void term_flush(void)
{
fflush(stdout);
}
const char *readline_tty(ReadlineState *s,
const char *prompt, BOOL multi_line)
{
int len, i, ctrl_c_count, c, ret;
const char *ret_str;
uint8_t buf[128];
#ifdef _WIN32
set_processed_input(FALSE);
#endif
ret_str = NULL;
readline_start(s, prompt, FALSE);
ctrl_c_count = 0;
while (ret_str == NULL) {
len = read(0, buf, sizeof(buf));
if (len == 0)
break;
for(i = 0; i < len; i++) {
c = buf[i];
#ifdef _WIN32
if (c == 3) {
ctrl_c_pressed++;
} else
#endif
{
ret = readline_handle_byte(s, c);
if (ret == READLINE_RET_EXIT) {
goto done;
} else if (ret == READLINE_RET_ACCEPTED) {
ret_str = (const char *)s->term_cmd_buf;
goto done;
}
ctrl_c_count = 0;
}
}
if (ctrl_c_pressed) {
ctrl_c_pressed = 0;
if (ctrl_c_count == 0) {
printf("(Press Ctrl-C again to quit)\n");
ctrl_c_count++;
} else {
printf("Exiting.\n");
break;
}
}
}
done:
#ifdef _WIN32
set_processed_input(TRUE);
#endif
return ret_str;
}
BOOL readline_is_interrupted(void)
{
BOOL ret;
ret = (ctrl_c_pressed != 0);
ctrl_c_pressed = 0;
return ret;
}