blob: 4ccf0273403a112c178fd8f449e2bb4c51f11c35 [file] [log] [blame]
Steve Dower39294992016-08-30 21:22:36 -07001/*
2 An implementation of Windows console I/O
3
4 Classes defined here: _WindowsConsoleIO
5
6 Written by Steve Dower
7*/
8
9#define PY_SSIZE_T_CLEAN
10#include "Python.h"
Victor Stinnerbcda8f12018-11-21 22:27:47 +010011#include "pycore_object.h"
Steve Dower39294992016-08-30 21:22:36 -070012
13#ifdef MS_WINDOWS
14
Victor Stinner4a21e572020-04-15 02:35:41 +020015#include "structmember.h" // PyMemberDef
Steve Dower39294992016-08-30 21:22:36 -070016#ifdef HAVE_SYS_TYPES_H
17#include <sys/types.h>
18#endif
19#ifdef HAVE_SYS_STAT_H
20#include <sys/stat.h>
21#endif
22#include <stddef.h> /* For offsetof */
23
24#define WIN32_LEAN_AND_MEAN
25#include <windows.h>
Steve Dower27f26ad2016-09-17 13:51:23 -070026#include <fcntl.h>
Steve Dower39294992016-08-30 21:22:36 -070027
28#include "_iomodule.h"
29
30/* BUFSIZ determines how many characters can be typed at the console
31 before it starts blocking. */
32#if BUFSIZ < (16*1024)
33#define SMALLCHUNK (2*1024)
34#elif (BUFSIZ >= (2 << 25))
Victor Stinner8c663fd2017-11-08 14:44:44 -080035#error "unreasonable BUFSIZ > 64 MiB defined"
Steve Dower39294992016-08-30 21:22:36 -070036#else
37#define SMALLCHUNK BUFSIZ
38#endif
39
40/* BUFMAX determines how many bytes can be read in one go. */
41#define BUFMAX (32*1024*1024)
42
Steve Dower312cef72016-10-03 09:04:58 -070043/* SMALLBUF determines how many utf-8 characters will be
44 buffered within the stream, in order to support reads
45 of less than one character */
46#define SMALLBUF 4
47
Steve Dower39294992016-08-30 21:22:36 -070048char _get_console_type(HANDLE handle) {
49 DWORD mode, peek_count;
50
51 if (handle == INVALID_HANDLE_VALUE)
52 return '\0';
Benjamin Petersone2e792d2016-09-19 22:17:16 -070053
Steve Dower39294992016-08-30 21:22:36 -070054 if (!GetConsoleMode(handle, &mode))
55 return '\0';
56
57 /* Peek at the handle to see whether it is an input or output handle */
58 if (GetNumberOfConsoleInputEvents(handle, &peek_count))
59 return 'r';
60 return 'w';
61}
62
63char _PyIO_get_console_type(PyObject *path_or_fd) {
Steve Dower722e3e22017-02-04 15:07:46 -080064 int fd = PyLong_AsLong(path_or_fd);
Steve Dower39294992016-08-30 21:22:36 -070065 PyErr_Clear();
66 if (fd >= 0) {
67 HANDLE handle;
68 _Py_BEGIN_SUPPRESS_IPH
69 handle = (HANDLE)_get_osfhandle(fd);
70 _Py_END_SUPPRESS_IPH
Steve Dower722e3e22017-02-04 15:07:46 -080071 if (handle == INVALID_HANDLE_VALUE)
Steve Dower39294992016-08-30 21:22:36 -070072 return '\0';
73 return _get_console_type(handle);
74 }
75
Steve Dower722e3e22017-02-04 15:07:46 -080076 PyObject *decoded;
77 wchar_t *decoded_wstr;
Steve Dower39294992016-08-30 21:22:36 -070078
Steve Dower722e3e22017-02-04 15:07:46 -080079 if (!PyUnicode_FSDecoder(path_or_fd, &decoded)) {
Steve Dower39294992016-08-30 21:22:36 -070080 PyErr_Clear();
Steve Dower27f26ad2016-09-17 13:51:23 -070081 return '\0';
82 }
Steve Dower722e3e22017-02-04 15:07:46 -080083 decoded_wstr = PyUnicode_AsWideCharString(decoded, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -070084 Py_CLEAR(decoded);
Steve Dower722e3e22017-02-04 15:07:46 -080085 if (!decoded_wstr) {
Steve Dower27f26ad2016-09-17 13:51:23 -070086 PyErr_Clear();
87 return '\0';
88 }
Steve Dower39294992016-08-30 21:22:36 -070089
Steve Dowera7e16482017-02-04 17:36:47 -080090 char m = '\0';
91 if (!_wcsicmp(decoded_wstr, L"CONIN$")) {
92 m = 'r';
93 } else if (!_wcsicmp(decoded_wstr, L"CONOUT$")) {
94 m = 'w';
95 } else if (!_wcsicmp(decoded_wstr, L"CON")) {
96 m = 'x';
97 }
98 if (m) {
99 PyMem_Free(decoded_wstr);
100 return m;
101 }
102
Steve Dower722e3e22017-02-04 15:07:46 -0800103 DWORD length;
104 wchar_t name_buf[MAX_PATH], *pname_buf = name_buf;
Victor Stinner29adc132017-06-08 18:19:25 +0200105
Steve Dower722e3e22017-02-04 15:07:46 -0800106 length = GetFullPathNameW(decoded_wstr, MAX_PATH, pname_buf, NULL);
107 if (length > MAX_PATH) {
108 pname_buf = PyMem_New(wchar_t, length);
109 if (pname_buf)
110 length = GetFullPathNameW(decoded_wstr, length, pname_buf, NULL);
111 else
112 length = 0;
113 }
114 PyMem_Free(decoded_wstr);
115
Steve Dower722e3e22017-02-04 15:07:46 -0800116 if (length) {
117 wchar_t *name = pname_buf;
118 if (length >= 4 && name[3] == L'\\' &&
119 (name[2] == L'.' || name[2] == L'?') &&
120 name[1] == L'\\' && name[0] == L'\\') {
121 name += 4;
122 }
123 if (!_wcsicmp(name, L"CONIN$")) {
124 m = 'r';
125 } else if (!_wcsicmp(name, L"CONOUT$")) {
126 m = 'w';
127 } else if (!_wcsicmp(name, L"CON")) {
128 m = 'x';
129 }
Steve Dower39294992016-08-30 21:22:36 -0700130 }
131
Steve Dower722e3e22017-02-04 15:07:46 -0800132 if (pname_buf != name_buf)
133 PyMem_Free(pname_buf);
Steve Dower39294992016-08-30 21:22:36 -0700134 return m;
135}
136
Steve Dower722e3e22017-02-04 15:07:46 -0800137
Steve Dower39294992016-08-30 21:22:36 -0700138/*[clinic input]
139module _io
140class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
141[clinic start generated code]*/
142/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e897fdc1fba4e131]*/
143
Steve Dower39294992016-08-30 21:22:36 -0700144typedef struct {
145 PyObject_HEAD
146 HANDLE handle;
147 int fd;
148 unsigned int created : 1;
149 unsigned int readable : 1;
150 unsigned int writable : 1;
151 unsigned int closehandle : 1;
152 char finalizing;
153 unsigned int blksize;
154 PyObject *weakreflist;
155 PyObject *dict;
Steve Dower312cef72016-10-03 09:04:58 -0700156 char buf[SMALLBUF];
157 wchar_t wbuf;
Steve Dower39294992016-08-30 21:22:36 -0700158} winconsoleio;
159
160PyTypeObject PyWindowsConsoleIO_Type;
161
162_Py_IDENTIFIER(name);
163
164int
165_PyWindowsConsoleIO_closed(PyObject *self)
166{
167 return ((winconsoleio *)self)->handle == INVALID_HANDLE_VALUE;
168}
169
170
171/* Returns 0 on success, -1 with exception set on failure. */
172static int
173internal_close(winconsoleio *self)
174{
175 if (self->handle != INVALID_HANDLE_VALUE) {
176 if (self->closehandle) {
177 if (self->fd >= 0) {
178 _Py_BEGIN_SUPPRESS_IPH
179 close(self->fd);
180 _Py_END_SUPPRESS_IPH
181 }
182 CloseHandle(self->handle);
183 }
184 self->handle = INVALID_HANDLE_VALUE;
185 self->fd = -1;
186 }
187 return 0;
188}
189
190/*[clinic input]
191_io._WindowsConsoleIO.close
192
193Close the handle.
194
195A closed handle cannot be used for further I/O operations. close() may be
196called more than once without error.
197[clinic start generated code]*/
198
199static PyObject *
200_io__WindowsConsoleIO_close_impl(winconsoleio *self)
201/*[clinic end generated code: output=27ef95b66c29057b input=185617e349ae4c7b]*/
202{
203 PyObject *res;
204 PyObject *exc, *val, *tb;
205 int rc;
206 _Py_IDENTIFIER(close);
Jeroen Demeyer59ad1102019-07-11 10:59:05 +0200207 res = _PyObject_CallMethodIdOneArg((PyObject*)&PyRawIOBase_Type,
Zackery Spytzd3952092019-07-19 00:07:06 -0600208 &PyId_close, (PyObject*)self);
Steve Dower39294992016-08-30 21:22:36 -0700209 if (!self->closehandle) {
210 self->handle = INVALID_HANDLE_VALUE;
211 return res;
212 }
213 if (res == NULL)
214 PyErr_Fetch(&exc, &val, &tb);
215 rc = internal_close(self);
216 if (res == NULL)
217 _PyErr_ChainExceptions(exc, val, tb);
218 if (rc < 0)
219 Py_CLEAR(res);
220 return res;
221}
222
223static PyObject *
224winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
225{
226 winconsoleio *self;
227
228 assert(type != NULL && type->tp_alloc != NULL);
229
230 self = (winconsoleio *) type->tp_alloc(type, 0);
231 if (self != NULL) {
232 self->handle = INVALID_HANDLE_VALUE;
233 self->fd = -1;
234 self->created = 0;
235 self->readable = 0;
236 self->writable = 0;
237 self->closehandle = 0;
238 self->blksize = 0;
239 self->weakreflist = NULL;
240 }
241
242 return (PyObject *) self;
243}
244
245/*[clinic input]
246_io._WindowsConsoleIO.__init__
247 file as nameobj: object
248 mode: str = "r"
Serhiy Storchaka202fda52017-03-12 10:10:47 +0200249 closefd: bool(accept={int}) = True
Steve Dower39294992016-08-30 21:22:36 -0700250 opener: object = None
251
252Open a console buffer by file descriptor.
253
254The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All
255other mode characters will be ignored. Mode 'b' will be assumed if it is
256omitted. The *opener* parameter is always ignored.
257[clinic start generated code]*/
258
259static int
260_io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
261 const char *mode, int closefd,
262 PyObject *opener)
Serhiy Storchaka202fda52017-03-12 10:10:47 +0200263/*[clinic end generated code: output=3fd9cbcdd8d95429 input=06ae4b863c63244b]*/
Steve Dower39294992016-08-30 21:22:36 -0700264{
265 const char *s;
266 wchar_t *name = NULL;
Steve Dower27f26ad2016-09-17 13:51:23 -0700267 char console_type = '\0';
Steve Dower39294992016-08-30 21:22:36 -0700268 int ret = 0;
269 int rwa = 0;
270 int fd = -1;
271 int fd_is_own = 0;
272
273 assert(PyWindowsConsoleIO_Check(self));
274 if (self->handle >= 0) {
275 if (self->closehandle) {
276 /* Have to close the existing file first. */
277 if (internal_close(self) < 0)
278 return -1;
279 }
280 else
281 self->handle = INVALID_HANDLE_VALUE;
282 }
283
Steve Dower39294992016-08-30 21:22:36 -0700284 fd = _PyLong_AsInt(nameobj);
285 if (fd < 0) {
286 if (!PyErr_Occurred()) {
287 PyErr_SetString(PyExc_ValueError,
288 "negative file descriptor");
289 return -1;
290 }
291 PyErr_Clear();
292 }
293 self->fd = fd;
294
295 if (fd < 0) {
Victor Stinner29adc132017-06-08 18:19:25 +0200296 PyObject *decodedname;
Steve Dower39294992016-08-30 21:22:36 -0700297
298 int d = PyUnicode_FSDecoder(nameobj, (void*)&decodedname);
299 if (!d)
300 return -1;
301
Serhiy Storchakae613e6a2017-06-27 16:03:14 +0300302 name = PyUnicode_AsWideCharString(decodedname, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -0700303 console_type = _PyIO_get_console_type(decodedname);
Steve Dower39294992016-08-30 21:22:36 -0700304 Py_CLEAR(decodedname);
305 if (name == NULL)
306 return -1;
Steve Dower39294992016-08-30 21:22:36 -0700307 }
308
309 s = mode;
310 while (*s) {
311 switch (*s++) {
312 case '+':
313 case 'a':
314 case 'b':
315 case 'x':
316 break;
317 case 'r':
318 if (rwa)
319 goto bad_mode;
320 rwa = 1;
321 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700322 if (console_type == 'x')
323 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700324 break;
325 case 'w':
326 if (rwa)
327 goto bad_mode;
328 rwa = 1;
329 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700330 if (console_type == 'x')
331 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700332 break;
333 default:
334 PyErr_Format(PyExc_ValueError,
335 "invalid mode: %.200s", mode);
336 goto error;
337 }
338 }
339
340 if (!rwa)
341 goto bad_mode;
342
343 if (fd >= 0) {
344 _Py_BEGIN_SUPPRESS_IPH
345 self->handle = (HANDLE)_get_osfhandle(fd);
346 _Py_END_SUPPRESS_IPH
347 self->closehandle = 0;
348 } else {
349 DWORD access = GENERIC_READ;
350
351 self->closehandle = 1;
352 if (!closefd) {
353 PyErr_SetString(PyExc_ValueError,
354 "Cannot use closefd=False with file name");
355 goto error;
356 }
357
358 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700359 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700360
361 Py_BEGIN_ALLOW_THREADS
362 /* Attempt to open for read/write initially, then fall back
363 on the specific access. This is required for modern names
364 CONIN$ and CONOUT$, which allow reading/writing state as
365 well as reading/writing content. */
366 self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
367 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
368 if (self->handle == INVALID_HANDLE_VALUE)
369 self->handle = CreateFileW(name, access,
370 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
371 Py_END_ALLOW_THREADS
372
373 if (self->handle == INVALID_HANDLE_VALUE) {
374 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
375 goto error;
376 }
377 }
378
Steve Dower27f26ad2016-09-17 13:51:23 -0700379 if (console_type == '\0')
380 console_type = _get_console_type(self->handle);
381
382 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700383 PyErr_SetString(PyExc_ValueError,
384 "Cannot open console input buffer for writing");
385 goto error;
386 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700387 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700388 PyErr_SetString(PyExc_ValueError,
389 "Cannot open console output buffer for reading");
390 goto error;
391 }
392
393 self->blksize = DEFAULT_BUFFER_SIZE;
394 memset(self->buf, 0, 4);
395
396 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
397 goto error;
398
399 goto done;
400
401bad_mode:
402 PyErr_SetString(PyExc_ValueError,
403 "Must have exactly one of read or write mode");
404error:
405 ret = -1;
406 internal_close(self);
407
408done:
409 if (name)
410 PyMem_Free(name);
411 return ret;
412}
413
414static int
415winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
416{
417 Py_VISIT(self->dict);
418 return 0;
419}
420
421static int
422winconsoleio_clear(winconsoleio *self)
423{
424 Py_CLEAR(self->dict);
425 return 0;
426}
427
428static void
429winconsoleio_dealloc(winconsoleio *self)
430{
431 self->finalizing = 1;
432 if (_PyIOBase_finalize((PyObject *) self) < 0)
433 return;
434 _PyObject_GC_UNTRACK(self);
435 if (self->weakreflist != NULL)
436 PyObject_ClearWeakRefs((PyObject *) self);
437 Py_CLEAR(self->dict);
438 Py_TYPE(self)->tp_free((PyObject *)self);
439}
440
441static PyObject *
442err_closed(void)
443{
444 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
445 return NULL;
446}
447
448static PyObject *
449err_mode(const char *action)
450{
451 _PyIO_State *state = IO_STATE();
452 if (state != NULL)
453 PyErr_Format(state->unsupported_operation,
454 "Console buffer does not support %s", action);
455 return NULL;
456}
457
458/*[clinic input]
459_io._WindowsConsoleIO.fileno
460
461Return the underlying file descriptor (an integer).
462
463fileno is only set when a file descriptor is used to open
464one of the standard streams.
465
466[clinic start generated code]*/
467
468static PyObject *
469_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
470/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/
471{
472 if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) {
473 _Py_BEGIN_SUPPRESS_IPH
474 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700475 self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700476 else
Steve Dower27f26ad2016-09-17 13:51:23 -0700477 self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700478 _Py_END_SUPPRESS_IPH
479 }
480 if (self->fd < 0)
481 return err_mode("fileno");
482 return PyLong_FromLong(self->fd);
483}
484
485/*[clinic input]
486_io._WindowsConsoleIO.readable
487
488True if console is an input buffer.
489[clinic start generated code]*/
490
491static PyObject *
492_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
493/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
494{
495 if (self->handle == INVALID_HANDLE_VALUE)
496 return err_closed();
497 return PyBool_FromLong((long) self->readable);
498}
499
500/*[clinic input]
501_io._WindowsConsoleIO.writable
502
503True if console is an output buffer.
504[clinic start generated code]*/
505
506static PyObject *
507_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
508/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
509{
510 if (self->handle == INVALID_HANDLE_VALUE)
511 return err_closed();
512 return PyBool_FromLong((long) self->writable);
513}
514
515static DWORD
516_buflen(winconsoleio *self)
517{
Steve Dower312cef72016-10-03 09:04:58 -0700518 for (DWORD i = 0; i < SMALLBUF; ++i) {
Steve Dower39294992016-08-30 21:22:36 -0700519 if (!self->buf[i])
520 return i;
521 }
Steve Dower312cef72016-10-03 09:04:58 -0700522 return SMALLBUF;
Steve Dower39294992016-08-30 21:22:36 -0700523}
524
525static DWORD
526_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
527{
528 DWORD n = 0;
529
530 while (self->buf[0] && len--) {
Steve Dower312cef72016-10-03 09:04:58 -0700531 buf[n++] = self->buf[0];
532 for (int i = 1; i < SMALLBUF; ++i)
533 self->buf[i - 1] = self->buf[i];
534 self->buf[SMALLBUF - 1] = 0;
Steve Dower39294992016-08-30 21:22:36 -0700535 }
536
537 return n;
538}
539
540static wchar_t *
541read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
542 int err = 0, sig = 0;
543
544 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
545 if (!buf)
546 goto error;
Steve Dower312cef72016-10-03 09:04:58 -0700547
Steve Dower39294992016-08-30 21:22:36 -0700548 *readlen = 0;
549
Steve Dower312cef72016-10-03 09:04:58 -0700550 //DebugBreak();
Steve Dower39294992016-08-30 21:22:36 -0700551 Py_BEGIN_ALLOW_THREADS
Steve Dower312cef72016-10-03 09:04:58 -0700552 DWORD off = 0;
553 while (off < maxlen) {
Victor Stinnerbcda8f12018-11-21 22:27:47 +0100554 DWORD n = (DWORD)-1;
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700555 DWORD len = min(maxlen - off, BUFSIZ);
Steve Dower39294992016-08-30 21:22:36 -0700556 SetLastError(0);
557 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
558
559 if (!res) {
560 err = GetLastError();
561 break;
562 }
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700563 if (n == (DWORD)-1 && (err = GetLastError()) == ERROR_OPERATION_ABORTED) {
564 break;
565 }
Steve Dower39294992016-08-30 21:22:36 -0700566 if (n == 0) {
567 err = GetLastError();
568 if (err != ERROR_OPERATION_ABORTED)
569 break;
570 err = 0;
571 HANDLE hInterruptEvent = _PyOS_SigintEvent();
572 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
Steve Dower312cef72016-10-03 09:04:58 -0700573 == WAIT_OBJECT_0) {
Steve Dower39294992016-08-30 21:22:36 -0700574 ResetEvent(hInterruptEvent);
575 Py_BLOCK_THREADS
576 sig = PyErr_CheckSignals();
577 Py_UNBLOCK_THREADS
578 if (sig < 0)
579 break;
580 }
581 }
582 *readlen += n;
583
584 /* If we didn't read a full buffer that time, don't try
585 again or we will block a second time. */
586 if (n < len)
587 break;
588 /* If the buffer ended with a newline, break out */
589 if (buf[*readlen - 1] == '\n')
590 break;
Steve Dower312cef72016-10-03 09:04:58 -0700591 /* If the buffer ends with a high surrogate, expand the
592 buffer and read an extra character. */
593 WORD char_type;
594 if (off + BUFSIZ >= maxlen &&
595 GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) &&
596 char_type == C3_HIGHSURROGATE) {
597 wchar_t *newbuf;
598 maxlen += 1;
599 Py_BLOCK_THREADS
600 newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t));
601 Py_UNBLOCK_THREADS
602 if (!newbuf) {
603 sig = -1;
604 break;
605 }
606 buf = newbuf;
607 /* Only advance by n and not BUFSIZ in this case */
608 off += n;
609 continue;
610 }
611
612 off += BUFSIZ;
Steve Dower39294992016-08-30 21:22:36 -0700613 }
Steve Dower312cef72016-10-03 09:04:58 -0700614
Steve Dower39294992016-08-30 21:22:36 -0700615 Py_END_ALLOW_THREADS
616
617 if (sig)
618 goto error;
619 if (err) {
620 PyErr_SetFromWindowsErr(err);
621 goto error;
622 }
623
624 if (*readlen > 0 && buf[0] == L'\x1a') {
625 PyMem_Free(buf);
626 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
627 if (!buf)
628 goto error;
629 buf[0] = L'\0';
630 *readlen = 0;
631 }
632
633 return buf;
634
635error:
636 if (buf)
637 PyMem_Free(buf);
638 return NULL;
639}
640
641
642static Py_ssize_t
643readinto(winconsoleio *self, char *buf, Py_ssize_t len)
644{
645 if (self->handle == INVALID_HANDLE_VALUE) {
646 err_closed();
647 return -1;
648 }
649 if (!self->readable) {
650 err_mode("reading");
651 return -1;
652 }
653 if (len == 0)
654 return 0;
655 if (len > BUFMAX) {
656 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
657 return -1;
658 }
659
660 /* Each character may take up to 4 bytes in the final buffer.
661 This is highly conservative, but necessary to avoid
662 failure for any given Unicode input (e.g. \U0010ffff).
663 If the caller requests fewer than 4 bytes, we buffer one
664 character.
665 */
666 DWORD wlen = (DWORD)(len / 4);
667 if (wlen == 0) {
668 wlen = 1;
669 }
670
671 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
672 if (read_len) {
673 buf = &buf[read_len];
674 len -= read_len;
675 wlen -= 1;
676 }
677 if (len == read_len || wlen == 0)
678 return read_len;
679
680 DWORD n;
681 wchar_t *wbuf = read_console_w(self->handle, wlen, &n);
682 if (wbuf == NULL)
683 return -1;
684 if (n == 0) {
685 PyMem_Free(wbuf);
686 return read_len;
687 }
688
689 int err = 0;
690 DWORD u8n = 0;
691
692 Py_BEGIN_ALLOW_THREADS
693 if (len < 4) {
694 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
695 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
696 NULL, NULL))
697 u8n = _copyfrombuf(self, buf, (DWORD)len);
698 } else {
699 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
700 buf, (DWORD)len, NULL, NULL);
701 }
702
703 if (u8n) {
704 read_len += u8n;
705 u8n = 0;
706 } else {
707 err = GetLastError();
708 if (err == ERROR_INSUFFICIENT_BUFFER) {
709 /* Calculate the needed buffer for a more useful error, as this
710 means our "/ 4" logic above is insufficient for some input.
711 */
712 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
713 NULL, 0, NULL, NULL);
714 }
715 }
716 Py_END_ALLOW_THREADS
717
718 PyMem_Free(wbuf);
719
720 if (u8n) {
721 PyErr_Format(PyExc_SystemError,
Serhiy Storchakad53fe5f2019-03-13 22:59:55 +0200722 "Buffer had room for %zd bytes but %u bytes required",
Steve Dower39294992016-08-30 21:22:36 -0700723 len, u8n);
724 return -1;
725 }
726 if (err) {
727 PyErr_SetFromWindowsErr(err);
728 return -1;
729 }
730
731 return read_len;
732}
733
734/*[clinic input]
735_io._WindowsConsoleIO.readinto
736 buffer: Py_buffer(accept={rwbuffer})
737 /
738
739Same as RawIOBase.readinto().
740[clinic start generated code]*/
741
742static PyObject *
743_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
744/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
745{
746 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
747 if (len < 0)
748 return NULL;
749
750 return PyLong_FromSsize_t(len);
751}
752
753static DWORD
754new_buffersize(winconsoleio *self, DWORD currentsize)
755{
756 DWORD addend;
757
758 /* Expand the buffer by an amount proportional to the current size,
759 giving us amortized linear-time behavior. For bigger sizes, use a
760 less-than-double growth factor to avoid excessive allocation. */
761 if (currentsize > 65536)
762 addend = currentsize >> 3;
763 else
764 addend = 256 + currentsize;
765 if (addend < SMALLCHUNK)
766 /* Avoid tiny read() calls. */
767 addend = SMALLCHUNK;
768 return addend + currentsize;
769}
770
771/*[clinic input]
772_io._WindowsConsoleIO.readall
773
774Read all data from the console, returned as bytes.
775
776Return an empty bytes object at EOF.
777[clinic start generated code]*/
778
779static PyObject *
780_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
781/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
782{
783 wchar_t *buf;
784 DWORD bufsize, n, len = 0;
785 PyObject *bytes;
786 DWORD bytes_size, rn;
787
788 if (self->handle == INVALID_HANDLE_VALUE)
789 return err_closed();
790
791 bufsize = BUFSIZ;
792
793 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
794 if (buf == NULL)
795 return NULL;
796
797 while (1) {
798 wchar_t *subbuf;
799
800 if (len >= (Py_ssize_t)bufsize) {
801 DWORD newsize = new_buffersize(self, len);
802 if (newsize > BUFMAX)
803 break;
804 if (newsize < bufsize) {
805 PyErr_SetString(PyExc_OverflowError,
806 "unbounded read returned more bytes "
807 "than a Python bytes object can hold");
808 PyMem_Free(buf);
809 return NULL;
810 }
811 bufsize = newsize;
812
Zackery Spytz4c49da02018-12-07 03:11:30 -0700813 wchar_t *tmp = PyMem_Realloc(buf,
814 (bufsize + 1) * sizeof(wchar_t));
815 if (tmp == NULL) {
Steve Dower39294992016-08-30 21:22:36 -0700816 PyMem_Free(buf);
817 return NULL;
818 }
Zackery Spytz4c49da02018-12-07 03:11:30 -0700819 buf = tmp;
Steve Dower39294992016-08-30 21:22:36 -0700820 }
821
822 subbuf = read_console_w(self->handle, bufsize - len, &n);
823
824 if (subbuf == NULL) {
825 PyMem_Free(buf);
826 return NULL;
827 }
828
829 if (n > 0)
830 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
831
832 PyMem_Free(subbuf);
833
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700834 /* when the read is empty we break */
835 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700836 break;
837
838 len += n;
839 }
840
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700841 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700842 /* when the result starts with ^Z we return an empty buffer */
843 PyMem_Free(buf);
844 return PyBytes_FromStringAndSize(NULL, 0);
845 }
846
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700847 if (len) {
848 Py_BEGIN_ALLOW_THREADS
849 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
850 NULL, 0, NULL, NULL);
851 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700852
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700853 if (!bytes_size) {
854 DWORD err = GetLastError();
855 PyMem_Free(buf);
856 return PyErr_SetFromWindowsErr(err);
857 }
858 } else {
859 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700860 }
861
862 bytes_size += _buflen(self);
863 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
864 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
865
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700866 if (len) {
867 Py_BEGIN_ALLOW_THREADS
868 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
869 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
870 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700871
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700872 if (!bytes_size) {
873 DWORD err = GetLastError();
874 PyMem_Free(buf);
875 Py_CLEAR(bytes);
876 return PyErr_SetFromWindowsErr(err);
877 }
878
879 /* add back the number of preserved bytes */
880 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700881 }
882
883 PyMem_Free(buf);
884 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
885 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
886 Py_CLEAR(bytes);
887 return NULL;
888 }
889 }
890 return bytes;
891}
892
893/*[clinic input]
894_io._WindowsConsoleIO.read
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300895 size: Py_ssize_t(accept={int, NoneType}) = -1
Steve Dower39294992016-08-30 21:22:36 -0700896 /
897
898Read at most size bytes, returned as bytes.
899
900Only makes one system call when size is a positive integer,
901so less data may be returned than requested.
902Return an empty bytes object at EOF.
903[clinic start generated code]*/
904
905static PyObject *
906_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300907/*[clinic end generated code: output=57df68af9f4b22d0 input=8bc73bc15d0fa072]*/
Steve Dower39294992016-08-30 21:22:36 -0700908{
909 PyObject *bytes;
910 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700911
Steve Dower39294992016-08-30 21:22:36 -0700912 if (self->handle == INVALID_HANDLE_VALUE)
913 return err_closed();
914 if (!self->readable)
915 return err_mode("reading");
916
917 if (size < 0)
918 return _io__WindowsConsoleIO_readall_impl(self);
919 if (size > BUFMAX) {
920 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
921 return NULL;
922 }
923
924 bytes = PyBytes_FromStringAndSize(NULL, size);
925 if (bytes == NULL)
926 return NULL;
927
928 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
929 if (bytes_size < 0) {
930 Py_CLEAR(bytes);
931 return NULL;
932 }
933
934 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
935 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
936 Py_CLEAR(bytes);
937 return NULL;
938 }
939 }
940
941 return bytes;
942}
943
944/*[clinic input]
945_io._WindowsConsoleIO.write
946 b: Py_buffer
947 /
948
949Write buffer b to file, return number of bytes written.
950
951Only makes one system call, so not all of the data may be written.
952The number of bytes actually written is returned.
953[clinic start generated code]*/
954
955static PyObject *
956_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
957/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
958{
959 BOOL res = TRUE;
960 wchar_t *wbuf;
961 DWORD len, wlen, n = 0;
962
963 if (self->handle == INVALID_HANDLE_VALUE)
964 return err_closed();
965 if (!self->writable)
966 return err_mode("writing");
967
Serhiy Storchaka42c35d92018-02-24 18:55:51 +0200968 if (!b->len) {
969 return PyLong_FromLong(0);
970 }
Steve Dower39294992016-08-30 21:22:36 -0700971 if (b->len > BUFMAX)
972 len = BUFMAX;
973 else
974 len = (DWORD)b->len;
975
976 Py_BEGIN_ALLOW_THREADS
977 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
978
979 /* issue11395 there is an unspecified upper bound on how many bytes
980 can be written at once. We cap at 32k - the caller will have to
981 handle partial writes.
982 Since we don't know how many input bytes are being ignored, we
983 have to reduce and recalculate. */
984 while (wlen > 32766 / sizeof(wchar_t)) {
985 len /= 2;
986 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
987 }
988 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700989
Steve Dower39294992016-08-30 21:22:36 -0700990 if (!wlen)
991 return PyErr_SetFromWindowsErr(0);
992
993 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
994
995 Py_BEGIN_ALLOW_THREADS
996 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
997 if (wlen) {
998 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
Segev Finer523776c2017-06-02 19:26:01 +0300999 if (res && n < wlen) {
Steve Dower39294992016-08-30 21:22:36 -07001000 /* Wrote fewer characters than expected, which means our
1001 * len value may be wrong. So recalculate it from the
1002 * characters that were written. As this could potentially
1003 * result in a different value, we also validate that value.
1004 */
1005 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
1006 NULL, 0, NULL, NULL);
1007 if (len) {
1008 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
1009 NULL, 0);
1010 assert(wlen == len);
1011 }
1012 }
1013 } else
1014 res = 0;
1015 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001016
Steve Dower39294992016-08-30 21:22:36 -07001017 if (!res) {
1018 DWORD err = GetLastError();
1019 PyMem_Free(wbuf);
1020 return PyErr_SetFromWindowsErr(err);
1021 }
1022
1023 PyMem_Free(wbuf);
1024 return PyLong_FromSsize_t(len);
1025}
1026
1027static PyObject *
1028winconsoleio_repr(winconsoleio *self)
1029{
1030 if (self->handle == INVALID_HANDLE_VALUE)
1031 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1032
1033 if (self->readable)
1034 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1035 self->closehandle ? "True" : "False");
1036 if (self->writable)
1037 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1038 self->closehandle ? "True" : "False");
1039
1040 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1041 return NULL;
1042}
1043
1044/*[clinic input]
1045_io._WindowsConsoleIO.isatty
1046
1047Always True.
1048[clinic start generated code]*/
1049
1050static PyObject *
1051_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1052/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1053{
1054 if (self->handle == INVALID_HANDLE_VALUE)
1055 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001056
Steve Dower39294992016-08-30 21:22:36 -07001057 Py_RETURN_TRUE;
1058}
1059
Steve Dower39294992016-08-30 21:22:36 -07001060#include "clinic/winconsoleio.c.h"
1061
1062static PyMethodDef winconsoleio_methods[] = {
1063 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1064 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1065 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1066 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1067 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1068 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1069 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1070 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1071 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
Steve Dower39294992016-08-30 21:22:36 -07001072 {NULL, NULL} /* sentinel */
1073};
1074
1075/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1076
1077static PyObject *
1078get_closed(winconsoleio *self, void *closure)
1079{
1080 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1081}
1082
1083static PyObject *
1084get_closefd(winconsoleio *self, void *closure)
1085{
1086 return PyBool_FromLong((long)(self->closehandle));
1087}
1088
1089static PyObject *
1090get_mode(winconsoleio *self, void *closure)
1091{
1092 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1093}
1094
1095static PyGetSetDef winconsoleio_getsetlist[] = {
1096 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1097 {"closefd", (getter)get_closefd, NULL,
1098 "True if the file descriptor will be closed by close()."},
1099 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1100 {NULL},
1101};
1102
1103static PyMemberDef winconsoleio_members[] = {
1104 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1105 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1106 {NULL}
1107};
1108
1109PyTypeObject PyWindowsConsoleIO_Type = {
1110 PyVarObject_HEAD_INIT(NULL, 0)
1111 "_io._WindowsConsoleIO",
1112 sizeof(winconsoleio),
1113 0,
1114 (destructor)winconsoleio_dealloc, /* tp_dealloc */
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001115 0, /* tp_vectorcall_offset */
Steve Dower39294992016-08-30 21:22:36 -07001116 0, /* tp_getattr */
1117 0, /* tp_setattr */
Jeroen Demeyer530f5062019-05-31 04:13:39 +02001118 0, /* tp_as_async */
Steve Dower39294992016-08-30 21:22:36 -07001119 (reprfunc)winconsoleio_repr, /* tp_repr */
1120 0, /* tp_as_number */
1121 0, /* tp_as_sequence */
1122 0, /* tp_as_mapping */
1123 0, /* tp_hash */
1124 0, /* tp_call */
1125 0, /* tp_str */
1126 PyObject_GenericGetAttr, /* tp_getattro */
1127 0, /* tp_setattro */
1128 0, /* tp_as_buffer */
1129 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
Antoine Pitrouada319b2019-05-29 22:12:38 +02001130 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
Steve Dower39294992016-08-30 21:22:36 -07001131 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1132 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1133 (inquiry)winconsoleio_clear, /* tp_clear */
1134 0, /* tp_richcompare */
1135 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1136 0, /* tp_iter */
1137 0, /* tp_iternext */
1138 winconsoleio_methods, /* tp_methods */
1139 winconsoleio_members, /* tp_members */
1140 winconsoleio_getsetlist, /* tp_getset */
1141 0, /* tp_base */
1142 0, /* tp_dict */
1143 0, /* tp_descr_get */
1144 0, /* tp_descr_set */
1145 offsetof(winconsoleio, dict), /* tp_dictoffset */
1146 _io__WindowsConsoleIO___init__, /* tp_init */
1147 PyType_GenericAlloc, /* tp_alloc */
1148 winconsoleio_new, /* tp_new */
1149 PyObject_GC_Del, /* tp_free */
1150 0, /* tp_is_gc */
1151 0, /* tp_bases */
1152 0, /* tp_mro */
1153 0, /* tp_cache */
1154 0, /* tp_subclasses */
1155 0, /* tp_weaklist */
1156 0, /* tp_del */
1157 0, /* tp_version_tag */
1158 0, /* tp_finalize */
1159};
1160
Benjamin Petersone5024512018-09-12 12:06:42 -07001161PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
Steve Dower312cef72016-10-03 09:04:58 -07001162
Steve Dower39294992016-08-30 21:22:36 -07001163#endif /* MS_WINDOWS */