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