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 > 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 
     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 /*[python input]
    144 class 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 
    150 typedef 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;
    162     char buf[SMALLBUF];
    163     wchar_t wbuf;
    164 } winconsoleio;
    165 
    166 PyTypeObject PyWindowsConsoleIO_Type;
    167 
    168 _Py_IDENTIFIER(name);
    169 
    170 int
    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. */
    178 static int
    179 internal_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 
    199 Close the handle.
    200 
    201 A closed handle cannot be used for further I/O operations.  close() may be
    202 called more than once without error.
    203 [clinic start generated code]*/
    204 
    205 static 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 
    229 static PyObject *
    230 winconsoleio_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 
    258 Open a console buffer by file descriptor.
    259 
    260 The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All
    261 other mode characters will be ignored. Mode 'b' will be assumed if it is
    262 omitted. The *opener* parameter is always ignored.
    263 [clinic start generated code]*/
    264 
    265 static 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;
    273     char console_type = '\0';
    274     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);
    317         console_type = _PyIO_get_console_type(decodedname);
    318         Py_CLEAR(decodedname);
    319         if (name == NULL)
    320             return -1;
    321         if (console_type == '\0') {
    322             PyErr_SetString(PyExc_ValueError,
    323                 "Cannot open non-console file");
    324             return -1;
    325         }
    326 
    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;
    347             if (console_type == 'x')
    348                 console_type = 'r';
    349             break;
    350         case 'w':
    351             if (rwa)
    352                 goto bad_mode;
    353             rwa = 1;
    354             self->writable = 1;
    355             if (console_type == 'x')
    356                 console_type = 'w';
    357             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)
    384             access = GENERIC_WRITE;
    385 
    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 
    404     if (console_type == '\0')
    405         console_type = _get_console_type(self->handle);
    406 
    407     if (console_type == '\0') {
    408         PyErr_SetString(PyExc_ValueError,
    409             "Cannot open non-console file");
    410         goto error;
    411     }
    412     if (self->writable && console_type != 'w') {
    413         PyErr_SetString(PyExc_ValueError,
    414             "Cannot open console input buffer for writing");
    415         goto error;
    416     }
    417     if (self->readable && console_type != 'r') {
    418         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 
    431 bad_mode:
    432     PyErr_SetString(PyExc_ValueError,
    433                     "Must have exactly one of read or write mode");
    434 error:
    435     ret = -1;
    436     internal_close(self);
    437 
    438 done:
    439     if (name)
    440         PyMem_Free(name);
    441     return ret;
    442 }
    443 
    444 static int
    445 winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
    446 {
    447     Py_VISIT(self->dict);
    448     return 0;
    449 }
    450 
    451 static int
    452 winconsoleio_clear(winconsoleio *self)
    453 {
    454     Py_CLEAR(self->dict);
    455     return 0;
    456 }
    457 
    458 static void
    459 winconsoleio_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 
    471 static PyObject *
    472 err_closed(void)
    473 {
    474     PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
    475     return NULL;
    476 }
    477 
    478 static PyObject *
    479 err_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 
    491 Return the underlying file descriptor (an integer).
    492 
    493 fileno is only set when a file descriptor is used to open
    494 one of the standard streams.
    495 
    496 [clinic start generated code]*/
    497 
    498 static 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)
    505             self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
    506         else
    507             self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
    508         _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 
    518 True if console is an input buffer.
    519 [clinic start generated code]*/
    520 
    521 static 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 
    533 True if console is an output buffer.
    534 [clinic start generated code]*/
    535 
    536 static 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 
    545 static DWORD
    546 _buflen(winconsoleio *self)
    547 {
    548     for (DWORD i = 0; i < SMALLBUF; ++i) {
    549         if (!self->buf[i])
    550             return i;
    551     }
    552     return SMALLBUF;
    553 }
    554 
    555 static DWORD
    556 _copyfrombuf(winconsoleio *self, char *buf, DWORD len)
    557 {
    558     DWORD n = 0;
    559 
    560     while (self->buf[0] && len--) {
    561         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;
    565     }
    566 
    567     return n;
    568 }
    569 
    570 static wchar_t *
    571 read_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;
    577 
    578     *readlen = 0;
    579 
    580     //DebugBreak();
    581     Py_BEGIN_ALLOW_THREADS
    582     DWORD off = 0;
    583     while (off < maxlen) {
    584         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)
    599                     == WAIT_OBJECT_0) {
    600                 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;
    617         /* 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;
    639     }
    640 
    641     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 
    661 error:
    662     if (buf)
    663         PyMem_Free(buf);
    664     return NULL;
    665 }
    666 
    667 
    668 static Py_ssize_t
    669 readinto(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 
    765 Same as RawIOBase.readinto().
    766 [clinic start generated code]*/
    767 
    768 static 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 
    779 static DWORD
    780 new_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 
    800 Read all data from the console, returned as bytes.
    801 
    802 Return an empty bytes object at EOF.
    803 [clinic start generated code]*/
    804 
    805 static 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 
    858         /* when the read is empty we break */
    859         if (n == 0)
    860             break;
    861 
    862         len += n;
    863     }
    864 
    865     if (len == 0 && _buflen(self) == 0) {
    866         /* when the result starts with ^Z we return an empty buffer */
    867         PyMem_Free(buf);
    868         return PyBytes_FromStringAndSize(NULL, 0);
    869     }
    870 
    871     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
    876 
    877         if (!bytes_size) {
    878             DWORD err = GetLastError();
    879             PyMem_Free(buf);
    880             return PyErr_SetFromWindowsErr(err);
    881         }
    882     } else {
    883         bytes_size = 0;
    884     }
    885 
    886     bytes_size += _buflen(self);
    887     bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
    888     rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
    889 
    890     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
    895 
    896         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;
    905     }
    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 
    922 Read at most size bytes, returned as bytes.
    923 
    924 Only makes one system call when size is a positive integer,
    925 so less data may be returned than requested.
    926 Return an empty bytes object at EOF.
    927 [clinic start generated code]*/
    928 
    929 static 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;
    935 
    936     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 
    973 Write buffer b to file, return number of bytes written.
    974 
    975 Only makes one system call, so not all of the data may be written.
    976 The number of bytes actually written is returned.
    977 [clinic start generated code]*/
    978 
    979 static 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
   1010 
   1011     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
   1037 
   1038     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 
   1048 static PyObject *
   1049 winconsoleio_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 
   1068 Always True.
   1069 [clinic start generated code]*/
   1070 
   1071 static 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();
   1077 
   1078     Py_RETURN_TRUE;
   1079 }
   1080 
   1081 static PyObject *
   1082 winconsoleio_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 
   1091 static 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 
   1107 static PyObject *
   1108 get_closed(winconsoleio *self, void *closure)
   1109 {
   1110     return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
   1111 }
   1112 
   1113 static PyObject *
   1114 get_closefd(winconsoleio *self, void *closure)
   1115 {
   1116     return PyBool_FromLong((long)(self->closehandle));
   1117 }
   1118 
   1119 static PyObject *
   1120 get_mode(winconsoleio *self, void *closure)
   1121 {
   1122     return PyUnicode_FromString(self->readable ? "rb" : "wb");
   1123 }
   1124 
   1125 static 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 
   1133 static PyMemberDef winconsoleio_members[] = {
   1134     {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
   1135     {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
   1136     {NULL}
   1137 };
   1138 
   1139 PyTypeObject 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 
   1191 PyAPI_DATA(PyObject *) _PyWindowsConsoleIO_Type = (PyObject*)&PyWindowsConsoleIO_Type;
   1192 
   1193 #endif /* MS_WINDOWS */
   1194