blob: 1d169e2764ed27088f9e187620e065f9a4ae365c [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);
213 res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
214 &PyId_close, "O", self);
215 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;
Steve Dower722e3e22017-02-04 15:07:46 -0800321 if (console_type == '\0') {
322 PyErr_SetString(PyExc_ValueError,
323 "Cannot open non-console file");
324 return -1;
325 }
Steve Dower39294992016-08-30 21:22:36 -0700326
327 if (wcslen(name) != length) {
328 PyMem_Free(name);
329 PyErr_SetString(PyExc_ValueError, "embedded null character");
330 return -1;
331 }
332 }
333
334 s = mode;
335 while (*s) {
336 switch (*s++) {
337 case '+':
338 case 'a':
339 case 'b':
340 case 'x':
341 break;
342 case 'r':
343 if (rwa)
344 goto bad_mode;
345 rwa = 1;
346 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700347 if (console_type == 'x')
348 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700349 break;
350 case 'w':
351 if (rwa)
352 goto bad_mode;
353 rwa = 1;
354 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700355 if (console_type == 'x')
356 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700357 break;
358 default:
359 PyErr_Format(PyExc_ValueError,
360 "invalid mode: %.200s", mode);
361 goto error;
362 }
363 }
364
365 if (!rwa)
366 goto bad_mode;
367
368 if (fd >= 0) {
369 _Py_BEGIN_SUPPRESS_IPH
370 self->handle = (HANDLE)_get_osfhandle(fd);
371 _Py_END_SUPPRESS_IPH
372 self->closehandle = 0;
373 } else {
374 DWORD access = GENERIC_READ;
375
376 self->closehandle = 1;
377 if (!closefd) {
378 PyErr_SetString(PyExc_ValueError,
379 "Cannot use closefd=False with file name");
380 goto error;
381 }
382
383 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700384 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700385
386 Py_BEGIN_ALLOW_THREADS
387 /* Attempt to open for read/write initially, then fall back
388 on the specific access. This is required for modern names
389 CONIN$ and CONOUT$, which allow reading/writing state as
390 well as reading/writing content. */
391 self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
392 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
393 if (self->handle == INVALID_HANDLE_VALUE)
394 self->handle = CreateFileW(name, access,
395 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
396 Py_END_ALLOW_THREADS
397
398 if (self->handle == INVALID_HANDLE_VALUE) {
399 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
400 goto error;
401 }
402 }
403
Steve Dower27f26ad2016-09-17 13:51:23 -0700404 if (console_type == '\0')
405 console_type = _get_console_type(self->handle);
406
Steve Dower722e3e22017-02-04 15:07:46 -0800407 if (console_type == '\0') {
408 PyErr_SetString(PyExc_ValueError,
409 "Cannot open non-console file");
410 goto error;
411 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700412 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700413 PyErr_SetString(PyExc_ValueError,
414 "Cannot open console input buffer for writing");
415 goto error;
416 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700417 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700418 PyErr_SetString(PyExc_ValueError,
419 "Cannot open console output buffer for reading");
420 goto error;
421 }
422
423 self->blksize = DEFAULT_BUFFER_SIZE;
424 memset(self->buf, 0, 4);
425
426 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
427 goto error;
428
429 goto done;
430
431bad_mode:
432 PyErr_SetString(PyExc_ValueError,
433 "Must have exactly one of read or write mode");
434error:
435 ret = -1;
436 internal_close(self);
437
438done:
439 if (name)
440 PyMem_Free(name);
441 return ret;
442}
443
444static int
445winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
446{
447 Py_VISIT(self->dict);
448 return 0;
449}
450
451static int
452winconsoleio_clear(winconsoleio *self)
453{
454 Py_CLEAR(self->dict);
455 return 0;
456}
457
458static void
459winconsoleio_dealloc(winconsoleio *self)
460{
461 self->finalizing = 1;
462 if (_PyIOBase_finalize((PyObject *) self) < 0)
463 return;
464 _PyObject_GC_UNTRACK(self);
465 if (self->weakreflist != NULL)
466 PyObject_ClearWeakRefs((PyObject *) self);
467 Py_CLEAR(self->dict);
468 Py_TYPE(self)->tp_free((PyObject *)self);
469}
470
471static PyObject *
472err_closed(void)
473{
474 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
475 return NULL;
476}
477
478static PyObject *
479err_mode(const char *action)
480{
481 _PyIO_State *state = IO_STATE();
482 if (state != NULL)
483 PyErr_Format(state->unsupported_operation,
484 "Console buffer does not support %s", action);
485 return NULL;
486}
487
488/*[clinic input]
489_io._WindowsConsoleIO.fileno
490
491Return the underlying file descriptor (an integer).
492
493fileno is only set when a file descriptor is used to open
494one of the standard streams.
495
496[clinic start generated code]*/
497
498static PyObject *
499_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
500/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/
501{
502 if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) {
503 _Py_BEGIN_SUPPRESS_IPH
504 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700505 self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700506 else
Steve Dower27f26ad2016-09-17 13:51:23 -0700507 self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700508 _Py_END_SUPPRESS_IPH
509 }
510 if (self->fd < 0)
511 return err_mode("fileno");
512 return PyLong_FromLong(self->fd);
513}
514
515/*[clinic input]
516_io._WindowsConsoleIO.readable
517
518True if console is an input buffer.
519[clinic start generated code]*/
520
521static PyObject *
522_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
523/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
524{
525 if (self->handle == INVALID_HANDLE_VALUE)
526 return err_closed();
527 return PyBool_FromLong((long) self->readable);
528}
529
530/*[clinic input]
531_io._WindowsConsoleIO.writable
532
533True if console is an output buffer.
534[clinic start generated code]*/
535
536static PyObject *
537_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
538/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
539{
540 if (self->handle == INVALID_HANDLE_VALUE)
541 return err_closed();
542 return PyBool_FromLong((long) self->writable);
543}
544
545static DWORD
546_buflen(winconsoleio *self)
547{
Steve Dower312cef72016-10-03 09:04:58 -0700548 for (DWORD i = 0; i < SMALLBUF; ++i) {
Steve Dower39294992016-08-30 21:22:36 -0700549 if (!self->buf[i])
550 return i;
551 }
Steve Dower312cef72016-10-03 09:04:58 -0700552 return SMALLBUF;
Steve Dower39294992016-08-30 21:22:36 -0700553}
554
555static DWORD
556_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
557{
558 DWORD n = 0;
559
560 while (self->buf[0] && len--) {
Steve Dower312cef72016-10-03 09:04:58 -0700561 buf[n++] = self->buf[0];
562 for (int i = 1; i < SMALLBUF; ++i)
563 self->buf[i - 1] = self->buf[i];
564 self->buf[SMALLBUF - 1] = 0;
Steve Dower39294992016-08-30 21:22:36 -0700565 }
566
567 return n;
568}
569
570static wchar_t *
571read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
572 int err = 0, sig = 0;
573
574 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
575 if (!buf)
576 goto error;
Steve Dower312cef72016-10-03 09:04:58 -0700577
Steve Dower39294992016-08-30 21:22:36 -0700578 *readlen = 0;
579
Steve Dower312cef72016-10-03 09:04:58 -0700580 //DebugBreak();
Steve Dower39294992016-08-30 21:22:36 -0700581 Py_BEGIN_ALLOW_THREADS
Steve Dower312cef72016-10-03 09:04:58 -0700582 DWORD off = 0;
583 while (off < maxlen) {
Steve Dower39294992016-08-30 21:22:36 -0700584 DWORD n, len = min(maxlen - off, BUFSIZ);
585 SetLastError(0);
586 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
587
588 if (!res) {
589 err = GetLastError();
590 break;
591 }
592 if (n == 0) {
593 err = GetLastError();
594 if (err != ERROR_OPERATION_ABORTED)
595 break;
596 err = 0;
597 HANDLE hInterruptEvent = _PyOS_SigintEvent();
598 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
Steve Dower312cef72016-10-03 09:04:58 -0700599 == WAIT_OBJECT_0) {
Steve Dower39294992016-08-30 21:22:36 -0700600 ResetEvent(hInterruptEvent);
601 Py_BLOCK_THREADS
602 sig = PyErr_CheckSignals();
603 Py_UNBLOCK_THREADS
604 if (sig < 0)
605 break;
606 }
607 }
608 *readlen += n;
609
610 /* If we didn't read a full buffer that time, don't try
611 again or we will block a second time. */
612 if (n < len)
613 break;
614 /* If the buffer ended with a newline, break out */
615 if (buf[*readlen - 1] == '\n')
616 break;
Steve Dower312cef72016-10-03 09:04:58 -0700617 /* If the buffer ends with a high surrogate, expand the
618 buffer and read an extra character. */
619 WORD char_type;
620 if (off + BUFSIZ >= maxlen &&
621 GetStringTypeW(CT_CTYPE3, &buf[*readlen - 1], 1, &char_type) &&
622 char_type == C3_HIGHSURROGATE) {
623 wchar_t *newbuf;
624 maxlen += 1;
625 Py_BLOCK_THREADS
626 newbuf = (wchar_t*)PyMem_Realloc(buf, maxlen * sizeof(wchar_t));
627 Py_UNBLOCK_THREADS
628 if (!newbuf) {
629 sig = -1;
630 break;
631 }
632 buf = newbuf;
633 /* Only advance by n and not BUFSIZ in this case */
634 off += n;
635 continue;
636 }
637
638 off += BUFSIZ;
Steve Dower39294992016-08-30 21:22:36 -0700639 }
Steve Dower312cef72016-10-03 09:04:58 -0700640
Steve Dower39294992016-08-30 21:22:36 -0700641 Py_END_ALLOW_THREADS
642
643 if (sig)
644 goto error;
645 if (err) {
646 PyErr_SetFromWindowsErr(err);
647 goto error;
648 }
649
650 if (*readlen > 0 && buf[0] == L'\x1a') {
651 PyMem_Free(buf);
652 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
653 if (!buf)
654 goto error;
655 buf[0] = L'\0';
656 *readlen = 0;
657 }
658
659 return buf;
660
661error:
662 if (buf)
663 PyMem_Free(buf);
664 return NULL;
665}
666
667
668static Py_ssize_t
669readinto(winconsoleio *self, char *buf, Py_ssize_t len)
670{
671 if (self->handle == INVALID_HANDLE_VALUE) {
672 err_closed();
673 return -1;
674 }
675 if (!self->readable) {
676 err_mode("reading");
677 return -1;
678 }
679 if (len == 0)
680 return 0;
681 if (len > BUFMAX) {
682 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
683 return -1;
684 }
685
686 /* Each character may take up to 4 bytes in the final buffer.
687 This is highly conservative, but necessary to avoid
688 failure for any given Unicode input (e.g. \U0010ffff).
689 If the caller requests fewer than 4 bytes, we buffer one
690 character.
691 */
692 DWORD wlen = (DWORD)(len / 4);
693 if (wlen == 0) {
694 wlen = 1;
695 }
696
697 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
698 if (read_len) {
699 buf = &buf[read_len];
700 len -= read_len;
701 wlen -= 1;
702 }
703 if (len == read_len || wlen == 0)
704 return read_len;
705
706 DWORD n;
707 wchar_t *wbuf = read_console_w(self->handle, wlen, &n);
708 if (wbuf == NULL)
709 return -1;
710 if (n == 0) {
711 PyMem_Free(wbuf);
712 return read_len;
713 }
714
715 int err = 0;
716 DWORD u8n = 0;
717
718 Py_BEGIN_ALLOW_THREADS
719 if (len < 4) {
720 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
721 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
722 NULL, NULL))
723 u8n = _copyfrombuf(self, buf, (DWORD)len);
724 } else {
725 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
726 buf, (DWORD)len, NULL, NULL);
727 }
728
729 if (u8n) {
730 read_len += u8n;
731 u8n = 0;
732 } else {
733 err = GetLastError();
734 if (err == ERROR_INSUFFICIENT_BUFFER) {
735 /* Calculate the needed buffer for a more useful error, as this
736 means our "/ 4" logic above is insufficient for some input.
737 */
738 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
739 NULL, 0, NULL, NULL);
740 }
741 }
742 Py_END_ALLOW_THREADS
743
744 PyMem_Free(wbuf);
745
746 if (u8n) {
747 PyErr_Format(PyExc_SystemError,
748 "Buffer had room for %d bytes but %d bytes required",
749 len, u8n);
750 return -1;
751 }
752 if (err) {
753 PyErr_SetFromWindowsErr(err);
754 return -1;
755 }
756
757 return read_len;
758}
759
760/*[clinic input]
761_io._WindowsConsoleIO.readinto
762 buffer: Py_buffer(accept={rwbuffer})
763 /
764
765Same as RawIOBase.readinto().
766[clinic start generated code]*/
767
768static PyObject *
769_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
770/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
771{
772 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
773 if (len < 0)
774 return NULL;
775
776 return PyLong_FromSsize_t(len);
777}
778
779static DWORD
780new_buffersize(winconsoleio *self, DWORD currentsize)
781{
782 DWORD addend;
783
784 /* Expand the buffer by an amount proportional to the current size,
785 giving us amortized linear-time behavior. For bigger sizes, use a
786 less-than-double growth factor to avoid excessive allocation. */
787 if (currentsize > 65536)
788 addend = currentsize >> 3;
789 else
790 addend = 256 + currentsize;
791 if (addend < SMALLCHUNK)
792 /* Avoid tiny read() calls. */
793 addend = SMALLCHUNK;
794 return addend + currentsize;
795}
796
797/*[clinic input]
798_io._WindowsConsoleIO.readall
799
800Read all data from the console, returned as bytes.
801
802Return an empty bytes object at EOF.
803[clinic start generated code]*/
804
805static PyObject *
806_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
807/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
808{
809 wchar_t *buf;
810 DWORD bufsize, n, len = 0;
811 PyObject *bytes;
812 DWORD bytes_size, rn;
813
814 if (self->handle == INVALID_HANDLE_VALUE)
815 return err_closed();
816
817 bufsize = BUFSIZ;
818
819 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
820 if (buf == NULL)
821 return NULL;
822
823 while (1) {
824 wchar_t *subbuf;
825
826 if (len >= (Py_ssize_t)bufsize) {
827 DWORD newsize = new_buffersize(self, len);
828 if (newsize > BUFMAX)
829 break;
830 if (newsize < bufsize) {
831 PyErr_SetString(PyExc_OverflowError,
832 "unbounded read returned more bytes "
833 "than a Python bytes object can hold");
834 PyMem_Free(buf);
835 return NULL;
836 }
837 bufsize = newsize;
838
839 buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t));
840 if (!buf) {
841 PyMem_Free(buf);
842 return NULL;
843 }
844 }
845
846 subbuf = read_console_w(self->handle, bufsize - len, &n);
847
848 if (subbuf == NULL) {
849 PyMem_Free(buf);
850 return NULL;
851 }
852
853 if (n > 0)
854 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
855
856 PyMem_Free(subbuf);
857
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700858 /* when the read is empty we break */
859 if (n == 0)
Steve Dower39294992016-08-30 21:22:36 -0700860 break;
861
862 len += n;
863 }
864
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700865 if (len == 0 && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700866 /* when the result starts with ^Z we return an empty buffer */
867 PyMem_Free(buf);
868 return PyBytes_FromStringAndSize(NULL, 0);
869 }
870
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700871 if (len) {
872 Py_BEGIN_ALLOW_THREADS
873 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
874 NULL, 0, NULL, NULL);
875 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700876
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700877 if (!bytes_size) {
878 DWORD err = GetLastError();
879 PyMem_Free(buf);
880 return PyErr_SetFromWindowsErr(err);
881 }
882 } else {
883 bytes_size = 0;
Steve Dower39294992016-08-30 21:22:36 -0700884 }
885
886 bytes_size += _buflen(self);
887 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
888 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
889
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700890 if (len) {
891 Py_BEGIN_ALLOW_THREADS
892 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
893 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
894 Py_END_ALLOW_THREADS
Steve Dower39294992016-08-30 21:22:36 -0700895
Steve Dowerc6f9b2b2016-10-08 12:37:33 -0700896 if (!bytes_size) {
897 DWORD err = GetLastError();
898 PyMem_Free(buf);
899 Py_CLEAR(bytes);
900 return PyErr_SetFromWindowsErr(err);
901 }
902
903 /* add back the number of preserved bytes */
904 bytes_size += rn;
Steve Dower39294992016-08-30 21:22:36 -0700905 }
906
907 PyMem_Free(buf);
908 if (bytes_size < (size_t)PyBytes_GET_SIZE(bytes)) {
909 if (_PyBytes_Resize(&bytes, n * sizeof(wchar_t)) < 0) {
910 Py_CLEAR(bytes);
911 return NULL;
912 }
913 }
914 return bytes;
915}
916
917/*[clinic input]
918_io._WindowsConsoleIO.read
919 size: io_ssize_t = -1
920 /
921
922Read at most size bytes, returned as bytes.
923
924Only makes one system call when size is a positive integer,
925so less data may be returned than requested.
926Return an empty bytes object at EOF.
927[clinic start generated code]*/
928
929static PyObject *
930_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
931/*[clinic end generated code: output=57df68af9f4b22d0 input=6c56fceec460f1dd]*/
932{
933 PyObject *bytes;
934 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700935
Steve Dower39294992016-08-30 21:22:36 -0700936 if (self->handle == INVALID_HANDLE_VALUE)
937 return err_closed();
938 if (!self->readable)
939 return err_mode("reading");
940
941 if (size < 0)
942 return _io__WindowsConsoleIO_readall_impl(self);
943 if (size > BUFMAX) {
944 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
945 return NULL;
946 }
947
948 bytes = PyBytes_FromStringAndSize(NULL, size);
949 if (bytes == NULL)
950 return NULL;
951
952 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
953 if (bytes_size < 0) {
954 Py_CLEAR(bytes);
955 return NULL;
956 }
957
958 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
959 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
960 Py_CLEAR(bytes);
961 return NULL;
962 }
963 }
964
965 return bytes;
966}
967
968/*[clinic input]
969_io._WindowsConsoleIO.write
970 b: Py_buffer
971 /
972
973Write buffer b to file, return number of bytes written.
974
975Only makes one system call, so not all of the data may be written.
976The number of bytes actually written is returned.
977[clinic start generated code]*/
978
979static PyObject *
980_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
981/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
982{
983 BOOL res = TRUE;
984 wchar_t *wbuf;
985 DWORD len, wlen, n = 0;
986
987 if (self->handle == INVALID_HANDLE_VALUE)
988 return err_closed();
989 if (!self->writable)
990 return err_mode("writing");
991
992 if (b->len > BUFMAX)
993 len = BUFMAX;
994 else
995 len = (DWORD)b->len;
996
997 Py_BEGIN_ALLOW_THREADS
998 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
999
1000 /* issue11395 there is an unspecified upper bound on how many bytes
1001 can be written at once. We cap at 32k - the caller will have to
1002 handle partial writes.
1003 Since we don't know how many input bytes are being ignored, we
1004 have to reduce and recalculate. */
1005 while (wlen > 32766 / sizeof(wchar_t)) {
1006 len /= 2;
1007 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
1008 }
1009 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001010
Steve Dower39294992016-08-30 21:22:36 -07001011 if (!wlen)
1012 return PyErr_SetFromWindowsErr(0);
1013
1014 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
1015
1016 Py_BEGIN_ALLOW_THREADS
1017 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
1018 if (wlen) {
1019 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
1020 if (n < wlen) {
1021 /* Wrote fewer characters than expected, which means our
1022 * len value may be wrong. So recalculate it from the
1023 * characters that were written. As this could potentially
1024 * result in a different value, we also validate that value.
1025 */
1026 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
1027 NULL, 0, NULL, NULL);
1028 if (len) {
1029 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
1030 NULL, 0);
1031 assert(wlen == len);
1032 }
1033 }
1034 } else
1035 res = 0;
1036 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001037
Steve Dower39294992016-08-30 21:22:36 -07001038 if (!res) {
1039 DWORD err = GetLastError();
1040 PyMem_Free(wbuf);
1041 return PyErr_SetFromWindowsErr(err);
1042 }
1043
1044 PyMem_Free(wbuf);
1045 return PyLong_FromSsize_t(len);
1046}
1047
1048static PyObject *
1049winconsoleio_repr(winconsoleio *self)
1050{
1051 if (self->handle == INVALID_HANDLE_VALUE)
1052 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
1053
1054 if (self->readable)
1055 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
1056 self->closehandle ? "True" : "False");
1057 if (self->writable)
1058 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
1059 self->closehandle ? "True" : "False");
1060
1061 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
1062 return NULL;
1063}
1064
1065/*[clinic input]
1066_io._WindowsConsoleIO.isatty
1067
1068Always True.
1069[clinic start generated code]*/
1070
1071static PyObject *
1072_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
1073/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
1074{
1075 if (self->handle == INVALID_HANDLE_VALUE)
1076 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -07001077
Steve Dower39294992016-08-30 21:22:36 -07001078 Py_RETURN_TRUE;
1079}
1080
1081static PyObject *
1082winconsoleio_getstate(winconsoleio *self)
1083{
1084 PyErr_Format(PyExc_TypeError,
1085 "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
1086 return NULL;
1087}
1088
1089#include "clinic/winconsoleio.c.h"
1090
1091static PyMethodDef winconsoleio_methods[] = {
1092 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1093 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1094 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1095 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1096 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1097 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1098 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1099 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1100 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
1101 {"__getstate__", (PyCFunction)winconsoleio_getstate, METH_NOARGS, NULL},
1102 {NULL, NULL} /* sentinel */
1103};
1104
1105/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1106
1107static PyObject *
1108get_closed(winconsoleio *self, void *closure)
1109{
1110 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1111}
1112
1113static PyObject *
1114get_closefd(winconsoleio *self, void *closure)
1115{
1116 return PyBool_FromLong((long)(self->closehandle));
1117}
1118
1119static PyObject *
1120get_mode(winconsoleio *self, void *closure)
1121{
1122 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1123}
1124
1125static PyGetSetDef winconsoleio_getsetlist[] = {
1126 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1127 {"closefd", (getter)get_closefd, NULL,
1128 "True if the file descriptor will be closed by close()."},
1129 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1130 {NULL},
1131};
1132
1133static PyMemberDef winconsoleio_members[] = {
1134 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1135 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1136 {NULL}
1137};
1138
1139PyTypeObject PyWindowsConsoleIO_Type = {
1140 PyVarObject_HEAD_INIT(NULL, 0)
1141 "_io._WindowsConsoleIO",
1142 sizeof(winconsoleio),
1143 0,
1144 (destructor)winconsoleio_dealloc, /* tp_dealloc */
1145 0, /* tp_print */
1146 0, /* tp_getattr */
1147 0, /* tp_setattr */
1148 0, /* tp_reserved */
1149 (reprfunc)winconsoleio_repr, /* tp_repr */
1150 0, /* tp_as_number */
1151 0, /* tp_as_sequence */
1152 0, /* tp_as_mapping */
1153 0, /* tp_hash */
1154 0, /* tp_call */
1155 0, /* tp_str */
1156 PyObject_GenericGetAttr, /* tp_getattro */
1157 0, /* tp_setattro */
1158 0, /* tp_as_buffer */
1159 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1160 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1161 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1162 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1163 (inquiry)winconsoleio_clear, /* tp_clear */
1164 0, /* tp_richcompare */
1165 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1166 0, /* tp_iter */
1167 0, /* tp_iternext */
1168 winconsoleio_methods, /* tp_methods */
1169 winconsoleio_members, /* tp_members */
1170 winconsoleio_getsetlist, /* tp_getset */
1171 0, /* tp_base */
1172 0, /* tp_dict */
1173 0, /* tp_descr_get */
1174 0, /* tp_descr_set */
1175 offsetof(winconsoleio, dict), /* tp_dictoffset */
1176 _io__WindowsConsoleIO___init__, /* tp_init */
1177 PyType_GenericAlloc, /* tp_alloc */
1178 winconsoleio_new, /* tp_new */
1179 PyObject_GC_Del, /* tp_free */
1180 0, /* tp_is_gc */
1181 0, /* tp_bases */
1182 0, /* tp_mro */
1183 0, /* tp_cache */
1184 0, /* tp_subclasses */
1185 0, /* tp_weaklist */
1186 0, /* tp_del */
1187 0, /* tp_version_tag */
1188 0, /* tp_finalize */
1189};
1190
Steve Dower312cef72016-10-03 09:04:58 -07001191PyAPI_DATA(PyObject *) _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
1192
Steve Dower39294992016-08-30 21:22:36 -07001193#endif /* MS_WINDOWS */