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