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