diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 39d19aa1..f45ce2b0 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -380,28 +380,35 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* half-duplex so read-state can safely overlap write-state. */ #define UV_TTY_PRIVATE_FIELDS \ HANDLE handle; \ - HANDLE read_line_handle; \ - uv_buf_t read_line_buffer; \ - HANDLE read_raw_wait; \ - DWORD original_console_mode; \ - /* Fields used for translating win */ \ - /* keystrokes into vt100 characters */ \ - char last_key[8]; \ - unsigned char last_key_offset; \ - unsigned char last_key_len; \ - INPUT_RECORD last_input_record; \ - WCHAR last_utf16_high_surrogate; \ - /* utf8-to-utf16 conversion state */ \ - unsigned char utf8_bytes_left; \ - unsigned int utf8_codepoint; \ - /* eol conversion state */ \ - unsigned char previous_eol; \ - /* ansi parser state */ \ - unsigned char ansi_parser_state; \ - unsigned char ansi_csi_argc; \ - unsigned short ansi_csi_argv[4]; \ - COORD saved_position; \ - WORD saved_attributes; + union { \ + struct { \ + /* Used for readable TTY handles */ \ + HANDLE read_line_handle; \ + uv_buf_t read_line_buffer; \ + HANDLE read_raw_wait; \ + DWORD original_console_mode; \ + /* Fields used for translating win keystrokes into vt100 characters */ \ + char last_key[8]; \ + unsigned char last_key_offset; \ + unsigned char last_key_len; \ + WCHAR last_utf16_high_surrogate; \ + INPUT_RECORD last_input_record; \ + }; \ + struct { \ + /* Used for writable TTY handles */ \ + /* utf8-to-utf16 conversion state */ \ + unsigned int utf8_codepoint; \ + unsigned char utf8_bytes_left; \ + /* eol conversion state */ \ + unsigned char previous_eol; \ + /* ansi parser state */ \ + unsigned char ansi_parser_state; \ + unsigned char ansi_csi_argc; \ + unsigned short ansi_csi_argv[4]; \ + COORD saved_position; \ + WORD saved_attributes; \ + }; \ + }; #define UV_POLL_PRIVATE_FIELDS \ SOCKET socket; \ diff --git a/src/win/internal.h b/src/win/internal.h index 25b4e937..212b2b16 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -74,9 +74,10 @@ #define UV_HANDLE_PIPESERVER 0x02000000 /* Only used by uv_tty_t handles. */ -#define UV_HANDLE_TTY_RAW 0x01000000 -#define UV_HANDLE_TTY_SAVED_POSITION 0x02000000 -#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x04000000 +#define UV_HANDLE_TTY_READABLE 0x01000000 +#define UV_HANDLE_TTY_RAW 0x02000000 +#define UV_HANDLE_TTY_SAVED_POSITION 0x04000000 +#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x08000000 /* Only used by uv_poll_t handles. */ #define UV_HANDLE_POLL_SLOW 0x02000000 diff --git a/src/win/tty.c b/src/win/tty.c index e67a6be2..8575f4f7 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -89,52 +89,70 @@ void uv_console_init() { int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { - HANDLE win_handle; - CONSOLE_SCREEN_BUFFER_INFO info; + HANDLE handle = INVALID_HANDLE_VALUE; + DWORD original_console_mode = 0; + CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; - win_handle = (HANDLE) _get_osfhandle(fd); - if (win_handle == INVALID_HANDLE_VALUE) { - uv__set_sys_error(loop, ERROR_INVALID_HANDLE); + handle = (HANDLE) _get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) { + uv__set_artificial_error(loop, UV_EBADF); return -1; } - if (!GetConsoleMode(win_handle, &tty->original_console_mode)) { - uv__set_sys_error(loop, GetLastError()); - return -1; - } + if (readable) { + /* Try to obtain the original console mode fromt he input handle. */ + if (!GetConsoleMode(handle, &original_console_mode)) { + uv__set_sys_error(loop, GetLastError()); + return -1; + } - /* Initialize virtual window size; if it fails, assume that this is stdin. */ - if (GetConsoleScreenBufferInfo(win_handle, &info)) { + } else { + /* Obtain the screen buffer info with the output handle. */ + if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) { + uv__set_sys_error(loop, GetLastError()); + return -1; + } + + /* Update the virtual window. We must hold the tty_output_lock because the */ + /* virtual window state is shared between all uv_tty handles. */ EnterCriticalSection(&uv_tty_output_lock); - uv_tty_update_virtual_window(&info); + uv_tty_update_virtual_window(&screen_buffer_info); LeaveCriticalSection(&uv_tty_output_lock); } + uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY); uv_connection_init((uv_stream_t*) tty); - tty->handle = win_handle; - tty->read_line_handle = NULL; - tty->read_line_buffer = uv_null_buf_; - tty->read_raw_wait = NULL; + tty->handle = handle; tty->reqs_pending = 0; tty->flags |= UV_HANDLE_BOUND; - /* Init keycode-to-vt100 mapper state. */ - tty->last_key_len = 0; - tty->last_key_offset = 0; - tty->last_utf16_high_surrogate = 0; - memset(&tty->last_input_record, 0, sizeof tty->last_input_record); + if (readable) { + /* Initialize TTY input specific fields. */ + tty->original_console_mode = original_console_mode; + tty->flags |= UV_HANDLE_TTY_READABLE; + tty->read_line_handle = NULL; + tty->read_line_buffer = uv_null_buf_; + tty->read_raw_wait = NULL; - /* Init utf8-to-utf16 conversion state. */ - tty->utf8_bytes_left = 0; - tty->utf8_codepoint = 0; + /* Init keycode-to-vt100 mapper state. */ + tty->last_key_len = 0; + tty->last_key_offset = 0; + tty->last_utf16_high_surrogate = 0; + memset(&tty->last_input_record, 0, sizeof tty->last_input_record); + } else { + /* TTY output specific fields. */ + /* Init utf8-to-utf16 conversion state. */ + tty->utf8_bytes_left = 0; + tty->utf8_codepoint = 0; - /* Initialize eol conversion state */ - tty->previous_eol = 0; + /* Initialize eol conversion state */ + tty->previous_eol = 0; - /* Init ANSI parser state. */ - tty->ansi_parser_state = ANSI_NORMAL; + /* Init ANSI parser state. */ + tty->ansi_parser_state = ANSI_NORMAL; + } return 0; } @@ -146,6 +164,11 @@ int uv_tty_set_mode(uv_tty_t* tty, int mode) { uv_alloc_cb alloc_cb; uv_read_cb read_cb; + if (!(tty->flags & UV_HANDLE_TTY_READABLE)) { + uv__set_artificial_error(tty->loop, UV_EINVAL); + return -1; + } + if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) { return 0; } @@ -442,6 +465,7 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, off_t buf_used; assert(handle->type == UV_TTY); + assert(handle->flags & UV_HANDLE_TTY_READABLE); handle->flags &= ~UV_HANDLE_READ_PENDING; if (!(handle->flags & UV_HANDLE_READING) || @@ -681,6 +705,7 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle, uv_buf_t buf; assert(handle->type == UV_TTY); + assert(handle->flags & UV_HANDLE_TTY_READABLE); buf = handle->read_line_buffer; @@ -724,6 +749,8 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle, void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req) { + assert(handle->type == UV_TTY); + assert(handle->flags & UV_HANDLE_TTY_READABLE); /* If the read_line_buffer member is zero, it must have been an raw read. */ /* Otherwise it was a line-buffered read. */ @@ -740,6 +767,11 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; + if (!(handle->flags & UV_HANDLE_TTY_READABLE)) { + uv__set_artificial_error(handle->loop, UV_EINVAL); + return -1; + } + handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); handle->read_cb = read_cb; @@ -767,6 +799,10 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, int uv_tty_read_stop(uv_tty_t* handle) { uv_loop_t* loop = handle->loop; + if (!(handle->flags & UV_HANDLE_TTY_READABLE)) { + uv__set_artificial_error(handle->loop, UV_EINVAL); + return -1; + } if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; @@ -1676,6 +1712,11 @@ int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { DWORD error; + if (handle->flags & UV_HANDLE_TTY_READABLE) { + uv__set_artificial_error(handle->loop, UV_EINVAL); + return -1; + } + if ((handle->flags & UV_HANDLE_SHUTTING) || (handle->flags & UV_HANDLE_CLOSING)) { uv__set_sys_error(loop, WSAESHUTDOWN); @@ -1727,11 +1768,16 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, void uv_tty_close(uv_tty_t* handle) { - handle->flags |= UV_HANDLE_SHUTTING; - - uv_tty_read_stop(handle); CloseHandle(handle->handle); + if (handle->flags & UV_HANDLE_TTY_READABLE) { + /* Readable TTY handle */ + uv_tty_read_stop(handle); + } else { + /* Writable TTY handle */ + handle->flags |= UV_HANDLE_SHUTTING; + } + uv__handle_start(handle); if (handle->reqs_pending == 0) { @@ -1741,7 +1787,7 @@ void uv_tty_close(uv_tty_t* handle) { void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { - if ((handle->flags && UV_HANDLE_CONNECTION) && + if (!(handle->flags && UV_HANDLE_TTY_READABLE) && handle->shutdown_req != NULL && handle->write_reqs_pending == 0) { UNREGISTER_HANDLE_REQ(loop, handle, handle->shutdown_req); @@ -1766,11 +1812,13 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { handle->reqs_pending == 0) { /* The console handle duplicate used for line reading should be destroyed */ /* by uv_tty_read_stop. */ - assert(handle->read_line_handle == NULL); + assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || + handle->read_line_handle == NULL); /* The wait handle used for raw reading should be unregistered when the */ /* wait callback runs. */ - assert(handle->read_raw_wait == NULL); + assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || + handle->read_raw_wait == NULL); assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_stop(handle);