blob: dd0997a10580d0de25f7654c4e56c5a32c3a96d5 [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
15#include "structmember.h"
16#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);
Victor Stinner61bdb0d2016-12-09 15:39:28 +0100207 res = _PyObject_CallMethodIdObjArgs((PyObject*)&PyRawIOBase_Type,
208 &PyId_close, self, NULL);
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
284 if (PyFloat_Check(nameobj)) {
285 PyErr_SetString(PyExc_TypeError,
286 "integer argument expected, got float");
287 return -1;
288 }
289
290 fd = _PyLong_AsInt(nameobj);
291 if (fd < 0) {
292 if (!PyErr_Occurred()) {
293 PyErr_SetString(PyExc_ValueError,
294 "negative file descriptor");
295 return -1;
296 }
297 PyErr_Clear();
298 }
299 self->fd = fd;
300
301 if (fd < 0) {
Victor Stinner29adc132017-06-08 18:19:25 +0200302 PyObject *decodedname;
Steve Dower39294992016-08-30 21:22:36 -0700303
304 int d = PyUnicode_FSDecoder(nameobj, (void*)&decodedname);
305 if (!d)
306 return -1;
307
Serhiy Storchakae613e6a2017-06-27 16:03:14 +0300308 name = PyUnicode_AsWideCharString(decodedname, NULL);
Steve Dower27f26ad2016-09-17 13:51:23 -0700309 console_type = _PyIO_get_console_type(decodedname);
Steve Dower39294992016-08-30 21:22:36 -0700310 Py_CLEAR(decodedname);
311 if (name == NULL)
312 return -1;
Steve Dower39294992016-08-30 21:22:36 -0700313 }
314
315 s = mode;
316 while (*s) {
317 switch (*s++) {
318 case '+':
319 case 'a':
320 case 'b':
321 case 'x':
322 break;
323 case 'r':
324 if (rwa)
325 goto bad_mode;
326 rwa = 1;
327 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700328 if (console_type == 'x')
329 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700330 break;
331 case 'w':
332 if (rwa)
333 goto bad_mode;
334 rwa = 1;
335 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700336 if (console_type == 'x')
337 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700338 break;
339 default:
340 PyErr_Format(PyExc_ValueError,
341 "invalid mode: %.200s", mode);
342 goto error;
343 }
344 }
345
346 if (!rwa)
347 goto bad_mode;
348
349 if (fd >= 0) {
350 _Py_BEGIN_SUPPRESS_IPH
351 self->handle = (HANDLE)_get_osfhandle(fd);
352 _Py_END_SUPPRESS_IPH
353 self->closehandle = 0;
354 } else {
355 DWORD access = GENERIC_READ;
356
357 self->closehandle = 1;
358 if (!closefd) {
359 PyErr_SetString(PyExc_ValueError,
360 "Cannot use closefd=False with file name");
361 goto error;
362 }
363
364 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700365 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700366
367 Py_BEGIN_ALLOW_THREADS
368 /* Attempt to open for read/write initially, then fall back
369 on the specific access. This is required for modern names
370 CONIN$ and CONOUT$, which allow reading/writing state as
371 well as reading/writing content. */
372 self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
373 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
374 if (self->handle == INVALID_HANDLE_VALUE)
375 self->handle = CreateFileW(name, access,
376 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
377 Py_END_ALLOW_THREADS
378
379 if (self->handle == INVALID_HANDLE_VALUE) {
380 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
381 goto error;
382 }
383 }
384
Steve Dower27f26ad2016-09-17 13:51:23 -0700385 if (console_type == '\0')
386 console_type = _get_console_type(self->handle);
387
388 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700389 PyErr_SetString(PyExc_ValueError,
390 "Cannot open console input buffer for writing");
391 goto error;
392 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700393 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700394 PyErr_SetString(PyExc_ValueError,
395 "Cannot open console output buffer for reading");
396 goto error;
397 }
398
399 self->blksize = DEFAULT_BUFFER_SIZE;
400 memset(self->buf, 0, 4);
401
402 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
403 goto error;
404
405 goto done;
406
407bad_mode:
408 PyErr_SetString(PyExc_ValueError,
409 "Must have exactly one of read or write mode");
410error:
411 ret = -1;
412 internal_close(self);
413
414done:
415 if (name)
416 PyMem_Free(name);
417 return ret;
418}
419
420static int
421winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
422{
423 Py_VISIT(self->dict);
424 return 0;
425}
426
427static int
428winconsoleio_clear(winconsoleio *self)
429{
430 Py_CLEAR(self->dict);
431 return 0;
432}
433
434static void
435winconsoleio_dealloc(winconsoleio *self)
436{
437 self->finalizing = 1;
438 if (_PyIOBase_finalize((PyObject *) self) < 0)
439 return;
440 _PyObject_GC_UNTRACK(self);
441 if (self->weakreflist != NULL)
442 PyObject_ClearWeakRefs((PyObject *) self);
443 Py_CLEAR(self->dict);
444 Py_TYPE(self)->tp_free((PyObject *)self);
445}
446
447static PyObject *
448err_closed(void)
449{
450 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
451 return NULL;
452}
453
454static PyObject *
455err_mode(const char *action)
456{
457 _PyIO_State *state = IO_STATE();
458 if (state != NULL)
459 PyErr_Format(state->unsupported_operation,
460 "Console buffer does not support %s", action);
461 return NULL;
462}
463
464/*[clinic input]
465_io._WindowsConsoleIO.fileno
466
467Return the underlying file descriptor (an integer).
468
469fileno is only set when a file descriptor is used to open
470one of the standard streams.
471
472[clinic start generated code]*/
473
474static PyObject *
475_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
476/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/
477{
478 if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) {
479 _Py_BEGIN_SUPPRESS_IPH
480 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700481 self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700482 else
Steve Dower27f26ad2016-09-17 13:51:23 -0700483 self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700484 _Py_END_SUPPRESS_IPH
485 }
486 if (self->fd < 0)
487 return err_mode("fileno");
488 return PyLong_FromLong(self->fd);
489}
490
491/*[clinic input]
492_io._WindowsConsoleIO.readable
493
494True if console is an input buffer.
495[clinic start generated code]*/
496
497static PyObject *
498_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
499/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
500{
501 if (self->handle == INVALID_HANDLE_VALUE)
502 return err_closed();
503 return PyBool_FromLong((long) self->readable);
504}
505
506/*[clinic input]
507_io._WindowsConsoleIO.writable
508
509True if console is an output buffer.
510[clinic start generated code]*/
511
512static PyObject *
513_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
514/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
515{
516 if (self->handle == INVALID_HANDLE_VALUE)
517 return err_closed();
518 return PyBool_FromLong((long) self->writable);
519}
520
521static DWORD
522_buflen(winconsoleio *self)
523{
Steve Dower312cef72016-10-03 09:04:58 -0700524 for (DWORD i = 0; i < SMALLBUF; ++i) {
Steve Dower39294992016-08-30 21:22:36 -0700525 if (!self->buf[i])
526 return i;
527 }
Steve Dower312cef72016-10-03 09:04:58 -0700528 return SMALLBUF;
Steve Dower39294992016-08-30 21:22:36 -0700529}
530
531static DWORD
532_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
533{
534 DWORD n = 0;
535
536 while (self->buf[0] && len--) {
Steve Dower312cef72016-10-03 09:04:58 -0700537 buf[n++] = self->buf[0];
538 for (int i = 1; i < SMALLBUF; ++i)
539 self->buf[i - 1] = self->buf[i];
540 self->buf[SMALLBUF - 1] = 0;
Steve Dower39294992016-08-30 21:22:36 -0700541 }
542
543 return n;
544}
545
546static wchar_t *
547read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
548 int err = 0, sig = 0;
549
550 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
551 if (!buf)
552 goto error;
Steve Dower312cef72016-10-03 09:04:58 -0700553
Steve Dower39294992016-08-30 21:22:36 -0700554 *readlen = 0;
555
Steve Dower312cef72016-10-03 09:04:58 -0700556 //DebugBreak();
Steve Dower39294992016-08-30 21:22:36 -0700557 Py_BEGIN_ALLOW_THREADS
Steve Dower312cef72016-10-03 09:04:58 -0700558 DWORD off = 0;
559 while (off < maxlen) {
Victor Stinnerbcda8f12018-11-21 22:27:47 +0100560 DWORD n = (DWORD)-1;
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700561 DWORD len = min(maxlen - off, BUFSIZ);
Steve Dower39294992016-08-30 21:22:36 -0700562 SetLastError(0);
563 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
564
565 if (!res) {
566 err = GetLastError();
567 break;
568 }
ValeriyaSinevichce75df32018-07-19 15:34:03 -0700569 if (n == (DWORD)-1 && (err = GetLastError()) == ERROR_OPERATION_ABORTED) {
570 break;
571 }
Steve Dower39294992016-08-30 21:22:36 -0700572 if (n == 0) {
573 err = GetLastError();
574 if (err != ERROR_OPERATION_ABORTED)
575 break;
576 err = 0;
577 HANDLE hInterruptEvent = _PyOS_SigintEvent();
578 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
Steve Dower312cef72016-10-03 09:04:58 -0700579 == WAIT_OBJECT_0) {
Steve Dower39294992016-08-30 21:22:36 -0700580 ResetEvent(hInterruptEvent);
581 Py_BLOCK_THREADS
582 sig = PyErr_CheckSignals();
583 Py_UNBLOCK_THREADS
584 if (sig < 0)
585 break;
586 }
587 }
588 *readlen += n;
589
590 /* If we didn't read a full buffer that time, don't try
591 again or we will block a second time. */
592 if (n < len)
593 break;
594 /* If the buffer ended with a newline, break out */
595 if (buf[*readlen - 1] == '\n')
596 break;
Steve Dower312cef72016-10-03 09:04:58 -0700597 /* If the buffer ends with a high surrogate, expand the
598 buffer and read an extra character. */
599 WORD char_type;
600 if (off + BUFSIZ >= maxlen &&
601 GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) &&
602 char_type == C3_HIGHSURROGATE) {
603 wchar_t *newbuf;
604 maxlen += 1;
605 Py_BLOCK_THREADS
606 newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t));
607 Py_UNBLOCK_THREADS
608 if (!newbuf) {
609 sig = -1;
610 break;
611 }
612 buf = newbuf;
613 /* Only advance by n and not BUFSIZ in this case */
614 off += n;
615 continue;
616 }
617
618 off += BUFSIZ;
Steve Dower39294992016-08-30 21:22:36 -0700619 }
Steve Dower312cef72016-10-03 09:04:58 -0700620
Steve Dower39294992016-08-30 21:22:36 -0700621 Py_END_ALLOW_THREADS
622
623 if (sig)
624 goto error;
625 if (err) {
626 PyErr_SetFromWindowsErr(err);
627 goto error;
628 }
629
630 if (*readlen > 0 && buf[0] == L'\x1a') {
631 PyMem_Free(buf);
632 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
633 if (!buf)
634 goto error;
635 buf[0] = L'\0';
636 *readlen = 0;
637 }
638
639 return buf;
640
641error:
642 if (buf)
643 PyMem_Free(buf);
644 return NULL;
645}
646
647
648static Py_ssize_t
649readinto(winconsoleio *self, char *buf, Py_ssize_t len)
650{
651 if (self->handle == INVALID_HANDLE_VALUE) {
652 err_closed();
653 return -1;
654 }
655 if (!self->readable) {
656 err_mode("reading");
657 return -1;
658 }
659 if (len == 0)
660 return 0;
661 if (len > BUFMAX) {
662 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
663 return -1;
664 }
665
666 /* Each character may take up to 4 bytes in the final buffer.
667 This is highly conservative, but necessary to avoid
668 failure for any given Unicode input (e.g. \U0010ffff).
669 If the caller requests fewer than 4 bytes, we buffer one
670 character.
671 */
672 DWORD wlen = (DWORD)(len / 4);
673 if (wlen == 0) {
674 wlen = 1;
675 }
676
677 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
678 if (read_len) {
679 buf = &buf[read_len];
680 len -= read_len;
681 wlen -= 1;
682 }
683 if (len == read_len || wlen == 0)
684 return read_len;
685
686 DWORD n;
687 wchar_t *wbuf = read_console_w(self->handle, wlen, &n);
688 if (wbuf == NULL)
689 return -1;
690 if (n == 0) {
691 PyMem_Free(wbuf);
692 return read_len;
693 }
694
695 int err = 0;
696 DWORD u8n = 0;
697
698 Py_BEGIN_ALLOW_THREADS
699 if (len < 4) {
700 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
701 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
702 NULL, NULL))
703 u8n = _copyfrombuf(self, buf, (DWORD)len);
704 } else {
705 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
706 buf, (DWORD)len, NULL, NULL);
707 }
708
709 if (u8n) {
710 read_len += u8n;
711 u8n = 0;
712 } else {
713 err = GetLastError();
714 if (err == ERROR_INSUFFICIENT_BUFFER) {
715 /* Calculate the needed buffer for a more useful error, as this
716 means our "/ 4" logic above is insufficient for some input.
717 */
718 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
719 NULL, 0, NULL, NULL);
720 }
721 }
722 Py_END_ALLOW_THREADS
723
724 PyMem_Free(wbuf);
725
726 if (u8n) {
727 PyErr_Format(PyExc_SystemError,
728 "Buffer had room for %d bytes but %d bytes required",
729 len, u8n);
730 return -1;
731 }
732 if (err) {
733 PyErr_SetFromWindowsErr(err);
734 return -1;
735 }
736
737 return read_len;
738}
739
740/*[clinic input]
741_io._WindowsConsoleIO.readinto
742 buffer: Py_buffer(accept={rwbuffer})
743 /
744
745Same as RawIOBase.readinto().
746[clinic start generated code]*/
747
748static PyObject *
749_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
750/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
751{
752 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
753 if (len < 0)
754 return NULL;
755
756 return PyLong_FromSsize_t(len);
757}
758
759static DWORD
760new_buffersize(winconsoleio *self, DWORD currentsize)
761{
762 DWORD addend;
763
764 /* Expand the buffer by an amount proportional to the current size,
765 giving us amortized linear-time behavior. For bigger sizes, use a
766 less-than-double growth factor to avoid excessive allocation. */
767 if (currentsize > 65536)
768 addend = currentsize >> 3;
769 else
770 addend = 256 + currentsize;
771 if (addend < SMALLCHUNK)
772 /* Avoid tiny read() calls. */
773 addend = SMALLCHUNK;
774 return addend + currentsize;
775}
776
777/*[clinic input]
778_io._WindowsConsoleIO.readall
779
780Read all data from the console, returned as bytes.
781
782Return an empty bytes object at EOF.
783[clinic start generated code]*/
784
785static PyObject *
786_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
787/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
788{
789 wchar_t *buf;
790 DWORD bufsize, n, len = 0;
791 PyObject *bytes;
792 DWORD bytes_size, rn;
793
794 if (self->handle == INVALID_HANDLE_VALUE)
795 return err_closed();
796
797 bufsize = BUFSIZ;
798
799 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
800 if (buf == NULL)
801 return NULL;
802
803 while (1) {
804 wchar_t *subbuf;
805
806 if (len >= (Py_ssize_t)bufsize) {
807 DWORD newsize = new_buffersize(self, len);
808 if (newsize > BUFMAX)
809 break;
810 if (newsize < bufsize) {
811 PyErr_SetString(PyExc_OverflowError,
812 "unbounded read returned more bytes "
813 "than a Python bytes object can hold");
814 PyMem_Free(buf);
815 return NULL;
816 }
817 bufsize = newsize;
818
Zackery Spytz4c49da02018-12-07 03:11:30 -0700819 wchar_t *tmp = PyMem_Realloc(buf,
820 (bufsize + 1) * sizeof(wchar_t));
821 if (tmp == NULL) {
Steve Dower39294992016-08-30 21:22:36 -0700822 PyMem_Free(buf);
823 return NULL;
824 }
Zackery Spytz4c49da02018-12-07 03:11:30 -0700825 buf = tmp;
Steve Dower39294992016-08-30 21:22:36 -0700826 }
827
828 subbuf = read_console_w(self->handle, bufsize - len, &n);
829
830 if (subbuf == NULL) {
831 PyMem_Free(buf);
832 return NULL;
833 }
834
835 if (n > 0)
836 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
837
838 PyMem_Free(subbuf);
839
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700840 /* when the read is empty we break */
841 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700842 break;
843
844 len += n;
845 }
846
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700847 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700848 /* when the result starts with ^Z we return an empty buffer */
849 PyMem_Free(buf);
850 return PyBytes_FromStringAndSize(NULL, 0);
851 }
852
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700853 if (len) {
854 Py_BEGIN_ALLOW_THREADS
855 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
856 NULL, 0, NULL, NULL);
857 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700858
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700859 if (!bytes_size) {
860 DWORD err = GetLastError();
861 PyMem_Free(buf);
862 return PyErr_SetFromWindowsErr(err);
863 }
864 } else {
865 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700866 }
867
868 bytes_size += _buflen(self);
869 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
870 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
871
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700872 if (len) {
873 Py_BEGIN_ALLOW_THREADS
874 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
875 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
876 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700877
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700878 if (!bytes_size) {
879 DWORD err = GetLastError();
880 PyMem_Free(buf);
881 Py_CLEAR(bytes);
882 return PyErr_SetFromWindowsErr(err);
883 }
884
885 /* add back the number of preserved bytes */
886 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700887 }
888
889 PyMem_Free(buf);
890 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
891 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
892 Py_CLEAR(bytes);
893 return NULL;
894 }
895 }
896 return bytes;
897}
898
899/*[clinic input]
900_io._WindowsConsoleIO.read
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300901 size: Py_ssize_t(accept={int, NoneType}) = -1
Steve Dower39294992016-08-30 21:22:36 -0700902 /
903
904Read at most size bytes, returned as bytes.
905
906Only makes one system call when size is a positive integer,
907so less data may be returned than requested.
908Return an empty bytes object at EOF.
909[clinic start generated code]*/
910
911static PyObject *
912_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300913/*[clinic end generated code: output=57df68af9f4b22d0 input=8bc73bc15d0fa072]*/
Steve Dower39294992016-08-30 21:22:36 -0700914{
915 PyObject *bytes;
916 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700917
Steve Dower39294992016-08-30 21:22:36 -0700918 if (self->handle == INVALID_HANDLE_VALUE)
919 return err_closed();
920 if (!self->readable)
921 return err_mode("reading");
922
923 if (size < 0)
924 return _io__WindowsConsoleIO_readall_impl(self);
925 if (size > BUFMAX) {
926 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
927 return NULL;
928 }
929
930 bytes = PyBytes_FromStringAndSize(NULL, size);
931 if (bytes == NULL)
932 return NULL;
933
934 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
935 if (bytes_size < 0) {
936 Py_CLEAR(bytes);
937 return NULL;
938 }
939
940 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
941 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
942 Py_CLEAR(bytes);
943 return NULL;
944 }
945 }
946
947 return bytes;
948}
949
950/*[clinic input]
951_io._WindowsConsoleIO.write
952 b: Py_buffer
953 /
954
955Write buffer b to file, return number of bytes written.
956
957Only makes one system call, so not all of the data may be written.
958The number of bytes actually written is returned.
959[clinic start generated code]*/
960
961static PyObject *
962_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
963/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
964{
965 BOOL res = TRUE;
966 wchar_t *wbuf;
967 DWORD len, wlen, n = 0;
968
969 if (self->handle == INVALID_HANDLE_VALUE)
970 return err_closed();
971 if (!self->writable)
972 return err_mode("writing");
973
Serhiy Storchaka42c35d92018-02-24 18:55:51 +0200974 if (!b->len) {
975 return PyLong_FromLong(0);
976 }
Steve Dower39294992016-08-30 21:22:36 -0700977 if (b->len > BUFMAX)
978 len = BUFMAX;
979 else
980 len = (DWORD)b->len;
981
982 Py_BEGIN_ALLOW_THREADS
983 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
984
985 /* issue11395 there is an unspecified upper bound on how many bytes
986 can be written at once. We cap at 32k - the caller will have to
987 handle partial writes.
988 Since we don't know how many input bytes are being ignored, we
989 have to reduce and recalculate. */
990 while (wlen > 32766 / sizeof(wchar_t)) {
991 len /= 2;
992 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
993 }
994 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700995
Steve Dower39294992016-08-30 21:22:36 -0700996 if (!wlen)
997 return PyErr_SetFromWindowsErr(0);
998
999 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
1000
1001 Py_BEGIN_ALLOW_THREADS
1002 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
1003 if (wlen) {
1004 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
Segev Finer523776c2017-06-02 19:26:01 +03001005 if (res && n < wlen) {
Steve Dower39294992016-08-30 21:22:36 -07001006 /* Wrote fewer characters than expected, which means our
1007 * len value may be wrong. So recalculate it from the
1008 * characters that were written. As this could potentially
1009 * result in a different value, we also validate that value.
1010 */
1011 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
1012 NULL, 0, NULL, NULL);
1013 if (len) {
1014 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
1015 NULL, 0);
1016 assert(wlen == len);
1017 }
1018 }
1019 } else
1020 res = 0;
1021 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001022
Steve Dower39294992016-08-30 21:22:36 -07001023 if (!res) {
1024 DWORD err = GetLastError();
1025 PyMem_Free(wbuf);
1026 return PyErr_SetFromWindowsErr(err);
1027 }
1028
1029 PyMem_Free(wbuf);
1030 return PyLong_FromSsize_t(len);
1031}
1032
1033static PyObject *
1034winconsoleio_repr(winconsoleio *self)
1035{
1036 if (self->handle == INVALID_HANDLE_VALUE)
1037 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1038
1039 if (self->readable)
1040 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1041 self->closehandle ? "True" : "False");
1042 if (self->writable)
1043 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1044 self->closehandle ? "True" : "False");
1045
1046 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1047 return NULL;
1048}
1049
1050/*[clinic input]
1051_io._WindowsConsoleIO.isatty
1052
1053Always True.
1054[clinic start generated code]*/
1055
1056static PyObject *
1057_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1058/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1059{
1060 if (self->handle == INVALID_HANDLE_VALUE)
1061 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001062
Steve Dower39294992016-08-30 21:22:36 -07001063 Py_RETURN_TRUE;
1064}
1065
Steve Dower39294992016-08-30 21:22:36 -07001066#include "clinic/winconsoleio.c.h"
1067
1068static PyMethodDef winconsoleio_methods[] = {
1069 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1070 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1071 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1072 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1073 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1074 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1075 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1076 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1077 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
Steve Dower39294992016-08-30 21:22:36 -07001078 {NULL, NULL} /* sentinel */
1079};
1080
1081/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1082
1083static PyObject *
1084get_closed(winconsoleio *self, void *closure)
1085{
1086 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1087}
1088
1089static PyObject *
1090get_closefd(winconsoleio *self, void *closure)
1091{
1092 return PyBool_FromLong((long)(self->closehandle));
1093}
1094
1095static PyObject *
1096get_mode(winconsoleio *self, void *closure)
1097{
1098 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1099}
1100
1101static PyGetSetDef winconsoleio_getsetlist[] = {
1102 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1103 {"closefd", (getter)get_closefd, NULL,
1104 "True if the file descriptor will be closed by close()."},
1105 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1106 {NULL},
1107};
1108
1109static PyMemberDef winconsoleio_members[] = {
1110 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1111 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1112 {NULL}
1113};
1114
1115PyTypeObject PyWindowsConsoleIO_Type = {
1116 PyVarObject_HEAD_INIT(NULL, 0)
1117 "_io._WindowsConsoleIO",
1118 sizeof(winconsoleio),
1119 0,
1120 (destructor)winconsoleio_dealloc, /* tp_dealloc */
1121 0, /* tp_print */
1122 0, /* tp_getattr */
1123 0, /* tp_setattr */
1124 0, /* tp_reserved */
1125 (reprfunc)winconsoleio_repr, /* tp_repr */
1126 0, /* tp_as_number */
1127 0, /* tp_as_sequence */
1128 0, /* tp_as_mapping */
1129 0, /* tp_hash */
1130 0, /* tp_call */
1131 0, /* tp_str */
1132 PyObject_GenericGetAttr, /* tp_getattro */
1133 0, /* tp_setattro */
1134 0, /* tp_as_buffer */
1135 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1136 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1137 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1138 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1139 (inquiry)winconsoleio_clear, /* tp_clear */
1140 0, /* tp_richcompare */
1141 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1142 0, /* tp_iter */
1143 0, /* tp_iternext */
1144 winconsoleio_methods, /* tp_methods */
1145 winconsoleio_members, /* tp_members */
1146 winconsoleio_getsetlist, /* tp_getset */
1147 0, /* tp_base */
1148 0, /* tp_dict */
1149 0, /* tp_descr_get */
1150 0, /* tp_descr_set */
1151 offsetof(winconsoleio, dict), /* tp_dictoffset */
1152 _io__WindowsConsoleIO___init__, /* tp_init */
1153 PyType_GenericAlloc, /* tp_alloc */
1154 winconsoleio_new, /* tp_new */
1155 PyObject_GC_Del, /* tp_free */
1156 0, /* tp_is_gc */
1157 0, /* tp_bases */
1158 0, /* tp_mro */
1159 0, /* tp_cache */
1160 0, /* tp_subclasses */
1161 0, /* tp_weaklist */
1162 0, /* tp_del */
1163 0, /* tp_version_tag */
1164 0, /* tp_finalize */
1165};
1166
Benjamin Petersone5024512018-09-12 12:06:42 -07001167PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
Steve Dower312cef72016-10-03 09:04:58 -07001168
Steve Dower39294992016-08-30 21:22:36 -07001169#endif /* MS_WINDOWS */