blob: 824690ff58d68863e9f14302fe6019e4de8e0c4c [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
819 buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t));
820 if (!buf) {
821 PyMem_Free(buf);
822 return NULL;
823 }
824 }
825
826 subbuf = read_console_w(self->handle, bufsize - len, &n);
827
828 if (subbuf == NULL) {
829 PyMem_Free(buf);
830 return NULL;
831 }
832
833 if (n > 0)
834 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
835
836 PyMem_Free(subbuf);
837
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700838 /* when the read is empty we break */
839 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700840 break;
841
842 len += n;
843 }
844
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700845 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700846 /* when the result starts with ^Z we return an empty buffer */
847 PyMem_Free(buf);
848 return PyBytes_FromStringAndSize(NULL, 0);
849 }
850
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700851 if (len) {
852 Py_BEGIN_ALLOW_THREADS
853 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
854 NULL, 0, NULL, NULL);
855 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700856
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700857 if (!bytes_size) {
858 DWORD err = GetLastError();
859 PyMem_Free(buf);
860 return PyErr_SetFromWindowsErr(err);
861 }
862 } else {
863 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700864 }
865
866 bytes_size += _buflen(self);
867 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
868 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
869
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700870 if (len) {
871 Py_BEGIN_ALLOW_THREADS
872 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
873 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
874 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700875
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700876 if (!bytes_size) {
877 DWORD err = GetLastError();
878 PyMem_Free(buf);
879 Py_CLEAR(bytes);
880 return PyErr_SetFromWindowsErr(err);
881 }
882
883 /* add back the number of preserved bytes */
884 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700885 }
886
887 PyMem_Free(buf);
888 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
889 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
890 Py_CLEAR(bytes);
891 return NULL;
892 }
893 }
894 return bytes;
895}
896
897/*[clinic input]
898_io._WindowsConsoleIO.read
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300899 size: Py_ssize_t(accept={int, NoneType}) = -1
Steve Dower39294992016-08-30 21:22:36 -0700900 /
901
902Read at most size bytes, returned as bytes.
903
904Only makes one system call when size is a positive integer,
905so less data may be returned than requested.
906Return an empty bytes object at EOF.
907[clinic start generated code]*/
908
909static PyObject *
910_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
Serhiy Storchaka762bf402017-03-30 09:15:31 +0300911/*[clinic end generated code: output=57df68af9f4b22d0 input=8bc73bc15d0fa072]*/
Steve Dower39294992016-08-30 21:22:36 -0700912{
913 PyObject *bytes;
914 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700915
Steve Dower39294992016-08-30 21:22:36 -0700916 if (self->handle == INVALID_HANDLE_VALUE)
917 return err_closed();
918 if (!self->readable)
919 return err_mode("reading");
920
921 if (size < 0)
922 return _io__WindowsConsoleIO_readall_impl(self);
923 if (size > BUFMAX) {
924 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
925 return NULL;
926 }
927
928 bytes = PyBytes_FromStringAndSize(NULL, size);
929 if (bytes == NULL)
930 return NULL;
931
932 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
933 if (bytes_size < 0) {
934 Py_CLEAR(bytes);
935 return NULL;
936 }
937
938 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
939 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
940 Py_CLEAR(bytes);
941 return NULL;
942 }
943 }
944
945 return bytes;
946}
947
948/*[clinic input]
949_io._WindowsConsoleIO.write
950 b: Py_buffer
951 /
952
953Write buffer b to file, return number of bytes written.
954
955Only makes one system call, so not all of the data may be written.
956The number of bytes actually written is returned.
957[clinic start generated code]*/
958
959static PyObject *
960_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
961/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
962{
963 BOOL res = TRUE;
964 wchar_t *wbuf;
965 DWORD len, wlen, n = 0;
966
967 if (self->handle == INVALID_HANDLE_VALUE)
968 return err_closed();
969 if (!self->writable)
970 return err_mode("writing");
971
Serhiy Storchaka42c35d92018-02-24 18:55:51 +0200972 if (!b->len) {
973 return PyLong_FromLong(0);
974 }
Steve Dower39294992016-08-30 21:22:36 -0700975 if (b->len > BUFMAX)
976 len = BUFMAX;
977 else
978 len = (DWORD)b->len;
979
980 Py_BEGIN_ALLOW_THREADS
981 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
982
983 /* issue11395 there is an unspecified upper bound on how many bytes
984 can be written at once. We cap at 32k - the caller will have to
985 handle partial writes.
986 Since we don't know how many input bytes are being ignored, we
987 have to reduce and recalculate. */
988 while (wlen > 32766 / sizeof(wchar_t)) {
989 len /= 2;
990 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
991 }
992 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700993
Steve Dower39294992016-08-30 21:22:36 -0700994 if (!wlen)
995 return PyErr_SetFromWindowsErr(0);
996
997 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
998
999 Py_BEGIN_ALLOW_THREADS
1000 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
1001 if (wlen) {
1002 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
Segev Finer523776c2017-06-02 19:26:01 +03001003 if (res && n < wlen) {
Steve Dower39294992016-08-30 21:22:36 -07001004 /* Wrote fewer characters than expected, which means our
1005 * len value may be wrong. So recalculate it from the
1006 * characters that were written. As this could potentially
1007 * result in a different value, we also validate that value.
1008 */
1009 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
1010 NULL, 0, NULL, NULL);
1011 if (len) {
1012 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
1013 NULL, 0);
1014 assert(wlen == len);
1015 }
1016 }
1017 } else
1018 res = 0;
1019 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001020
Steve Dower39294992016-08-30 21:22:36 -07001021 if (!res) {
1022 DWORD err = GetLastError();
1023 PyMem_Free(wbuf);
1024 return PyErr_SetFromWindowsErr(err);
1025 }
1026
1027 PyMem_Free(wbuf);
1028 return PyLong_FromSsize_t(len);
1029}
1030
1031static PyObject *
1032winconsoleio_repr(winconsoleio *self)
1033{
1034 if (self->handle == INVALID_HANDLE_VALUE)
1035 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1036
1037 if (self->readable)
1038 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1039 self->closehandle ? "True" : "False");
1040 if (self->writable)
1041 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1042 self->closehandle ? "True" : "False");
1043
1044 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1045 return NULL;
1046}
1047
1048/*[clinic input]
1049_io._WindowsConsoleIO.isatty
1050
1051Always True.
1052[clinic start generated code]*/
1053
1054static PyObject *
1055_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1056/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1057{
1058 if (self->handle == INVALID_HANDLE_VALUE)
1059 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001060
Steve Dower39294992016-08-30 21:22:36 -07001061 Py_RETURN_TRUE;
1062}
1063
Steve Dower39294992016-08-30 21:22:36 -07001064#include "clinic/winconsoleio.c.h"
1065
1066static PyMethodDef winconsoleio_methods[] = {
1067 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1068 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1069 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1070 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1071 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1072 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1073 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1074 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1075 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
Steve Dower39294992016-08-30 21:22:36 -07001076 {NULL, NULL} /* sentinel */
1077};
1078
1079/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1080
1081static PyObject *
1082get_closed(winconsoleio *self, void *closure)
1083{
1084 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1085}
1086
1087static PyObject *
1088get_closefd(winconsoleio *self, void *closure)
1089{
1090 return PyBool_FromLong((long)(self->closehandle));
1091}
1092
1093static PyObject *
1094get_mode(winconsoleio *self, void *closure)
1095{
1096 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1097}
1098
1099static PyGetSetDef winconsoleio_getsetlist[] = {
1100 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1101 {"closefd", (getter)get_closefd, NULL,
1102 "True if the file descriptor will be closed by close()."},
1103 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1104 {NULL},
1105};
1106
1107static PyMemberDef winconsoleio_members[] = {
1108 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1109 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1110 {NULL}
1111};
1112
1113PyTypeObject PyWindowsConsoleIO_Type = {
1114 PyVarObject_HEAD_INIT(NULL, 0)
1115 "_io._WindowsConsoleIO",
1116 sizeof(winconsoleio),
1117 0,
1118 (destructor)winconsoleio_dealloc, /* tp_dealloc */
1119 0, /* tp_print */
1120 0, /* tp_getattr */
1121 0, /* tp_setattr */
1122 0, /* tp_reserved */
1123 (reprfunc)winconsoleio_repr, /* tp_repr */
1124 0, /* tp_as_number */
1125 0, /* tp_as_sequence */
1126 0, /* tp_as_mapping */
1127 0, /* tp_hash */
1128 0, /* tp_call */
1129 0, /* tp_str */
1130 PyObject_GenericGetAttr, /* tp_getattro */
1131 0, /* tp_setattro */
1132 0, /* tp_as_buffer */
1133 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1134 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1135 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1136 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1137 (inquiry)winconsoleio_clear, /* tp_clear */
1138 0, /* tp_richcompare */
1139 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1140 0, /* tp_iter */
1141 0, /* tp_iternext */
1142 winconsoleio_methods, /* tp_methods */
1143 winconsoleio_members, /* tp_members */
1144 winconsoleio_getsetlist, /* tp_getset */
1145 0, /* tp_base */
1146 0, /* tp_dict */
1147 0, /* tp_descr_get */
1148 0, /* tp_descr_set */
1149 offsetof(winconsoleio, dict), /* tp_dictoffset */
1150 _io__WindowsConsoleIO___init__, /* tp_init */
1151 PyType_GenericAlloc, /* tp_alloc */
1152 winconsoleio_new, /* tp_new */
1153 PyObject_GC_Del, /* tp_free */
1154 0, /* tp_is_gc */
1155 0, /* tp_bases */
1156 0, /* tp_mro */
1157 0, /* tp_cache */
1158 0, /* tp_subclasses */
1159 0, /* tp_weaklist */
1160 0, /* tp_del */
1161 0, /* tp_version_tag */
1162 0, /* tp_finalize */
1163};
1164
Benjamin Petersone5024512018-09-12 12:06:42 -07001165PyObject * _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
Steve Dower312cef72016-10-03 09:04:58 -07001166
Steve Dower39294992016-08-30 21:22:36 -07001167#endif /* MS_WINDOWS */