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 */