e9585c9eea8cee1f002b9ee225138dbaa6ae896e
[dpdk.git] / lib / librte_cmdline / cmdline_os_windows.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2020 Dmitry Kozlyuk
3  */
4
5 #include <io.h>
6
7 #include <rte_os.h>
8
9 #include "cmdline_private.h"
10
11 /* Missing from some MinGW-w64 distributions. */
12 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
13 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
14 #endif
15
16 #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
17 #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
18 #endif
19
20 void
21 terminal_adjust(struct cmdline *cl)
22 {
23         HANDLE handle;
24         DWORD mode;
25
26         ZeroMemory(&cl->oldterm, sizeof(cl->oldterm));
27
28         /* Detect console input, set it up and make it emulate VT100. */
29         handle = GetStdHandle(STD_INPUT_HANDLE);
30         if (GetConsoleMode(handle, &mode)) {
31                 cl->oldterm.is_console_input = 1;
32                 cl->oldterm.input_mode = mode;
33
34                 mode &= ~(
35                         ENABLE_LINE_INPUT |      /* no line buffering */
36                         ENABLE_ECHO_INPUT |      /* no echo */
37                         ENABLE_PROCESSED_INPUT | /* pass Ctrl+C to program */
38                         ENABLE_MOUSE_INPUT |     /* no mouse events */
39                         ENABLE_WINDOW_INPUT);    /* no window resize events */
40                 mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
41                 SetConsoleMode(handle, mode);
42         }
43
44         /* Detect console output and make it emulate VT100. */
45         handle = GetStdHandle(STD_OUTPUT_HANDLE);
46         if (GetConsoleMode(handle, &mode)) {
47                 cl->oldterm.is_console_output = 1;
48                 cl->oldterm.output_mode = mode;
49
50                 mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
51                 mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
52                 SetConsoleMode(handle, mode);
53         }
54 }
55
56 void
57 terminal_restore(const struct cmdline *cl)
58 {
59         if (cl->oldterm.is_console_input) {
60                 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
61                 SetConsoleMode(handle, cl->oldterm.input_mode);
62         }
63
64         if (cl->oldterm.is_console_output) {
65                 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
66                 SetConsoleMode(handle, cl->oldterm.output_mode);
67         }
68 }
69
70 static int
71 cmdline_is_key_down(const INPUT_RECORD *record)
72 {
73         return (record->EventType == KEY_EVENT) &&
74                 record->Event.KeyEvent.bKeyDown;
75 }
76
77 static int
78 cmdline_poll_char_console(HANDLE handle)
79 {
80         INPUT_RECORD record;
81         DWORD events;
82
83         if (!PeekConsoleInput(handle, &record, 1, &events)) {
84                 /* Simulate poll(3) behavior on EOF. */
85                 return (GetLastError() == ERROR_HANDLE_EOF) ? 1 : -1;
86         }
87
88         if ((events == 0) || !cmdline_is_key_down(&record))
89                 return 0;
90
91         return 1;
92 }
93
94 static int
95 cmdline_poll_char_file(struct cmdline *cl, HANDLE handle)
96 {
97         DWORD type = GetFileType(handle);
98
99         /* Since console is handled by cmdline_poll_char_console(),
100          * this is either a serial port or input handle had been replaced.
101          */
102         if (type == FILE_TYPE_CHAR)
103                 return cmdline_poll_char_console(handle);
104
105         /* PeekNamedPipe() can handle all pipes and also sockets. */
106         if (type == FILE_TYPE_PIPE) {
107                 DWORD bytes_avail;
108                 if (!PeekNamedPipe(handle, NULL, 0, NULL, &bytes_avail, NULL))
109                         return (GetLastError() == ERROR_BROKEN_PIPE) ? 1 : -1;
110                 return bytes_avail ? 1 : 0;
111         }
112
113         /* There is no straightforward way to peek a file in Windows
114          * I/O model. Read the byte, if it is not the end of file,
115          * buffer it for subsequent read. This will not work with
116          * a file being appended and probably some other edge cases.
117          */
118         if (type == FILE_TYPE_DISK) {
119                 char c;
120                 int ret;
121
122                 ret = _read(cl->s_in, &c, sizeof(c));
123                 if (ret == 1) {
124                         cl->repeat_count = 1;
125                         cl->repeated_char = c;
126                 }
127                 return ret;
128         }
129
130         /* GetFileType() failed or file of unknown type,
131          * which we do not know how to peek anyway.
132          */
133         return -1;
134 }
135
136 int
137 cmdline_poll_char(struct cmdline *cl)
138 {
139         HANDLE handle = (HANDLE)_get_osfhandle(cl->s_in);
140         return cl->oldterm.is_console_input ?
141                 cmdline_poll_char_console(handle) :
142                 cmdline_poll_char_file(cl, handle);
143 }
144
145 ssize_t
146 cmdline_read_char(struct cmdline *cl, char *c)
147 {
148         HANDLE handle;
149         INPUT_RECORD record;
150         KEY_EVENT_RECORD *key;
151         DWORD events;
152
153         if (!cl->oldterm.is_console_input)
154                 return _read(cl->s_in, c, 1);
155
156         /* Return repeated strokes from previous event. */
157         if (cl->repeat_count > 0) {
158                 *c = cl->repeated_char;
159                 cl->repeat_count--;
160                 return 1;
161         }
162
163         handle = (HANDLE)_get_osfhandle(cl->s_in);
164         key = &record.Event.KeyEvent;
165         do {
166                 if (!ReadConsoleInput(handle, &record, 1, &events)) {
167                         if (GetLastError() == ERROR_HANDLE_EOF) {
168                                 *c = EOF;
169                                 return 0;
170                         }
171                         return -1;
172                 }
173         } while (!cmdline_is_key_down(&record));
174
175         *c = key->uChar.AsciiChar;
176
177         /* Save repeated strokes from a single event. */
178         if (key->wRepeatCount > 1) {
179                 cl->repeated_char = *c;
180                 cl->repeat_count = key->wRepeatCount - 1;
181         }
182
183         return 1;
184 }
185
186 int
187 cmdline_vdprintf(int fd, const char *format, va_list op)
188 {
189         int copy, ret;
190         FILE *file;
191
192         copy = _dup(fd);
193         if (copy < 0)
194                 return -1;
195
196         file = _fdopen(copy, "a");
197         if (file == NULL) {
198                 _close(copy);
199                 return -1;
200         }
201
202         ret = vfprintf(file, format, op);
203
204         fclose(file); /* also closes copy */
205
206         return ret;
207 }