winconsoleio.c 32 KB

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