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