--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2020 Dmitry Kozlyuk
+ */
+
+#include <io.h>
+
+#include <rte_os.h>
+
+#include "cmdline_private.h"
+
+/* Missing from some MinGW-w64 distributions. */
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
+#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
+#endif
+
+void
+terminal_adjust(struct cmdline *cl)
+{
+ HANDLE handle;
+ DWORD mode;
+
+ ZeroMemory(&cl->oldterm, sizeof(cl->oldterm));
+
+ /* Detect console input, set it up and make it emulate VT100. */
+ handle = GetStdHandle(STD_INPUT_HANDLE);
+ if (GetConsoleMode(handle, &mode)) {
+ cl->oldterm.is_console_input = 1;
+ cl->oldterm.input_mode = mode;
+
+ mode &= ~(
+ ENABLE_LINE_INPUT | /* no line buffering */
+ ENABLE_ECHO_INPUT | /* no echo */
+ ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
+ ENABLE_MOUSE_INPUT | /* no mouse events */
+ ENABLE_WINDOW_INPUT); /* no window resize events */
+ mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
+ SetConsoleMode(handle, mode);
+ }
+
+ /* Detect console output and make it emulate VT100. */
+ handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (GetConsoleMode(handle, &mode)) {
+ cl->oldterm.is_console_output = 1;
+ cl->oldterm.output_mode = mode;
+
+ mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
+ mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+ SetConsoleMode(handle, mode);
+ }
+}
+
+void
+terminal_restore(const struct cmdline *cl)
+{
+ if (cl->oldterm.is_console_input) {
+ HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
+ SetConsoleMode(handle, cl->oldterm.input_mode);
+ }
+
+ if (cl->oldterm.is_console_output) {
+ HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleMode(handle, cl->oldterm.output_mode);
+ }
+}
+
+static int
+cmdline_is_key_down(const INPUT_RECORD *record)
+{
+ return (record->EventType == KEY_EVENT) &&
+ record->Event.KeyEvent.bKeyDown;
+}
+
+static int
+cmdline_poll_char_console(HANDLE handle)
+{
+ INPUT_RECORD record;
+ DWORD events;
+
+ if (!PeekConsoleInput(handle, &record, 1, &events)) {
+ /* Simulate poll(3) behavior on EOF. */
+ return (GetLastError() == ERROR_HANDLE_EOF) ? 1 : -1;
+ }
+
+ if ((events == 0) || !cmdline_is_key_down(&record))
+ return 0;
+
+ return 1;
+}
+
+static int
+cmdline_poll_char_file(struct cmdline *cl, HANDLE handle)
+{
+ DWORD type = GetFileType(handle);
+
+ /* Since console is handled by cmdline_poll_char_console(),
+ * this is either a serial port or input handle had been replaced.
+ */
+ if (type == FILE_TYPE_CHAR)
+ return cmdline_poll_char_console(handle);
+
+ /* PeekNamedPipe() can handle all pipes and also sockets. */
+ if (type == FILE_TYPE_PIPE) {
+ DWORD bytes_avail;
+ if (!PeekNamedPipe(handle, NULL, 0, NULL, &bytes_avail, NULL))
+ return (GetLastError() == ERROR_BROKEN_PIPE) ? 1 : -1;
+ return bytes_avail ? 1 : 0;
+ }
+
+ /* There is no straightforward way to peek a file in Windows
+ * I/O model. Read the byte, if it is not the end of file,
+ * buffer it for subsequent read. This will not work with
+ * a file being appended and probably some other edge cases.
+ */
+ if (type == FILE_TYPE_DISK) {
+ char c;
+ int ret;
+
+ ret = _read(cl->s_in, &c, sizeof(c));
+ if (ret == 1) {
+ cl->repeat_count = 1;
+ cl->repeated_char = c;
+ }
+ return ret;
+ }
+
+ /* GetFileType() failed or file of unknown type,
+ * which we do not know how to peek anyway.
+ */
+ return -1;
+}
+
+int
+cmdline_poll_char(struct cmdline *cl)
+{
+ HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
+ return cl->oldterm.is_console_input ?
+ cmdline_poll_char_console(handle) :
+ cmdline_poll_char_file(cl, handle);
+}
+
+ssize_t
+cmdline_read_char(struct cmdline *cl, char *c)
+{
+ HANDLE handle;
+ INPUT_RECORD record;
+ KEY_EVENT_RECORD *key;
+ DWORD events;
+
+ if (!cl->oldterm.is_console_input)
+ return _read(cl->s_in, c, 1);
+
+ /* Return repeated strokes from previous event. */
+ if (cl->repeat_count > 0) {
+ *c = cl->repeated_char;
+ cl->repeat_count--;
+ return 1;
+ }
+
+ handle = (HANDLE)_get_osfhandle(cl->s_in);
+ key = &record.Event.KeyEvent;
+ do {
+ if (!ReadConsoleInput(handle, &record, 1, &events)) {
+ if (GetLastError() == ERROR_HANDLE_EOF) {
+ *c = EOF;
+ return 0;
+ }
+ return -1;
+ }
+ } while (!cmdline_is_key_down(&record));
+
+ *c = key->uChar.AsciiChar;
+
+ /* Save repeated strokes from a single event. */
+ if (key->wRepeatCount > 1) {
+ cl->repeated_char = *c;
+ cl->repeat_count = key->wRepeatCount - 1;
+ }
+
+ return 1;
+}
+
+int
+cmdline_vdprintf(int fd, const char *format, va_list op)
+{
+ int copy, ret;
+ FILE *file;
+
+ copy = _dup(fd);
+ if (copy < 0)
+ return -1;
+
+ file = _fdopen(copy, "a");
+ if (file == NULL) {
+ _close(copy);
+ return -1;
+ }
+
+ ret = vfprintf(file, format, op);
+
+ fclose(file); /* also closes copy */
+
+ return ret;
+}