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