blob: 4666a3867adb9d646d22834b16ff155486e9f43b [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))
34#error "unreasonable BUFSIZ > 64MB defined"
35#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) {
63 int fd;
64
65 fd = PyLong_AsLong(path_or_fd);
66 PyErr_Clear();
67 if (fd >= 0) {
68 HANDLE handle;
69 _Py_BEGIN_SUPPRESS_IPH
70 handle = (HANDLE)_get_osfhandle(fd);
71 _Py_END_SUPPRESS_IPH
72 if (!handle)
73 return '\0';
74 return _get_console_type(handle);
75 }
76
Steve Dower27f26ad2016-09-17 13:51:23 -070077 PyObject *decoded, *decoded_upper;
Steve Dower39294992016-08-30 21:22:36 -070078
79 int d = PyUnicode_FSDecoder(path_or_fd, &decoded);
80 if (!d) {
81 PyErr_Clear();
Steve Dower27f26ad2016-09-17 13:51:23 -070082 return '\0';
83 }
84 if (!PyUnicode_Check(decoded)) {
Steve Dower39294992016-08-30 21:22:36 -070085 Py_CLEAR(decoded);
86 return '\0';
87 }
Steve Dower27f26ad2016-09-17 13:51:23 -070088 decoded_upper = PyObject_CallMethod(decoded, "upper", "");
89 Py_CLEAR(decoded);
90 if (!decoded_upper) {
91 PyErr_Clear();
92 return '\0';
93 }
Steve Dower39294992016-08-30 21:22:36 -070094
95 char m = '\0';
Steve Dower27f26ad2016-09-17 13:51:23 -070096 if (PyUnicode_CompareWithASCIIString(decoded_upper, "CONIN$") == 0) {
Steve Dower39294992016-08-30 21:22:36 -070097 m = 'r';
Steve Dower27f26ad2016-09-17 13:51:23 -070098 } else if (PyUnicode_CompareWithASCIIString(decoded_upper, "CONOUT$") == 0) {
Steve Dower39294992016-08-30 21:22:36 -070099 m = 'w';
Steve Dower27f26ad2016-09-17 13:51:23 -0700100 } else if (PyUnicode_CompareWithASCIIString(decoded_upper, "CON") == 0) {
101 m = 'x';
Steve Dower39294992016-08-30 21:22:36 -0700102 }
103
Steve Dower27f26ad2016-09-17 13:51:23 -0700104 Py_CLEAR(decoded_upper);
Steve Dower39294992016-08-30 21:22:36 -0700105 return m;
106}
107
108/*[clinic input]
109module _io
110class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
111[clinic start generated code]*/
112/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e897fdc1fba4e131]*/
113
114/*[python input]
115class io_ssize_t_converter(CConverter):
116 type = 'Py_ssize_t'
117 converter = '_PyIO_ConvertSsize_t'
118[python start generated code]*/
119/*[python end generated code: output=da39a3ee5e6b4b0d input=d0a811d3cbfd1b33]*/
120
121typedef struct {
122 PyObject_HEAD
123 HANDLE handle;
124 int fd;
125 unsigned int created : 1;
126 unsigned int readable : 1;
127 unsigned int writable : 1;
128 unsigned int closehandle : 1;
129 char finalizing;
130 unsigned int blksize;
131 PyObject *weakreflist;
132 PyObject *dict;
Steve Dower312cef72016-10-03 09:04:58 -0700133 char buf[SMALLBUF];
134 wchar_t wbuf;
Steve Dower39294992016-08-30 21:22:36 -0700135} winconsoleio;
136
137PyTypeObject PyWindowsConsoleIO_Type;
138
139_Py_IDENTIFIER(name);
140
141int
142_PyWindowsConsoleIO_closed(PyObject *self)
143{
144 return ((winconsoleio *)self)->handle == INVALID_HANDLE_VALUE;
145}
146
147
148/* Returns 0 on success, -1 with exception set on failure. */
149static int
150internal_close(winconsoleio *self)
151{
152 if (self->handle != INVALID_HANDLE_VALUE) {
153 if (self->closehandle) {
154 if (self->fd >= 0) {
155 _Py_BEGIN_SUPPRESS_IPH
156 close(self->fd);
157 _Py_END_SUPPRESS_IPH
158 }
159 CloseHandle(self->handle);
160 }
161 self->handle = INVALID_HANDLE_VALUE;
162 self->fd = -1;
163 }
164 return 0;
165}
166
167/*[clinic input]
168_io._WindowsConsoleIO.close
169
170Close the handle.
171
172A closed handle cannot be used for further I/O operations. close() may be
173called more than once without error.
174[clinic start generated code]*/
175
176static PyObject *
177_io__WindowsConsoleIO_close_impl(winconsoleio *self)
178/*[clinic end generated code: output=27ef95b66c29057b input=185617e349ae4c7b]*/
179{
180 PyObject *res;
181 PyObject *exc, *val, *tb;
182 int rc;
183 _Py_IDENTIFIER(close);
184 res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
185 &PyId_close, "O", self);
186 if (!self->closehandle) {
187 self->handle = INVALID_HANDLE_VALUE;
188 return res;
189 }
190 if (res == NULL)
191 PyErr_Fetch(&exc, &val, &tb);
192 rc = internal_close(self);
193 if (res == NULL)
194 _PyErr_ChainExceptions(exc, val, tb);
195 if (rc < 0)
196 Py_CLEAR(res);
197 return res;
198}
199
200static PyObject *
201winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
202{
203 winconsoleio *self;
204
205 assert(type != NULL && type->tp_alloc != NULL);
206
207 self = (winconsoleio *) type->tp_alloc(type, 0);
208 if (self != NULL) {
209 self->handle = INVALID_HANDLE_VALUE;
210 self->fd = -1;
211 self->created = 0;
212 self->readable = 0;
213 self->writable = 0;
214 self->closehandle = 0;
215 self->blksize = 0;
216 self->weakreflist = NULL;
217 }
218
219 return (PyObject *) self;
220}
221
222/*[clinic input]
223_io._WindowsConsoleIO.__init__
224 file as nameobj: object
225 mode: str = "r"
226 closefd: int(c_default="1") = True
227 opener: object = None
228
229Open a console buffer by file descriptor.
230
231The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All
232other mode characters will be ignored. Mode 'b' will be assumed if it is
233omitted. The *opener* parameter is always ignored.
234[clinic start generated code]*/
235
236static int
237_io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
238 const char *mode, int closefd,
239 PyObject *opener)
240/*[clinic end generated code: output=3fd9cbcdd8d95429 input=61be39633a86f5d7]*/
241{
242 const char *s;
243 wchar_t *name = NULL;
Steve Dower27f26ad2016-09-17 13:51:23 -0700244 char console_type = '\0';
Steve Dower39294992016-08-30 21:22:36 -0700245 int ret = 0;
246 int rwa = 0;
247 int fd = -1;
248 int fd_is_own = 0;
249
250 assert(PyWindowsConsoleIO_Check(self));
251 if (self->handle >= 0) {
252 if (self->closehandle) {
253 /* Have to close the existing file first. */
254 if (internal_close(self) < 0)
255 return -1;
256 }
257 else
258 self->handle = INVALID_HANDLE_VALUE;
259 }
260
261 if (PyFloat_Check(nameobj)) {
262 PyErr_SetString(PyExc_TypeError,
263 "integer argument expected, got float");
264 return -1;
265 }
266
267 fd = _PyLong_AsInt(nameobj);
268 if (fd < 0) {
269 if (!PyErr_Occurred()) {
270 PyErr_SetString(PyExc_ValueError,
271 "negative file descriptor");
272 return -1;
273 }
274 PyErr_Clear();
275 }
276 self->fd = fd;
277
278 if (fd < 0) {
279 PyObject *decodedname = Py_None;
280 Py_INCREF(decodedname);
281
282 int d = PyUnicode_FSDecoder(nameobj, (void*)&decodedname);
283 if (!d)
284 return -1;
285
286 Py_ssize_t length;
287 name = PyUnicode_AsWideCharString(decodedname, &length);
Steve Dower27f26ad2016-09-17 13:51:23 -0700288 console_type = _PyIO_get_console_type(decodedname);
Steve Dower39294992016-08-30 21:22:36 -0700289 Py_CLEAR(decodedname);
290 if (name == NULL)
291 return -1;
292
293 if (wcslen(name) != length) {
294 PyMem_Free(name);
295 PyErr_SetString(PyExc_ValueError, "embedded null character");
296 return -1;
297 }
298 }
299
300 s = mode;
301 while (*s) {
302 switch (*s++) {
303 case '+':
304 case 'a':
305 case 'b':
306 case 'x':
307 break;
308 case 'r':
309 if (rwa)
310 goto bad_mode;
311 rwa = 1;
312 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700313 if (console_type == 'x')
314 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700315 break;
316 case 'w':
317 if (rwa)
318 goto bad_mode;
319 rwa = 1;
320 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700321 if (console_type == 'x')
322 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700323 break;
324 default:
325 PyErr_Format(PyExc_ValueError,
326 "invalid mode: %.200s", mode);
327 goto error;
328 }
329 }
330
331 if (!rwa)
332 goto bad_mode;
333
334 if (fd >= 0) {
335 _Py_BEGIN_SUPPRESS_IPH
336 self->handle = (HANDLE)_get_osfhandle(fd);
337 _Py_END_SUPPRESS_IPH
338 self->closehandle = 0;
339 } else {
340 DWORD access = GENERIC_READ;
341
342 self->closehandle = 1;
343 if (!closefd) {
344 PyErr_SetString(PyExc_ValueError,
345 "Cannot use closefd=False with file name");
346 goto error;
347 }
348
349 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700350 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700351
352 Py_BEGIN_ALLOW_THREADS
353 /* Attempt to open for read/write initially, then fall back
354 on the specific access. This is required for modern names
355 CONIN$ and CONOUT$, which allow reading/writing state as
356 well as reading/writing content. */
357 self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
358 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
359 if (self->handle == INVALID_HANDLE_VALUE)
360 self->handle = CreateFileW(name, access,
361 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
362 Py_END_ALLOW_THREADS
363
364 if (self->handle == INVALID_HANDLE_VALUE) {
365 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
366 goto error;
367 }
368 }
369
Steve Dower27f26ad2016-09-17 13:51:23 -0700370 if (console_type == '\0')
371 console_type = _get_console_type(self->handle);
372
373 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700374 PyErr_SetString(PyExc_ValueError,
375 "Cannot open console input buffer for writing");
376 goto error;
377 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700378 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700379 PyErr_SetString(PyExc_ValueError,
380 "Cannot open console output buffer for reading");
381 goto error;
382 }
383
384 self->blksize = DEFAULT_BUFFER_SIZE;
385 memset(self->buf, 0, 4);
386
387 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
388 goto error;
389
390 goto done;
391
392bad_mode:
393 PyErr_SetString(PyExc_ValueError,
394 "Must have exactly one of read or write mode");
395error:
396 ret = -1;
397 internal_close(self);
398
399done:
400 if (name)
401 PyMem_Free(name);
402 return ret;
403}
404
405static int
406winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
407{
408 Py_VISIT(self->dict);
409 return 0;
410}
411
412static int
413winconsoleio_clear(winconsoleio *self)
414{
415 Py_CLEAR(self->dict);
416 return 0;
417}
418
419static void
420winconsoleio_dealloc(winconsoleio *self)
421{
422 self->finalizing = 1;
423 if (_PyIOBase_finalize((PyObject *) self) < 0)
424 return;
425 _PyObject_GC_UNTRACK(self);
426 if (self->weakreflist != NULL)
427 PyObject_ClearWeakRefs((PyObject *) self);
428 Py_CLEAR(self->dict);
429 Py_TYPE(self)->tp_free((PyObject *)self);
430}
431
432static PyObject *
433err_closed(void)
434{
435 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
436 return NULL;
437}
438
439static PyObject *
440err_mode(const char *action)
441{
442 _PyIO_State *state = IO_STATE();
443 if (state != NULL)
444 PyErr_Format(state->unsupported_operation,
445 "Console buffer does not support %s", action);
446 return NULL;
447}
448
449/*[clinic input]
450_io._WindowsConsoleIO.fileno
451
452Return the underlying file descriptor (an integer).
453
454fileno is only set when a file descriptor is used to open
455one of the standard streams.
456
457[clinic start generated code]*/
458
459static PyObject *
460_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
461/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/
462{
463 if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) {
464 _Py_BEGIN_SUPPRESS_IPH
465 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700466 self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700467 else
Steve Dower27f26ad2016-09-17 13:51:23 -0700468 self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700469 _Py_END_SUPPRESS_IPH
470 }
471 if (self->fd < 0)
472 return err_mode("fileno");
473 return PyLong_FromLong(self->fd);
474}
475
476/*[clinic input]
477_io._WindowsConsoleIO.readable
478
479True if console is an input buffer.
480[clinic start generated code]*/
481
482static PyObject *
483_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
484/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
485{
486 if (self->handle == INVALID_HANDLE_VALUE)
487 return err_closed();
488 return PyBool_FromLong((long) self->readable);
489}
490
491/*[clinic input]
492_io._WindowsConsoleIO.writable
493
494True if console is an output buffer.
495[clinic start generated code]*/
496
497static PyObject *
498_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
499/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
500{
501 if (self->handle == INVALID_HANDLE_VALUE)
502 return err_closed();
503 return PyBool_FromLong((long) self->writable);
504}
505
506static DWORD
507_buflen(winconsoleio *self)
508{
Steve Dower312cef72016-10-03 09:04:58 -0700509 for (DWORD i = 0; i < SMALLBUF; ++i) {
Steve Dower39294992016-08-30 21:22:36 -0700510 if (!self->buf[i])
511 return i;
512 }
Steve Dower312cef72016-10-03 09:04:58 -0700513 return SMALLBUF;
Steve Dower39294992016-08-30 21:22:36 -0700514}
515
516static DWORD
517_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
518{
519 DWORD n = 0;
520
521 while (self->buf[0] && len--) {
Steve Dower312cef72016-10-03 09:04:58 -0700522 buf[n++] = self->buf[0];
523 for (int i = 1; i < SMALLBUF; ++i)
524 self->buf[i - 1] = self->buf[i];
525 self->buf[SMALLBUF - 1] = 0;
Steve Dower39294992016-08-30 21:22:36 -0700526 }
527
528 return n;
529}
530
531static wchar_t *
532read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
533 int err = 0, sig = 0;
534
535 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
536 if (!buf)
537 goto error;
Steve Dower312cef72016-10-03 09:04:58 -0700538
Steve Dower39294992016-08-30 21:22:36 -0700539 *readlen = 0;
540
Steve Dower312cef72016-10-03 09:04:58 -0700541 //DebugBreak();
Steve Dower39294992016-08-30 21:22:36 -0700542 Py_BEGIN_ALLOW_THREADS
Steve Dower312cef72016-10-03 09:04:58 -0700543 DWORD off = 0;
544 while (off < maxlen) {
Steve Dower39294992016-08-30 21:22:36 -0700545 DWORD n, len = min(maxlen - off, BUFSIZ);
546 SetLastError(0);
547 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
548
549 if (!res) {
550 err = GetLastError();
551 break;
552 }
553 if (n == 0) {
554 err = GetLastError();
555 if (err != ERROR_OPERATION_ABORTED)
556 break;
557 err = 0;
558 HANDLE hInterruptEvent = _PyOS_SigintEvent();
559 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
Steve Dower312cef72016-10-03 09:04:58 -0700560 == WAIT_OBJECT_0) {
Steve Dower39294992016-08-30 21:22:36 -0700561 ResetEvent(hInterruptEvent);
562 Py_BLOCK_THREADS
563 sig = PyErr_CheckSignals();
564 Py_UNBLOCK_THREADS
565 if (sig < 0)
566 break;
567 }
568 }
569 *readlen += n;
570
571 /* If we didn't read a full buffer that time, don't try
572 again or we will block a second time. */
573 if (n < len)
574 break;
575 /* If the buffer ended with a newline, break out */
576 if (buf[*readlen - 1] == '\n')
577 break;
Steve Dower312cef72016-10-03 09:04:58 -0700578 /* If the buffer ends with a high surrogate, expand the
579 buffer and read an extra character. */
580 WORD char_type;
581 if (off + BUFSIZ >= maxlen &&
582 GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) &&
583 char_type == C3_HIGHSURROGATE) {
584 wchar_t *newbuf;
585 maxlen += 1;
586 Py_BLOCK_THREADS
587 newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t));
588 Py_UNBLOCK_THREADS
589 if (!newbuf) {
590 sig = -1;
591 break;
592 }
593 buf = newbuf;
594 /* Only advance by n and not BUFSIZ in this case */
595 off += n;
596 continue;
597 }
598
599 off += BUFSIZ;
Steve Dower39294992016-08-30 21:22:36 -0700600 }
Steve Dower312cef72016-10-03 09:04:58 -0700601
Steve Dower39294992016-08-30 21:22:36 -0700602 Py_END_ALLOW_THREADS
603
604 if (sig)
605 goto error;
606 if (err) {
607 PyErr_SetFromWindowsErr(err);
608 goto error;
609 }
610
611 if (*readlen > 0 && buf[0] == L'\x1a') {
612 PyMem_Free(buf);
613 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
614 if (!buf)
615 goto error;
616 buf[0] = L'\0';
617 *readlen = 0;
618 }
619
620 return buf;
621
622error:
623 if (buf)
624 PyMem_Free(buf);
625 return NULL;
626}
627
628
629static Py_ssize_t
630readinto(winconsoleio *self, char *buf, Py_ssize_t len)
631{
632 if (self->handle == INVALID_HANDLE_VALUE) {
633 err_closed();
634 return -1;
635 }
636 if (!self->readable) {
637 err_mode("reading");
638 return -1;
639 }
640 if (len == 0)
641 return 0;
642 if (len > BUFMAX) {
643 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
644 return -1;
645 }
646
647 /* Each character may take up to 4 bytes in the final buffer.
648 This is highly conservative, but necessary to avoid
649 failure for any given Unicode input (e.g. \U0010ffff).
650 If the caller requests fewer than 4 bytes, we buffer one
651 character.
652 */
653 DWORD wlen = (DWORD)(len / 4);
654 if (wlen == 0) {
655 wlen = 1;
656 }
657
658 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
659 if (read_len) {
660 buf = &buf[read_len];
661 len -= read_len;
662 wlen -= 1;
663 }
664 if (len == read_len || wlen == 0)
665 return read_len;
666
667 DWORD n;
668 wchar_t *wbuf = read_console_w(self->handle, wlen, &n);
669 if (wbuf == NULL)
670 return -1;
671 if (n == 0) {
672 PyMem_Free(wbuf);
673 return read_len;
674 }
675
676 int err = 0;
677 DWORD u8n = 0;
678
679 Py_BEGIN_ALLOW_THREADS
680 if (len < 4) {
681 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
682 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
683 NULL, NULL))
684 u8n = _copyfrombuf(self, buf, (DWORD)len);
685 } else {
686 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
687 buf, (DWORD)len, NULL, NULL);
688 }
689
690 if (u8n) {
691 read_len += u8n;
692 u8n = 0;
693 } else {
694 err = GetLastError();
695 if (err == ERROR_INSUFFICIENT_BUFFER) {
696 /* Calculate the needed buffer for a more useful error, as this
697 means our "/ 4" logic above is insufficient for some input.
698 */
699 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
700 NULL, 0, NULL, NULL);
701 }
702 }
703 Py_END_ALLOW_THREADS
704
705 PyMem_Free(wbuf);
706
707 if (u8n) {
708 PyErr_Format(PyExc_SystemError,
709 "Buffer had room for %d bytes but %d bytes required",
710 len, u8n);
711 return -1;
712 }
713 if (err) {
714 PyErr_SetFromWindowsErr(err);
715 return -1;
716 }
717
718 return read_len;
719}
720
721/*[clinic input]
722_io._WindowsConsoleIO.readinto
723 buffer: Py_buffer(accept={rwbuffer})
724 /
725
726Same as RawIOBase.readinto().
727[clinic start generated code]*/
728
729static PyObject *
730_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
731/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
732{
733 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
734 if (len < 0)
735 return NULL;
736
737 return PyLong_FromSsize_t(len);
738}
739
740static DWORD
741new_buffersize(winconsoleio *self, DWORD currentsize)
742{
743 DWORD addend;
744
745 /* Expand the buffer by an amount proportional to the current size,
746 giving us amortized linear-time behavior. For bigger sizes, use a
747 less-than-double growth factor to avoid excessive allocation. */
748 if (currentsize > 65536)
749 addend = currentsize >> 3;
750 else
751 addend = 256 + currentsize;
752 if (addend < SMALLCHUNK)
753 /* Avoid tiny read() calls. */
754 addend = SMALLCHUNK;
755 return addend + currentsize;
756}
757
758/*[clinic input]
759_io._WindowsConsoleIO.readall
760
761Read all data from the console, returned as bytes.
762
763Return an empty bytes object at EOF.
764[clinic start generated code]*/
765
766static PyObject *
767_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
768/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
769{
770 wchar_t *buf;
771 DWORD bufsize, n, len = 0;
772 PyObject *bytes;
773 DWORD bytes_size, rn;
774
775 if (self->handle == INVALID_HANDLE_VALUE)
776 return err_closed();
777
778 bufsize = BUFSIZ;
779
780 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
781 if (buf == NULL)
782 return NULL;
783
784 while (1) {
785 wchar_t *subbuf;
786
787 if (len >= (Py_ssize_t)bufsize) {
788 DWORD newsize = new_buffersize(self, len);
789 if (newsize > BUFMAX)
790 break;
791 if (newsize < bufsize) {
792 PyErr_SetString(PyExc_OverflowError,
793 "unbounded read returned more bytes "
794 "than a Python bytes object can hold");
795 PyMem_Free(buf);
796 return NULL;
797 }
798 bufsize = newsize;
799
800 buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t));
801 if (!buf) {
802 PyMem_Free(buf);
803 return NULL;
804 }
805 }
806
807 subbuf = read_console_w(self->handle, bufsize - len, &n);
808
809 if (subbuf == NULL) {
810 PyMem_Free(buf);
811 return NULL;
812 }
813
814 if (n > 0)
815 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
816
817 PyMem_Free(subbuf);
818
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700819 /* when the read is empty we break */
820 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700821 break;
822
823 len += n;
824 }
825
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700826 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700827 /* when the result starts with ^Z we return an empty buffer */
828 PyMem_Free(buf);
829 return PyBytes_FromStringAndSize(NULL, 0);
830 }
831
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700832 if (len) {
833 Py_BEGIN_ALLOW_THREADS
834 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
835 NULL, 0, NULL, NULL);
836 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700837
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700838 if (!bytes_size) {
839 DWORD err = GetLastError();
840 PyMem_Free(buf);
841 return PyErr_SetFromWindowsErr(err);
842 }
843 } else {
844 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700845 }
846
847 bytes_size += _buflen(self);
848 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
849 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
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 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
855 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700856
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700857 if (!bytes_size) {
858 DWORD err = GetLastError();
859 PyMem_Free(buf);
860 Py_CLEAR(bytes);
861 return PyErr_SetFromWindowsErr(err);
862 }
863
864 /* add back the number of preserved bytes */
865 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700866 }
867
868 PyMem_Free(buf);
869 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
870 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
871 Py_CLEAR(bytes);
872 return NULL;
873 }
874 }
875 return bytes;
876}
877
878/*[clinic input]
879_io._WindowsConsoleIO.read
880 size: io_ssize_t = -1
881 /
882
883Read at most size bytes, returned as bytes.
884
885Only makes one system call when size is a positive integer,
886so less data may be returned than requested.
887Return an empty bytes object at EOF.
888[clinic start generated code]*/
889
890static PyObject *
891_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
892/*[clinic end generated code: output=57df68af9f4b22d0 input=6c56fceec460f1dd]*/
893{
894 PyObject *bytes;
895 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700896
Steve Dower39294992016-08-30 21:22:36 -0700897 if (self->handle == INVALID_HANDLE_VALUE)
898 return err_closed();
899 if (!self->readable)
900 return err_mode("reading");
901
902 if (size < 0)
903 return _io__WindowsConsoleIO_readall_impl(self);
904 if (size > BUFMAX) {
905 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
906 return NULL;
907 }
908
909 bytes = PyBytes_FromStringAndSize(NULL, size);
910 if (bytes == NULL)
911 return NULL;
912
913 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
914 if (bytes_size < 0) {
915 Py_CLEAR(bytes);
916 return NULL;
917 }
918
919 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
920 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
921 Py_CLEAR(bytes);
922 return NULL;
923 }
924 }
925
926 return bytes;
927}
928
929/*[clinic input]
930_io._WindowsConsoleIO.write
931 b: Py_buffer
932 /
933
934Write buffer b to file, return number of bytes written.
935
936Only makes one system call, so not all of the data may be written.
937The number of bytes actually written is returned.
938[clinic start generated code]*/
939
940static PyObject *
941_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
942/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
943{
944 BOOL res = TRUE;
945 wchar_t *wbuf;
946 DWORD len, wlen, n = 0;
947
948 if (self->handle == INVALID_HANDLE_VALUE)
949 return err_closed();
950 if (!self->writable)
951 return err_mode("writing");
952
953 if (b->len > BUFMAX)
954 len = BUFMAX;
955 else
956 len = (DWORD)b->len;
957
958 Py_BEGIN_ALLOW_THREADS
959 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
960
961 /* issue11395 there is an unspecified upper bound on how many bytes
962 can be written at once. We cap at 32k - the caller will have to
963 handle partial writes.
964 Since we don't know how many input bytes are being ignored, we
965 have to reduce and recalculate. */
966 while (wlen > 32766 / sizeof(wchar_t)) {
967 len /= 2;
968 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
969 }
970 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700971
Steve Dower39294992016-08-30 21:22:36 -0700972 if (!wlen)
973 return PyErr_SetFromWindowsErr(0);
974
975 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
976
977 Py_BEGIN_ALLOW_THREADS
978 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
979 if (wlen) {
980 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
981 if (n < wlen) {
982 /* Wrote fewer characters than expected, which means our
983 * len value may be wrong. So recalculate it from the
984 * characters that were written. As this could potentially
985 * result in a different value, we also validate that value.
986 */
987 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
988 NULL, 0, NULL, NULL);
989 if (len) {
990 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
991 NULL, 0);
992 assert(wlen == len);
993 }
994 }
995 } else
996 res = 0;
997 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700998
Steve Dower39294992016-08-30 21:22:36 -0700999 if (!res) {
1000 DWORD err = GetLastError();
1001 PyMem_Free(wbuf);
1002 return PyErr_SetFromWindowsErr(err);
1003 }
1004
1005 PyMem_Free(wbuf);
1006 return PyLong_FromSsize_t(len);
1007}
1008
1009static PyObject *
1010winconsoleio_repr(winconsoleio *self)
1011{
1012 if (self->handle == INVALID_HANDLE_VALUE)
1013 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1014
1015 if (self->readable)
1016 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1017 self->closehandle ? "True" : "False");
1018 if (self->writable)
1019 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1020 self->closehandle ? "True" : "False");
1021
1022 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1023 return NULL;
1024}
1025
1026/*[clinic input]
1027_io._WindowsConsoleIO.isatty
1028
1029Always True.
1030[clinic start generated code]*/
1031
1032static PyObject *
1033_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1034/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1035{
1036 if (self->handle == INVALID_HANDLE_VALUE)
1037 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001038
Steve Dower39294992016-08-30 21:22:36 -07001039 Py_RETURN_TRUE;
1040}
1041
1042static PyObject *
1043winconsoleio_getstate(winconsoleio *self)
1044{
1045 PyErr_Format(PyExc_TypeError,
1046 "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
1047 return NULL;
1048}
1049
1050#include "clinic/winconsoleio.c.h"
1051
1052static PyMethodDef winconsoleio_methods[] = {
1053 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1054 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1055 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1056 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1057 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1058 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1059 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1060 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1061 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
1062 {"__getstate__", (PyCFunction)winconsoleio_getstate, METH_NOARGS, NULL},
1063 {NULL, NULL} /* sentinel */
1064};
1065
1066/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1067
1068static PyObject *
1069get_closed(winconsoleio *self, void *closure)
1070{
1071 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1072}
1073
1074static PyObject *
1075get_closefd(winconsoleio *self, void *closure)
1076{
1077 return PyBool_FromLong((long)(self->closehandle));
1078}
1079
1080static PyObject *
1081get_mode(winconsoleio *self, void *closure)
1082{
1083 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1084}
1085
1086static PyGetSetDef winconsoleio_getsetlist[] = {
1087 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1088 {"closefd", (getter)get_closefd, NULL,
1089 "True if the file descriptor will be closed by close()."},
1090 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1091 {NULL},
1092};
1093
1094static PyMemberDef winconsoleio_members[] = {
1095 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1096 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1097 {NULL}
1098};
1099
1100PyTypeObject PyWindowsConsoleIO_Type = {
1101 PyVarObject_HEAD_INIT(NULL, 0)
1102 "_io._WindowsConsoleIO",
1103 sizeof(winconsoleio),
1104 0,
1105 (destructor)winconsoleio_dealloc, /* tp_dealloc */
1106 0, /* tp_print */
1107 0, /* tp_getattr */
1108 0, /* tp_setattr */
1109 0, /* tp_reserved */
1110 (reprfunc)winconsoleio_repr, /* tp_repr */
1111 0, /* tp_as_number */
1112 0, /* tp_as_sequence */
1113 0, /* tp_as_mapping */
1114 0, /* tp_hash */
1115 0, /* tp_call */
1116 0, /* tp_str */
1117 PyObject_GenericGetAttr, /* tp_getattro */
1118 0, /* tp_setattro */
1119 0, /* tp_as_buffer */
1120 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1121 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1122 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1123 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1124 (inquiry)winconsoleio_clear, /* tp_clear */
1125 0, /* tp_richcompare */
1126 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1127 0, /* tp_iter */
1128 0, /* tp_iternext */
1129 winconsoleio_methods, /* tp_methods */
1130 winconsoleio_members, /* tp_members */
1131 winconsoleio_getsetlist, /* tp_getset */
1132 0, /* tp_base */
1133 0, /* tp_dict */
1134 0, /* tp_descr_get */
1135 0, /* tp_descr_set */
1136 offsetof(winconsoleio, dict), /* tp_dictoffset */
1137 _io__WindowsConsoleIO___init__, /* tp_init */
1138 PyType_GenericAlloc, /* tp_alloc */
1139 winconsoleio_new, /* tp_new */
1140 PyObject_GC_Del, /* tp_free */
1141 0, /* tp_is_gc */
1142 0, /* tp_bases */
1143 0, /* tp_mro */
1144 0, /* tp_cache */
1145 0, /* tp_subclasses */
1146 0, /* tp_weaklist */
1147 0, /* tp_del */
1148 0, /* tp_version_tag */
1149 0, /* tp_finalize */
1150};
1151
Steve Dower312cef72016-10-03 09:04:58 -07001152PyAPI_DATA(PyObject *) _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
1153
Steve Dower39294992016-08-30 21:22:36 -07001154#endif /* MS_WINDOWS */