blob: 13342ec239d6d6687f8b490b67b6015887433c9a [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"
11
12#ifdef MS_WINDOWS
13
14#include "structmember.h"
15#ifdef HAVE_SYS_TYPES_H
16#include <sys/types.h>
17#endif
18#ifdef HAVE_SYS_STAT_H
19#include <sys/stat.h>
20#endif
21#include <stddef.h> /* For offsetof */
22
23#define WIN32_LEAN_AND_MEAN
24#include <windows.h>
Steve Dower27f26ad2016-09-17 13:51:23 -070025#include <fcntl.h>
Steve Dower39294992016-08-30 21:22:36 -070026
27#include "_iomodule.h"
28
29/* BUFSIZ determines how many characters can be typed at the console
30 before it starts blocking. */
31#if BUFSIZ < (16*1024)
32#define SMALLCHUNK (2*1024)
33#elif (BUFSIZ >= (2 << 25))
Victor Stinner8c663fd2017-11-08 14:44:44 -080034#error "unreasonable BUFSIZ > 64 MiB defined"
Steve Dower39294992016-08-30 21:22:36 -070035#else
36#define SMALLCHUNK BUFSIZ
37#endif
38
39/* BUFMAX determines how many bytes can be read in one go. */
40#define BUFMAX (32*1024*1024)
41
Steve Dower312cef72016-10-03 09:04:58 -070042/* SMALLBUF determines how many utf-8 characters will be
43 buffered within the stream, in order to support reads
44 of less than one character */
45#define SMALLBUF 4
46
Steve Dower39294992016-08-30 21:22:36 -070047char _get_console_type(HANDLE handle) {
48 DWORD mode, peek_count;
49
50 if (handle == INVALID_HANDLE_VALUE)
51 return '\0';
Benjamin Petersone2e792d2016-09-19 22:17:16 -070052
Steve Dower39294992016-08-30 21:22:36 -070053 if (!GetConsoleMode(handle, &mode))
54 return '\0';
55
56 /* Peek at the handle to see whether it is an input or output handle */
57 if (GetNumberOfConsoleInputEvents(handle, &peek_count))
58 return 'r';
59 return 'w';
60}
61
62char _PyIO_get_console_type(PyObject *path_or_fd) {
Steve Dower722e3e22017-02-04 15:07:46 -080063 int fd = PyLong_AsLong(path_or_fd);
Steve Dower39294992016-08-30 21:22:36 -070064 PyErr_Clear();
65 if (fd >= 0) {
66 HANDLE handle;
67 _Py_BEGIN_SUPPRESS_IPH
68 handle = (HANDLE)_get_osfhandle(fd);
69 _Py_END_SUPPRESS_IPH
Steve Dower722e3e22017-02-04 15:07:46 -080070 if (handle == INVALID_HANDLE_VALUE)
Steve Dower39294992016-08-30 21:22:36 -070071 return '\0';
72 return _get_console_type(handle);
73 }
74
Steve Dower722e3e22017-02-04 15:07:46 -080075 PyObject *decoded;
76 wchar_t *decoded_wstr;
Steve Dower39294992016-08-30 21:22:36 -070077
Steve Dower722e3e22017-02-04 15:07:46 -080078 if (!PyUnicode_FSDecoder(path_or_fd, &decoded)) {
Steve Dower39294992016-08-30 21:22:36 -070079 PyErr_Clear();
Steve Dower27f26ad2016-09-17 13:51:23 -070080 return '\0';
81 }
Steve Dower722e3e22017-02-04 15:07:46 -080082 decoded_wstr = PyUnicode_AsWideCharString(decoded, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -070083 Py_CLEAR(decoded);
Steve Dower722e3e22017-02-04 15:07:46 -080084 if (!decoded_wstr) {
Steve Dower27f26ad2016-09-17 13:51:23 -070085 PyErr_Clear();
86 return '\0';
87 }
Steve Dower39294992016-08-30 21:22:36 -070088
Steve Dowera7e16482017-02-04 17:36:47 -080089 char m = '\0';
90 if (!_wcsicmp(decoded_wstr, L"CONIN$")) {
91 m = 'r';
92 } else if (!_wcsicmp(decoded_wstr, L"CONOUT$")) {
93 m = 'w';
94 } else if (!_wcsicmp(decoded_wstr, L"CON")) {
95 m = 'x';
96 }
97 if (m) {
98 PyMem_Free(decoded_wstr);
99 return m;
100 }
101
Steve Dower722e3e22017-02-04 15:07:46 -0800102 DWORD length;
103 wchar_t name_buf[MAX_PATH], *pname_buf = name_buf;
Victor Stinner29adc132017-06-08 18:19:25 +0200104
Steve Dower722e3e22017-02-04 15:07:46 -0800105 length = GetFullPathNameW(decoded_wstr, MAX_PATH, pname_buf, NULL);
106 if (length > MAX_PATH) {
107 pname_buf = PyMem_New(wchar_t, length);
108 if (pname_buf)
109 length = GetFullPathNameW(decoded_wstr, length, pname_buf, NULL);
110 else
111 length = 0;
112 }
113 PyMem_Free(decoded_wstr);
114
Steve Dower722e3e22017-02-04 15:07:46 -0800115 if (length) {
116 wchar_t *name = pname_buf;
117 if (length >= 4 && name[3] == L'\\' &&
118 (name[2] == L'.' || name[2] == L'?') &&
119 name[1] == L'\\' && name[0] == L'\\') {
120 name += 4;
121 }
122 if (!_wcsicmp(name, L"CONIN$")) {
123 m = 'r';
124 } else if (!_wcsicmp(name, L"CONOUT$")) {
125 m = 'w';
126 } else if (!_wcsicmp(name, L"CON")) {
127 m = 'x';
128 }
Steve Dower39294992016-08-30 21:22:36 -0700129 }
130
Steve Dower722e3e22017-02-04 15:07:46 -0800131 if (pname_buf != name_buf)
132 PyMem_Free(pname_buf);
Steve Dower39294992016-08-30 21:22:36 -0700133 return m;
134}
135
Steve Dower722e3e22017-02-04 15:07:46 -0800136
Steve Dower39294992016-08-30 21:22:36 -0700137/*[clinic input]
138module _io
139class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
140[clinic start generated code]*/
141/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e897fdc1fba4e131]*/
142
Steve Dower39294992016-08-30 21:22:36 -0700143typedef struct {
144 PyObject_HEAD
145 HANDLE handle;
146 int fd;
147 unsigned int created : 1;
148 unsigned int readable : 1;
149 unsigned int writable : 1;
150 unsigned int closehandle : 1;
151 char finalizing;
152 unsigned int blksize;
153 PyObject *weakreflist;
154 PyObject *dict;
Steve Dower312cef72016-10-03 09:04:58 -0700155 char buf[SMALLBUF];
156 wchar_t wbuf;
Steve Dower39294992016-08-30 21:22:36 -0700157} winconsoleio;
158
159PyTypeObject PyWindowsConsoleIO_Type;
160
161_Py_IDENTIFIER(name);
162
163int
164_PyWindowsConsoleIO_closed(PyObject *self)
165{
166 return ((winconsoleio *)self)->handle == INVALID_HANDLE_VALUE;
167}
168
169
170/* Returns 0 on success, -1 with exception set on failure. */
171static int
172internal_close(winconsoleio *self)
173{
174 if (self->handle != INVALID_HANDLE_VALUE) {
175 if (self->closehandle) {
176 if (self->fd >= 0) {
177 _Py_BEGIN_SUPPRESS_IPH
178 close(self->fd);
179 _Py_END_SUPPRESS_IPH
180 }
181 CloseHandle(self->handle);
182 }
183 self->handle = INVALID_HANDLE_VALUE;
184 self->fd = -1;
185 }
186 return 0;
187}
188
189/*[clinic input]
190_io._WindowsConsoleIO.close
191
192Close the handle.
193
194A closed handle cannot be used for further I/O operations. close() may be
195called more than once without error.
196[clinic start generated code]*/
197
198static PyObject *
199_io__WindowsConsoleIO_close_impl(winconsoleio *self)
200/*[clinic end generated code: output=27ef95b66c29057b input=185617e349ae4c7b]*/
201{
202 PyObject *res;
203 PyObject *exc, *val, *tb;
204 int rc;
205 _Py_IDENTIFIER(close);
Victor Stinner61bdb0d2016-12-09 15:39:28 +0100206 res = _PyObject_CallMethodIdObjArgs((PyObject*)&PyRawIOBase_Type,
207 &PyId_close, self, NULL);
Steve Dower39294992016-08-30 21:22:36 -0700208 if (!self->closehandle) {
209 self->handle = INVALID_HANDLE_VALUE;
210 return res;
211 }
212 if (res == NULL)
213 PyErr_Fetch(&exc, &val, &tb);
214 rc = internal_close(self);
215 if (res == NULL)
216 _PyErr_ChainExceptions(exc, val, tb);
217 if (rc < 0)
218 Py_CLEAR(res);
219 return res;
220}
221
222static PyObject *
223winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
224{
225 winconsoleio *self;
226
227 assert(type != NULL && type->tp_alloc != NULL);
228
229 self = (winconsoleio *) type->tp_alloc(type, 0);
230 if (self != NULL) {
231 self->handle = INVALID_HANDLE_VALUE;
232 self->fd = -1;
233 self->created = 0;
234 self->readable = 0;
235 self->writable = 0;
236 self->closehandle = 0;
237 self->blksize = 0;
238 self->weakreflist = NULL;
239 }
240
241 return (PyObject *) self;
242}
243
244/*[clinic input]
245_io._WindowsConsoleIO.__init__
246 file as nameobj: object
247 mode: str = "r"
Serhiy Storchaka202fda52017-03-12 10:10:47 +0200248 closefd: bool(accept={int}) = True
Steve Dower39294992016-08-30 21:22:36 -0700249 opener: object = None
250
251Open a console buffer by file descriptor.
252
253The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All
254other mode characters will be ignored. Mode 'b' will be assumed if it is
255omitted. The *opener* parameter is always ignored.
256[clinic start generated code]*/
257
258static int
259_io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
260 const char *mode, int closefd,
261 PyObject *opener)
Serhiy Storchaka202fda52017-03-12 10:10:47 +0200262/*[clinic end generated code: output=3fd9cbcdd8d95429 input=06ae4b863c63244b]*/
Steve Dower39294992016-08-30 21:22:36 -0700263{
264 const char *s;
265 wchar_t *name = NULL;
Steve Dower27f26ad2016-09-17 13:51:23 -0700266 char console_type = '\0';
Steve Dower39294992016-08-30 21:22:36 -0700267 int ret = 0;
268 int rwa = 0;
269 int fd = -1;
270 int fd_is_own = 0;
271
272 assert(PyWindowsConsoleIO_Check(self));
273 if (self->handle >= 0) {
274 if (self->closehandle) {
275 /* Have to close the existing file first. */
276 if (internal_close(self) < 0)
277 return -1;
278 }
279 else
280 self->handle = INVALID_HANDLE_VALUE;
281 }
282
283 if (PyFloat_Check(nameobj)) {
284 PyErr_SetString(PyExc_TypeError,
285 "integer argument expected, got float");
286 return -1;
287 }
288
289 fd = _PyLong_AsInt(nameobj);
290 if (fd < 0) {
291 if (!PyErr_Occurred()) {
292 PyErr_SetString(PyExc_ValueError,
293 "negative file descriptor");
294 return -1;
295 }
296 PyErr_Clear();
297 }
298 self->fd = fd;
299
300 if (fd < 0) {
Victor Stinner29adc132017-06-08 18:19:25 +0200301 PyObject *decodedname;
Steve Dower39294992016-08-30 21:22:36 -0700302
303 int d = PyUnicode_FSDecoder(nameobj, (void*)&decodedname);
304 if (!d)
305 return -1;
306
Serhiy Storchakae613e6a2017-06-27 16:03:14 +0300307 name = PyUnicode_AsWideCharString(decodedname, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -0700308 console_type = _PyIO_get_console_type(decodedname);
Steve Dower39294992016-08-30 21:22:36 -0700309 Py_CLEAR(decodedname);
310 if (name == NULL)
311 return -1;
Steve Dower39294992016-08-30 21:22:36 -0700312 }
313
314 s = mode;
315 while (*s) {
316 switch (*s++) {
317 case '+':
318 case 'a':
319 case 'b':
320 case 'x':
321 break;
322 case 'r':
323 if (rwa)
324 goto bad_mode;
325 rwa = 1;
326 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700327 if (console_type == 'x')
328 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700329 break;
330 case 'w':
331 if (rwa)
332 goto bad_mode;
333 rwa = 1;
334 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700335 if (console_type == 'x')
336 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700337 break;
338 default:
339 PyErr_Format(PyExc_ValueError,
340 "invalid mode: %.200s", mode);
341 goto error;
342 }
343 }
344
345 if (!rwa)
346 goto bad_mode;
347
348 if (fd >= 0) {
349 _Py_BEGIN_SUPPRESS_IPH
350 self->handle = (HANDLE)_get_osfhandle(fd);
351 _Py_END_SUPPRESS_IPH
352 self->closehandle = 0;
353 } else {
354 DWORD access = GENERIC_READ;
355
356 self->closehandle = 1;
357 if (!closefd) {
358 PyErr_SetString(PyExc_ValueError,
359 "Cannot use closefd=False with file name");
360 goto error;
361 }
362
363 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700364 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700365
366 Py_BEGIN_ALLOW_THREADS
367 /* Attempt to open for read/write initially, then fall back
368 on the specific access. This is required for modern names
369 CONIN$ and CONOUT$, which allow reading/writing state as
370 well as reading/writing content. */
371 self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
372 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
373 if (self->handle == INVALID_HANDLE_VALUE)
374 self->handle = CreateFileW(name, access,
375 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
376 Py_END_ALLOW_THREADS
377
378 if (self->handle == INVALID_HANDLE_VALUE) {
379 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
380 goto error;
381 }
382 }
383
Steve Dower27f26ad2016-09-17 13:51:23 -0700384 if (console_type == '\0')
385 console_type = _get_console_type(self->handle);
386
387 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700388 PyErr_SetString(PyExc_ValueError,
389 "Cannot open console input buffer for writing");
390 goto error;
391 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700392 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700393 PyErr_SetString(PyExc_ValueError,
394 "Cannot open console output buffer for reading");
395 goto error;
396 }
397
398 self->blksize = DEFAULT_BUFFER_SIZE;
399 memset(self->buf, 0, 4);
400
401 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
402 goto error;
403
404 goto done;
405
406bad_mode:
407 PyErr_SetString(PyExc_ValueError,
408 "Must have exactly one of read or write mode");
409error:
410 ret = -1;
411 internal_close(self);
412
413done:
414 if (name)
415 PyMem_Free(name);
416 return ret;
417}
418
419static int
420winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
421{
422 Py_VISIT(self->dict);
423 return 0;
424}
425
426static int
427winconsoleio_clear(winconsoleio *self)
428{
429 Py_CLEAR(self->dict);
430 return 0;
431}
432
433static void
434winconsoleio_dealloc(winconsoleio *self)
435{
436 self->finalizing = 1;
437 if (_PyIOBase_finalize((PyObject *) self) < 0)
438 return;
439 _PyObject_GC_UNTRACK(self);
440 if (self->weakreflist != NULL)
441 PyObject_ClearWeakRefs((PyObject *) self);
442 Py_CLEAR(self->dict);
443 Py_TYPE(self)->tp_free((PyObject *)self);
444}
445
446static PyObject *
447err_closed(void)
448{
449 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
450 return NULL;
451}
452
453static PyObject *
454err_mode(const char *action)
455{
456 _PyIO_State *state = IO_STATE();
457 if (state != NULL)
458 PyErr_Format(state->unsupported_operation,
459 "Console buffer does not support %s", action);
460 return NULL;
461}
462
463/*[clinic input]
464_io._WindowsConsoleIO.fileno
465
466Return the underlying file descriptor (an integer).
467
468fileno is only set when a file descriptor is used to open
469one of the standard streams.
470
471[clinic start generated code]*/
472
473static PyObject *
474_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
475/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/
476{
477 if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) {
478 _Py_BEGIN_SUPPRESS_IPH
479 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700480 self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700481 else
Steve Dower27f26ad2016-09-17 13:51:23 -0700482 self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700483 _Py_END_SUPPRESS_IPH
484 }
485 if (self->fd < 0)
486 return err_mode("fileno");
487 return PyLong_FromLong(self->fd);
488}
489
490/*[clinic input]
491_io._WindowsConsoleIO.readable
492
493True if console is an input buffer.
494[clinic start generated code]*/
495
496static PyObject *
497_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
498/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
499{
500 if (self->handle == INVALID_HANDLE_VALUE)
501 return err_closed();
502 return PyBool_FromLong((long) self->readable);
503}
504
505/*[clinic input]
506_io._WindowsConsoleIO.writable
507
508True if console is an output buffer.
509[clinic start generated code]*/
510
511static PyObject *
512_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
513/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
514{
515 if (self->handle == INVALID_HANDLE_VALUE)
516 return err_closed();
517 return PyBool_FromLong((long) self->writable);
518}
519
520static DWORD
521_buflen(winconsoleio *self)
522{
Steve Dower312cef72016-10-03 09:04:58 -0700523 for (DWORD i = 0; i < SMALLBUF; ++i) {
Steve Dower39294992016-08-30 21:22:36 -0700524 if (!self->buf[i])
525 return i;
526 }
Steve Dower312cef72016-10-03 09:04:58 -0700527 return SMALLBUF;
Steve Dower39294992016-08-30 21:22:36 -0700528}
529
530static DWORD
531_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
532{
533 DWORD n = 0;
534
535 while (self->buf[0] && len--) {
Steve Dower312cef72016-10-03 09:04:58 -0700536 buf[n++] = self->buf[0];
537 for (int i = 1; i < SMALLBUF; ++i)
538 self->buf[i - 1] = self->buf[i];
539 self->buf[SMALLBUF - 1] = 0;
Steve Dower39294992016-08-30 21:22:36 -0700540 }
541
542 return n;
543}
544
545static wchar_t *
546read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
547 int err = 0, sig = 0;
548
549 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
550 if (!buf)
551 goto error;
Steve Dower312cef72016-10-03 09:04:58 -0700552
Steve Dower39294992016-08-30 21:22:36 -0700553 *readlen = 0;
554
Steve Dower312cef72016-10-03 09:04:58 -0700555 //DebugBreak();
Steve Dower39294992016-08-30 21:22:36 -0700556 Py_BEGIN_ALLOW_THREADS
Steve Dower312cef72016-10-03 09:04:58 -0700557 DWORD off = 0;
558 while (off < maxlen) {
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700559 DWORD n = (DWORD)-1;
560 DWORD len = min(maxlen - off, BUFSIZ);
Steve Dower39294992016-08-30 21:22:36 -0700561 SetLastError(0);
562 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
563
564 if (!res) {
565 err = GetLastError();
566 break;
567 }
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700568 if (n == (DWORD)-1 && (err = GetLastError()) == ERROR_OPERATION_ABORTED) {
569 break;
570 }
Steve Dower39294992016-08-30 21:22:36 -0700571 if (n == 0) {
572 err = GetLastError();
573 if (err != ERROR_OPERATION_ABORTED)
574 break;
575 err = 0;
576 HANDLE hInterruptEvent = _PyOS_SigintEvent();
577 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
Steve Dower312cef72016-10-03 09:04:58 -0700578 == WAIT_OBJECT_0) {
Steve Dower39294992016-08-30 21:22:36 -0700579 ResetEvent(hInterruptEvent);
580 Py_BLOCK_THREADS
581 sig = PyErr_CheckSignals();
582 Py_UNBLOCK_THREADS
583 if (sig < 0)
584 break;
585 }
586 }
587 *readlen += n;
588
589 /* If we didn't read a full buffer that time, don't try
590 again or we will block a second time. */
591 if (n < len)
592 break;
593 /* If the buffer ended with a newline, break out */
594 if (buf[*readlen - 1] == '\n')
595 break;
Steve Dower312cef72016-10-03 09:04:58 -0700596 /* If the buffer ends with a high surrogate, expand the
597 buffer and read an extra character. */
598 WORD char_type;
599 if (off + BUFSIZ >= maxlen &&
600 GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) &&
601 char_type == C3_HIGHSURROGATE) {
602 wchar_t *newbuf;
603 maxlen += 1;
604 Py_BLOCK_THREADS
605 newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t));
606 Py_UNBLOCK_THREADS
607 if (!newbuf) {
608 sig = -1;
609 break;
610 }
611 buf = newbuf;
612 /* Only advance by n and not BUFSIZ in this case */
613 off += n;
614 continue;
615 }
616
617 off += BUFSIZ;
Steve Dower39294992016-08-30 21:22:36 -0700618 }
Steve Dower312cef72016-10-03 09:04:58 -0700619
Steve Dower39294992016-08-30 21:22:36 -0700620 Py_END_ALLOW_THREADS
621
622 if (sig)
623 goto error;
624 if (err) {
625 PyErr_SetFromWindowsErr(err);
626 goto error;
627 }
628
629 if (*readlen > 0 && buf[0] == L'\x1a') {
630 PyMem_Free(buf);
631 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
632 if (!buf)
633 goto error;
634 buf[0] = L'\0';
635 *readlen = 0;
636 }
637
638 return buf;
639
640error:
641 if (buf)
642 PyMem_Free(buf);
643 return NULL;
644}
645
646
647static Py_ssize_t
648readinto(winconsoleio *self, char *buf, Py_ssize_t len)
649{
650 if (self->handle == INVALID_HANDLE_VALUE) {
651 err_closed();
652 return -1;
653 }
654 if (!self->readable) {
655 err_mode("reading");
656 return -1;
657 }
658 if (len == 0)
659 return 0;
660 if (len > BUFMAX) {
661 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
662 return -1;
663 }
664
665 /* Each character may take up to 4 bytes in the final buffer.
666 This is highly conservative, but necessary to avoid
667 failure for any given Unicode input (e.g. \U0010ffff).
668 If the caller requests fewer than 4 bytes, we buffer one
669 character.
670 */
671 DWORD wlen = (DWORD)(len / 4);
672 if (wlen == 0) {
673 wlen = 1;
674 }
675
676 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
677 if (read_len) {
678 buf = &buf[read_len];
679 len -= read_len;
680 wlen -= 1;
681 }
682 if (len == read_len || wlen == 0)
683 return read_len;
684
685 DWORD n;
686 wchar_t *wbuf = read_console_w(self->handle, wlen, &n);
687 if (wbuf == NULL)
688 return -1;
689 if (n == 0) {
690 PyMem_Free(wbuf);
691 return read_len;
692 }
693
694 int err = 0;
695 DWORD u8n = 0;
696
697 Py_BEGIN_ALLOW_THREADS
698 if (len < 4) {
699 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
700 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
701 NULL, NULL))
702 u8n = _copyfrombuf(self, buf, (DWORD)len);
703 } else {
704 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
705 buf, (DWORD)len, NULL, NULL);
706 }
707
708 if (u8n) {
709 read_len += u8n;
710 u8n = 0;
711 } else {
712 err = GetLastError();
713 if (err == ERROR_INSUFFICIENT_BUFFER) {
714 /* Calculate the needed buffer for a more useful error, as this
715 means our "/ 4" logic above is insufficient for some input.
716 */
717 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
718 NULL, 0, NULL, NULL);
719 }
720 }
721 Py_END_ALLOW_THREADS
722
723 PyMem_Free(wbuf);
724
725 if (u8n) {
726 PyErr_Format(PyExc_SystemError,
727 "Buffer had room for %d bytes but %d bytes required",
728 len, u8n);
729 return -1;
730 }
731 if (err) {
732 PyErr_SetFromWindowsErr(err);
733 return -1;
734 }
735
736 return read_len;
737}
738
739/*[clinic input]
740_io._WindowsConsoleIO.readinto
741 buffer: Py_buffer(accept={rwbuffer})
742 /
743
744Same as RawIOBase.readinto().
745[clinic start generated code]*/
746
747static PyObject *
748_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
749/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
750{
751 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
752 if (len < 0)
753 return NULL;
754
755 return PyLong_FromSsize_t(len);
756}
757
758static DWORD
759new_buffersize(winconsoleio *self, DWORD currentsize)
760{
761 DWORD addend;
762
763 /* Expand the buffer by an amount proportional to the current size,
764 giving us amortized linear-time behavior. For bigger sizes, use a
765 less-than-double growth factor to avoid excessive allocation. */
766 if (currentsize > 65536)
767 addend = currentsize >> 3;
768 else
769 addend = 256 + currentsize;
770 if (addend < SMALLCHUNK)
771 /* Avoid tiny read() calls. */
772 addend = SMALLCHUNK;
773 return addend + currentsize;
774}
775
776/*[clinic input]
777_io._WindowsConsoleIO.readall
778
779Read all data from the console, returned as bytes.
780
781Return an empty bytes object at EOF.
782[clinic start generated code]*/
783
784static PyObject *
785_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
786/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
787{
788 wchar_t *buf;
789 DWORD bufsize, n, len = 0;
790 PyObject *bytes;
791 DWORD bytes_size, rn;
792
793 if (self->handle == INVALID_HANDLE_VALUE)
794 return err_closed();
795
796 bufsize = BUFSIZ;
797
798 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
799 if (buf == NULL)
800 return NULL;
801
802 while (1) {
803 wchar_t *subbuf;
804
805 if (len >= (Py_ssize_t)bufsize) {
806 DWORD newsize = new_buffersize(self, len);
807 if (newsize > BUFMAX)
808 break;
809 if (newsize < bufsize) {
810 PyErr_SetString(PyExc_OverflowError,
811 "unbounded read returned more bytes "
812 "than a Python bytes object can hold");
813 PyMem_Free(buf);
814 return NULL;
815 }
816 bufsize = newsize;
817
818 buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t));
819 if (!buf) {
820 PyMem_Free(buf);
821 return NULL;
822 }
823 }
824
825 subbuf = read_console_w(self->handle, bufsize - len, &n);
826
827 if (subbuf == NULL) {
828 PyMem_Free(buf);
829 return NULL;
830 }
831
832 if (n > 0)
833 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
834
835 PyMem_Free(subbuf);
836
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700837 /* when the read is empty we break */
838 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700839 break;
840
841 len += n;
842 }
843
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700844 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700845 /* when the result starts with ^Z we return an empty buffer */
846 PyMem_Free(buf);
847 return PyBytes_FromStringAndSize(NULL, 0);
848 }
849
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700850 if (len) {
851 Py_BEGIN_ALLOW_THREADS
852 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
853 NULL, 0, NULL, NULL);
854 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700855
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700856 if (!bytes_size) {
857 DWORD err = GetLastError();
858 PyMem_Free(buf);
859 return PyErr_SetFromWindowsErr(err);
860 }
861 } else {
862 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700863 }
864
865 bytes_size += _buflen(self);
866 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
867 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
868
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700869 if (len) {
870 Py_BEGIN_ALLOW_THREADS
871 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
872 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
873 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700874
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700875 if (!bytes_size) {
876 DWORD err = GetLastError();
877 PyMem_Free(buf);
878 Py_CLEAR(bytes);
879 return PyErr_SetFromWindowsErr(err);
880 }
881
882 /* add back the number of preserved bytes */
883 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700884 }
885
886 PyMem_Free(buf);
887 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
888 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
889 Py_CLEAR(bytes);
890 return NULL;
891 }
892 }
893 return bytes;
894}
895
896/*[clinic input]
897_io._WindowsConsoleIO.read
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300898 size: Py_ssize_t(accept={int, NoneType}) = -1
Steve Dower39294992016-08-30 21:22:36 -0700899 /
900
901Read at most size bytes, returned as bytes.
902
903Only makes one system call when size is a positive integer,
904so less data may be returned than requested.
905Return an empty bytes object at EOF.
906[clinic start generated code]*/
907
908static PyObject *
909_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300910/*[clinic end generated code: output=57df68af9f4b22d0 input=8bc73bc15d0fa072]*/
Steve Dower39294992016-08-30 21:22:36 -0700911{
912 PyObject *bytes;
913 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700914
Steve Dower39294992016-08-30 21:22:36 -0700915 if (self->handle == INVALID_HANDLE_VALUE)
916 return err_closed();
917 if (!self->readable)
918 return err_mode("reading");
919
920 if (size < 0)
921 return _io__WindowsConsoleIO_readall_impl(self);
922 if (size > BUFMAX) {
923 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
924 return NULL;
925 }
926
927 bytes = PyBytes_FromStringAndSize(NULL, size);
928 if (bytes == NULL)
929 return NULL;
930
931 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
932 if (bytes_size < 0) {
933 Py_CLEAR(bytes);
934 return NULL;
935 }
936
937 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
938 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
939 Py_CLEAR(bytes);
940 return NULL;
941 }
942 }
943
944 return bytes;
945}
946
947/*[clinic input]
948_io._WindowsConsoleIO.write
949 b: Py_buffer
950 /
951
952Write buffer b to file, return number of bytes written.
953
954Only makes one system call, so not all of the data may be written.
955The number of bytes actually written is returned.
956[clinic start generated code]*/
957
958static PyObject *
959_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
960/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
961{
962 BOOL res = TRUE;
963 wchar_t *wbuf;
964 DWORD len, wlen, n = 0;
965
966 if (self->handle == INVALID_HANDLE_VALUE)
967 return err_closed();
968 if (!self->writable)
969 return err_mode("writing");
970
Serhiy Storchaka42c35d92018-02-24 18:55:51 +0200971 if (!b->len) {
972 return PyLong_FromLong(0);
973 }
Steve Dower39294992016-08-30 21:22:36 -0700974 if (b->len > BUFMAX)
975 len = BUFMAX;
976 else
977 len = (DWORD)b->len;
978
979 Py_BEGIN_ALLOW_THREADS
980 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
981
982 /* issue11395 there is an unspecified upper bound on how many bytes
983 can be written at once. We cap at 32k - the caller will have to
984 handle partial writes.
985 Since we don't know how many input bytes are being ignored, we
986 have to reduce and recalculate. */
987 while (wlen > 32766 / sizeof(wchar_t)) {
988 len /= 2;
989 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
990 }
991 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700992
Steve Dower39294992016-08-30 21:22:36 -0700993 if (!wlen)
994 return PyErr_SetFromWindowsErr(0);
995
996 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
997
998 Py_BEGIN_ALLOW_THREADS
999 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
1000 if (wlen) {
1001 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
Segev Finer523776c2017-06-02 19:26:01 +03001002 if (res && n < wlen) {
Steve Dower39294992016-08-30 21:22:36 -07001003 /* Wrote fewer characters than expected, which means our
1004 * len value may be wrong. So recalculate it from the
1005 * characters that were written. As this could potentially
1006 * result in a different value, we also validate that value.
1007 */
1008 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
1009 NULL, 0, NULL, NULL);
1010 if (len) {
1011 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
1012 NULL, 0);
1013 assert(wlen == len);
1014 }
1015 }
1016 } else
1017 res = 0;
1018 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001019
Steve Dower39294992016-08-30 21:22:36 -07001020 if (!res) {
1021 DWORD err = GetLastError();
1022 PyMem_Free(wbuf);
1023 return PyErr_SetFromWindowsErr(err);
1024 }
1025
1026 PyMem_Free(wbuf);
1027 return PyLong_FromSsize_t(len);
1028}
1029
1030static PyObject *
1031winconsoleio_repr(winconsoleio *self)
1032{
1033 if (self->handle == INVALID_HANDLE_VALUE)
1034 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1035
1036 if (self->readable)
1037 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1038 self->closehandle ? "True" : "False");
1039 if (self->writable)
1040 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1041 self->closehandle ? "True" : "False");
1042
1043 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1044 return NULL;
1045}
1046
1047/*[clinic input]
1048_io._WindowsConsoleIO.isatty
1049
1050Always True.
1051[clinic start generated code]*/
1052
1053static PyObject *
1054_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1055/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1056{
1057 if (self->handle == INVALID_HANDLE_VALUE)
1058 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001059
Steve Dower39294992016-08-30 21:22:36 -07001060 Py_RETURN_TRUE;
1061}
1062
1063static PyObject *
Siddhesh Poyarekar55edd0c2018-04-30 00:29:33 +05301064winconsoleio_getstate(winconsoleio *self, PyObject *Py_UNUSED(ignored))
Steve Dower39294992016-08-30 21:22:36 -07001065{
1066 PyErr_Format(PyExc_TypeError,
1067 "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
1068 return NULL;
1069}
1070
1071#include "clinic/winconsoleio.c.h"
1072
1073static PyMethodDef winconsoleio_methods[] = {
1074 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1075 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1076 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1077 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1078 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1079 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1080 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1081 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1082 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
1083 {"__getstate__", (PyCFunction)winconsoleio_getstate, METH_NOARGS, NULL},
1084 {NULL, NULL} /* sentinel */
1085};
1086
1087/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1088
1089static PyObject *
1090get_closed(winconsoleio *self, void *closure)
1091{
1092 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1093}
1094
1095static PyObject *
1096get_closefd(winconsoleio *self, void *closure)
1097{
1098 return PyBool_FromLong((long)(self->closehandle));
1099}
1100
1101static PyObject *
1102get_mode(winconsoleio *self, void *closure)
1103{
1104 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1105}
1106
1107static PyGetSetDef winconsoleio_getsetlist[] = {
1108 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1109 {"closefd", (getter)get_closefd, NULL,
1110 "True if the file descriptor will be closed by close()."},
1111 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1112 {NULL},
1113};
1114
1115static PyMemberDef winconsoleio_members[] = {
1116 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1117 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1118 {NULL}
1119};
1120
1121PyTypeObject PyWindowsConsoleIO_Type = {
1122 PyVarObject_HEAD_INIT(NULL, 0)
1123 "_io._WindowsConsoleIO",
1124 sizeof(winconsoleio),
1125 0,
1126 (destructor)winconsoleio_dealloc, /* tp_dealloc */
1127 0, /* tp_print */
1128 0, /* tp_getattr */
1129 0, /* tp_setattr */
1130 0, /* tp_reserved */
1131 (reprfunc)winconsoleio_repr, /* tp_repr */
1132 0, /* tp_as_number */
1133 0, /* tp_as_sequence */
1134 0, /* tp_as_mapping */
1135 0, /* tp_hash */
1136 0, /* tp_call */
1137 0, /* tp_str */
1138 PyObject_GenericGetAttr, /* tp_getattro */
1139 0, /* tp_setattro */
1140 0, /* tp_as_buffer */
1141 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1142 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1143 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1144 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1145 (inquiry)winconsoleio_clear, /* tp_clear */
1146 0, /* tp_richcompare */
1147 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1148 0, /* tp_iter */
1149 0, /* tp_iternext */
1150 winconsoleio_methods, /* tp_methods */
1151 winconsoleio_members, /* tp_members */
1152 winconsoleio_getsetlist, /* tp_getset */
1153 0, /* tp_base */
1154 0, /* tp_dict */
1155 0, /* tp_descr_get */
1156 0, /* tp_descr_set */
1157 offsetof(winconsoleio, dict), /* tp_dictoffset */
1158 _io__WindowsConsoleIO___init__, /* tp_init */
1159 PyType_GenericAlloc, /* tp_alloc */
1160 winconsoleio_new, /* tp_new */
1161 PyObject_GC_Del, /* tp_free */
1162 0, /* tp_is_gc */
1163 0, /* tp_bases */
1164 0, /* tp_mro */
1165 0, /* tp_cache */
1166 0, /* tp_subclasses */
1167 0, /* tp_weaklist */
1168 0, /* tp_del */
1169 0, /* tp_version_tag */
1170 0, /* tp_finalize */
1171};
1172
Benjamin Petersone5024512018-09-12 12:06:42 -07001173PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
Steve Dower312cef72016-10-03 09:04:58 -07001174
Steve Dower39294992016-08-30 21:22:36 -07001175#endif /* MS_WINDOWS */