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