blob: 0bf4ddf7e1bec03901efd838108c9170721013a1 [file] [log] [blame]
Steve Dower39294992016-08-30 21:22:36 -07001/*
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>
Steve Dower27f26ad2016-09-17 13:51:23 -070025#include <fcntl.h>
Steve Dower39294992016-08-30 21:22:36 -070026
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
42char _get_console_type(HANDLE handle) {
43 DWORD mode, peek_count;
44
45 if (handle == INVALID_HANDLE_VALUE)
46 return '\0';
Benjamin Petersone2e792d2016-09-19 22:17:16 -070047
Steve Dower39294992016-08-30 21:22:36 -070048 if (!GetConsoleMode(handle, &mode))
49 return '\0';
50
51 /* Peek at the handle to see whether it is an input or output handle */
52 if (GetNumberOfConsoleInputEvents(handle, &peek_count))
53 return 'r';
54 return 'w';
55}
56
57char _PyIO_get_console_type(PyObject *path_or_fd) {
58 int fd;
59
60 fd = PyLong_AsLong(path_or_fd);
61 PyErr_Clear();
62 if (fd >= 0) {
63 HANDLE handle;
64 _Py_BEGIN_SUPPRESS_IPH
65 handle = (HANDLE)_get_osfhandle(fd);
66 _Py_END_SUPPRESS_IPH
67 if (!handle)
68 return '\0';
69 return _get_console_type(handle);
70 }
71
Steve Dower27f26ad2016-09-17 13:51:23 -070072 PyObject *decoded, *decoded_upper;
Steve Dower39294992016-08-30 21:22:36 -070073
74 int d = PyUnicode_FSDecoder(path_or_fd, &decoded);
75 if (!d) {
76 PyErr_Clear();
Steve Dower27f26ad2016-09-17 13:51:23 -070077 return '\0';
78 }
79 if (!PyUnicode_Check(decoded)) {
Steve Dower39294992016-08-30 21:22:36 -070080 Py_CLEAR(decoded);
81 return '\0';
82 }
Steve Dower27f26ad2016-09-17 13:51:23 -070083 decoded_upper = PyObject_CallMethod(decoded, "upper", "");
84 Py_CLEAR(decoded);
85 if (!decoded_upper) {
86 PyErr_Clear();
87 return '\0';
88 }
Steve Dower39294992016-08-30 21:22:36 -070089
90 char m = '\0';
Steve Dower27f26ad2016-09-17 13:51:23 -070091 if (PyUnicode_CompareWithASCIIString(decoded_upper, "CONIN$") == 0) {
Steve Dower39294992016-08-30 21:22:36 -070092 m = 'r';
Steve Dower27f26ad2016-09-17 13:51:23 -070093 } else if (PyUnicode_CompareWithASCIIString(decoded_upper, "CONOUT$") == 0) {
Steve Dower39294992016-08-30 21:22:36 -070094 m = 'w';
Steve Dower27f26ad2016-09-17 13:51:23 -070095 } else if (PyUnicode_CompareWithASCIIString(decoded_upper, "CON") == 0) {
96 m = 'x';
Steve Dower39294992016-08-30 21:22:36 -070097 }
98
Steve Dower27f26ad2016-09-17 13:51:23 -070099 Py_CLEAR(decoded_upper);
Steve Dower39294992016-08-30 21:22:36 -0700100 return m;
101}
102
103/*[clinic input]
104module _io
105class _io._WindowsConsoleIO "winconsoleio *" "&PyWindowsConsoleIO_Type"
106[clinic start generated code]*/
107/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e897fdc1fba4e131]*/
108
109/*[python input]
110class io_ssize_t_converter(CConverter):
111 type = 'Py_ssize_t'
112 converter = '_PyIO_ConvertSsize_t'
113[python start generated code]*/
114/*[python end generated code: output=da39a3ee5e6b4b0d input=d0a811d3cbfd1b33]*/
115
116typedef struct {
117 PyObject_HEAD
118 HANDLE handle;
119 int fd;
120 unsigned int created : 1;
121 unsigned int readable : 1;
122 unsigned int writable : 1;
123 unsigned int closehandle : 1;
124 char finalizing;
125 unsigned int blksize;
126 PyObject *weakreflist;
127 PyObject *dict;
128 char buf[4];
129} winconsoleio;
130
131PyTypeObject PyWindowsConsoleIO_Type;
132
133_Py_IDENTIFIER(name);
134
135int
136_PyWindowsConsoleIO_closed(PyObject *self)
137{
138 return ((winconsoleio *)self)->handle == INVALID_HANDLE_VALUE;
139}
140
141
142/* Returns 0 on success, -1 with exception set on failure. */
143static int
144internal_close(winconsoleio *self)
145{
146 if (self->handle != INVALID_HANDLE_VALUE) {
147 if (self->closehandle) {
148 if (self->fd >= 0) {
149 _Py_BEGIN_SUPPRESS_IPH
150 close(self->fd);
151 _Py_END_SUPPRESS_IPH
152 }
153 CloseHandle(self->handle);
154 }
155 self->handle = INVALID_HANDLE_VALUE;
156 self->fd = -1;
157 }
158 return 0;
159}
160
161/*[clinic input]
162_io._WindowsConsoleIO.close
163
164Close the handle.
165
166A closed handle cannot be used for further I/O operations. close() may be
167called more than once without error.
168[clinic start generated code]*/
169
170static PyObject *
171_io__WindowsConsoleIO_close_impl(winconsoleio *self)
172/*[clinic end generated code: output=27ef95b66c29057b input=185617e349ae4c7b]*/
173{
174 PyObject *res;
175 PyObject *exc, *val, *tb;
176 int rc;
177 _Py_IDENTIFIER(close);
178 res = _PyObject_CallMethodId((PyObject*)&PyRawIOBase_Type,
179 &PyId_close, "O", self);
180 if (!self->closehandle) {
181 self->handle = INVALID_HANDLE_VALUE;
182 return res;
183 }
184 if (res == NULL)
185 PyErr_Fetch(&exc, &val, &tb);
186 rc = internal_close(self);
187 if (res == NULL)
188 _PyErr_ChainExceptions(exc, val, tb);
189 if (rc < 0)
190 Py_CLEAR(res);
191 return res;
192}
193
194static PyObject *
195winconsoleio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
196{
197 winconsoleio *self;
198
199 assert(type != NULL && type->tp_alloc != NULL);
200
201 self = (winconsoleio *) type->tp_alloc(type, 0);
202 if (self != NULL) {
203 self->handle = INVALID_HANDLE_VALUE;
204 self->fd = -1;
205 self->created = 0;
206 self->readable = 0;
207 self->writable = 0;
208 self->closehandle = 0;
209 self->blksize = 0;
210 self->weakreflist = NULL;
211 }
212
213 return (PyObject *) self;
214}
215
216/*[clinic input]
217_io._WindowsConsoleIO.__init__
218 file as nameobj: object
219 mode: str = "r"
220 closefd: int(c_default="1") = True
221 opener: object = None
222
223Open a console buffer by file descriptor.
224
225The mode can be 'rb' (default), or 'wb' for reading or writing bytes. All
226other mode characters will be ignored. Mode 'b' will be assumed if it is
227omitted. The *opener* parameter is always ignored.
228[clinic start generated code]*/
229
230static int
231_io__WindowsConsoleIO___init___impl(winconsoleio *self, PyObject *nameobj,
232 const char *mode, int closefd,
233 PyObject *opener)
234/*[clinic end generated code: output=3fd9cbcdd8d95429 input=61be39633a86f5d7]*/
235{
236 const char *s;
237 wchar_t *name = NULL;
Steve Dower27f26ad2016-09-17 13:51:23 -0700238 char console_type = '\0';
Steve Dower39294992016-08-30 21:22:36 -0700239 int ret = 0;
240 int rwa = 0;
241 int fd = -1;
242 int fd_is_own = 0;
243
244 assert(PyWindowsConsoleIO_Check(self));
245 if (self->handle >= 0) {
246 if (self->closehandle) {
247 /* Have to close the existing file first. */
248 if (internal_close(self) < 0)
249 return -1;
250 }
251 else
252 self->handle = INVALID_HANDLE_VALUE;
253 }
254
255 if (PyFloat_Check(nameobj)) {
256 PyErr_SetString(PyExc_TypeError,
257 "integer argument expected, got float");
258 return -1;
259 }
260
261 fd = _PyLong_AsInt(nameobj);
262 if (fd < 0) {
263 if (!PyErr_Occurred()) {
264 PyErr_SetString(PyExc_ValueError,
265 "negative file descriptor");
266 return -1;
267 }
268 PyErr_Clear();
269 }
270 self->fd = fd;
271
272 if (fd < 0) {
273 PyObject *decodedname = Py_None;
274 Py_INCREF(decodedname);
275
276 int d = PyUnicode_FSDecoder(nameobj, (void*)&decodedname);
277 if (!d)
278 return -1;
279
280 Py_ssize_t length;
281 name = PyUnicode_AsWideCharString(decodedname, &length);
Steve Dower27f26ad2016-09-17 13:51:23 -0700282 console_type = _PyIO_get_console_type(decodedname);
Steve Dower39294992016-08-30 21:22:36 -0700283 Py_CLEAR(decodedname);
284 if (name == NULL)
285 return -1;
286
287 if (wcslen(name) != length) {
288 PyMem_Free(name);
289 PyErr_SetString(PyExc_ValueError, "embedded null character");
290 return -1;
291 }
292 }
293
294 s = mode;
295 while (*s) {
296 switch (*s++) {
297 case '+':
298 case 'a':
299 case 'b':
300 case 'x':
301 break;
302 case 'r':
303 if (rwa)
304 goto bad_mode;
305 rwa = 1;
306 self->readable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700307 if (console_type == 'x')
308 console_type = 'r';
Steve Dower39294992016-08-30 21:22:36 -0700309 break;
310 case 'w':
311 if (rwa)
312 goto bad_mode;
313 rwa = 1;
314 self->writable = 1;
Steve Dower27f26ad2016-09-17 13:51:23 -0700315 if (console_type == 'x')
316 console_type = 'w';
Steve Dower39294992016-08-30 21:22:36 -0700317 break;
318 default:
319 PyErr_Format(PyExc_ValueError,
320 "invalid mode: %.200s", mode);
321 goto error;
322 }
323 }
324
325 if (!rwa)
326 goto bad_mode;
327
328 if (fd >= 0) {
329 _Py_BEGIN_SUPPRESS_IPH
330 self->handle = (HANDLE)_get_osfhandle(fd);
331 _Py_END_SUPPRESS_IPH
332 self->closehandle = 0;
333 } else {
334 DWORD access = GENERIC_READ;
335
336 self->closehandle = 1;
337 if (!closefd) {
338 PyErr_SetString(PyExc_ValueError,
339 "Cannot use closefd=False with file name");
340 goto error;
341 }
342
343 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700344 access = GENERIC_WRITE;
Steve Dower39294992016-08-30 21:22:36 -0700345
346 Py_BEGIN_ALLOW_THREADS
347 /* Attempt to open for read/write initially, then fall back
348 on the specific access. This is required for modern names
349 CONIN$ and CONOUT$, which allow reading/writing state as
350 well as reading/writing content. */
351 self->handle = CreateFileW(name, GENERIC_READ | GENERIC_WRITE,
352 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
353 if (self->handle == INVALID_HANDLE_VALUE)
354 self->handle = CreateFileW(name, access,
355 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
356 Py_END_ALLOW_THREADS
357
358 if (self->handle == INVALID_HANDLE_VALUE) {
359 PyErr_SetExcFromWindowsErrWithFilenameObject(PyExc_OSError, GetLastError(), nameobj);
360 goto error;
361 }
362 }
363
Steve Dower27f26ad2016-09-17 13:51:23 -0700364 if (console_type == '\0')
365 console_type = _get_console_type(self->handle);
366
367 if (self->writable && console_type != 'w') {
Steve Dower39294992016-08-30 21:22:36 -0700368 PyErr_SetString(PyExc_ValueError,
369 "Cannot open console input buffer for writing");
370 goto error;
371 }
Steve Dower27f26ad2016-09-17 13:51:23 -0700372 if (self->readable && console_type != 'r') {
Steve Dower39294992016-08-30 21:22:36 -0700373 PyErr_SetString(PyExc_ValueError,
374 "Cannot open console output buffer for reading");
375 goto error;
376 }
377
378 self->blksize = DEFAULT_BUFFER_SIZE;
379 memset(self->buf, 0, 4);
380
381 if (_PyObject_SetAttrId((PyObject *)self, &PyId_name, nameobj) < 0)
382 goto error;
383
384 goto done;
385
386bad_mode:
387 PyErr_SetString(PyExc_ValueError,
388 "Must have exactly one of read or write mode");
389error:
390 ret = -1;
391 internal_close(self);
392
393done:
394 if (name)
395 PyMem_Free(name);
396 return ret;
397}
398
399static int
400winconsoleio_traverse(winconsoleio *self, visitproc visit, void *arg)
401{
402 Py_VISIT(self->dict);
403 return 0;
404}
405
406static int
407winconsoleio_clear(winconsoleio *self)
408{
409 Py_CLEAR(self->dict);
410 return 0;
411}
412
413static void
414winconsoleio_dealloc(winconsoleio *self)
415{
416 self->finalizing = 1;
417 if (_PyIOBase_finalize((PyObject *) self) < 0)
418 return;
419 _PyObject_GC_UNTRACK(self);
420 if (self->weakreflist != NULL)
421 PyObject_ClearWeakRefs((PyObject *) self);
422 Py_CLEAR(self->dict);
423 Py_TYPE(self)->tp_free((PyObject *)self);
424}
425
426static PyObject *
427err_closed(void)
428{
429 PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
430 return NULL;
431}
432
433static PyObject *
434err_mode(const char *action)
435{
436 _PyIO_State *state = IO_STATE();
437 if (state != NULL)
438 PyErr_Format(state->unsupported_operation,
439 "Console buffer does not support %s", action);
440 return NULL;
441}
442
443/*[clinic input]
444_io._WindowsConsoleIO.fileno
445
446Return the underlying file descriptor (an integer).
447
448fileno is only set when a file descriptor is used to open
449one of the standard streams.
450
451[clinic start generated code]*/
452
453static PyObject *
454_io__WindowsConsoleIO_fileno_impl(winconsoleio *self)
455/*[clinic end generated code: output=006fa74ce3b5cfbf input=079adc330ddaabe6]*/
456{
457 if (self->fd < 0 && self->handle != INVALID_HANDLE_VALUE) {
458 _Py_BEGIN_SUPPRESS_IPH
459 if (self->writable)
Steve Dower27f26ad2016-09-17 13:51:23 -0700460 self->fd = _open_osfhandle((intptr_t)self->handle, _O_WRONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700461 else
Steve Dower27f26ad2016-09-17 13:51:23 -0700462 self->fd = _open_osfhandle((intptr_t)self->handle, _O_RDONLY | _O_BINARY);
Steve Dower39294992016-08-30 21:22:36 -0700463 _Py_END_SUPPRESS_IPH
464 }
465 if (self->fd < 0)
466 return err_mode("fileno");
467 return PyLong_FromLong(self->fd);
468}
469
470/*[clinic input]
471_io._WindowsConsoleIO.readable
472
473True if console is an input buffer.
474[clinic start generated code]*/
475
476static PyObject *
477_io__WindowsConsoleIO_readable_impl(winconsoleio *self)
478/*[clinic end generated code: output=daf9cef2743becf0 input=6be9defb5302daae]*/
479{
480 if (self->handle == INVALID_HANDLE_VALUE)
481 return err_closed();
482 return PyBool_FromLong((long) self->readable);
483}
484
485/*[clinic input]
486_io._WindowsConsoleIO.writable
487
488True if console is an output buffer.
489[clinic start generated code]*/
490
491static PyObject *
492_io__WindowsConsoleIO_writable_impl(winconsoleio *self)
493/*[clinic end generated code: output=e0a2ad7eae5abf67 input=cefbd8abc24df6a0]*/
494{
495 if (self->handle == INVALID_HANDLE_VALUE)
496 return err_closed();
497 return PyBool_FromLong((long) self->writable);
498}
499
500static DWORD
501_buflen(winconsoleio *self)
502{
503 for (DWORD i = 0; i < 4; ++i) {
504 if (!self->buf[i])
505 return i;
506 }
507 return 4;
508}
509
510static DWORD
511_copyfrombuf(winconsoleio *self, char *buf, DWORD len)
512{
513 DWORD n = 0;
514
515 while (self->buf[0] && len--) {
516 n += 1;
517 buf[0] = self->buf[0];
518 self->buf[0] = self->buf[1];
519 self->buf[1] = self->buf[2];
520 self->buf[2] = self->buf[3];
521 self->buf[3] = 0;
522 }
523
524 return n;
525}
526
527static wchar_t *
528read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
529 int err = 0, sig = 0;
530
531 wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t));
532 if (!buf)
533 goto error;
534 *readlen = 0;
535
536 Py_BEGIN_ALLOW_THREADS
537 for (DWORD off = 0; off < maxlen; off += BUFSIZ) {
538 DWORD n, len = min(maxlen - off, BUFSIZ);
539 SetLastError(0);
540 BOOL res = ReadConsoleW(handle, &buf[off], len, &n, NULL);
541
542 if (!res) {
543 err = GetLastError();
544 break;
545 }
546 if (n == 0) {
547 err = GetLastError();
548 if (err != ERROR_OPERATION_ABORTED)
549 break;
550 err = 0;
551 HANDLE hInterruptEvent = _PyOS_SigintEvent();
552 if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
553 == WAIT_OBJECT_0) {
554 ResetEvent(hInterruptEvent);
555 Py_BLOCK_THREADS
556 sig = PyErr_CheckSignals();
557 Py_UNBLOCK_THREADS
558 if (sig < 0)
559 break;
560 }
561 }
562 *readlen += n;
563
564 /* If we didn't read a full buffer that time, don't try
565 again or we will block a second time. */
566 if (n < len)
567 break;
568 /* If the buffer ended with a newline, break out */
569 if (buf[*readlen - 1] == '\n')
570 break;
571 }
572 Py_END_ALLOW_THREADS
573
574 if (sig)
575 goto error;
576 if (err) {
577 PyErr_SetFromWindowsErr(err);
578 goto error;
579 }
580
581 if (*readlen > 0 && buf[0] == L'\x1a') {
582 PyMem_Free(buf);
583 buf = (wchar_t *)PyMem_Malloc(sizeof(wchar_t));
584 if (!buf)
585 goto error;
586 buf[0] = L'\0';
587 *readlen = 0;
588 }
589
590 return buf;
591
592error:
593 if (buf)
594 PyMem_Free(buf);
595 return NULL;
596}
597
598
599static Py_ssize_t
600readinto(winconsoleio *self, char *buf, Py_ssize_t len)
601{
602 if (self->handle == INVALID_HANDLE_VALUE) {
603 err_closed();
604 return -1;
605 }
606 if (!self->readable) {
607 err_mode("reading");
608 return -1;
609 }
610 if (len == 0)
611 return 0;
612 if (len > BUFMAX) {
613 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
614 return -1;
615 }
616
617 /* Each character may take up to 4 bytes in the final buffer.
618 This is highly conservative, but necessary to avoid
619 failure for any given Unicode input (e.g. \U0010ffff).
620 If the caller requests fewer than 4 bytes, we buffer one
621 character.
622 */
623 DWORD wlen = (DWORD)(len / 4);
624 if (wlen == 0) {
625 wlen = 1;
626 }
627
628 DWORD read_len = _copyfrombuf(self, buf, (DWORD)len);
629 if (read_len) {
630 buf = &buf[read_len];
631 len -= read_len;
632 wlen -= 1;
633 }
634 if (len == read_len || wlen == 0)
635 return read_len;
636
637 DWORD n;
638 wchar_t *wbuf = read_console_w(self->handle, wlen, &n);
639 if (wbuf == NULL)
640 return -1;
641 if (n == 0) {
642 PyMem_Free(wbuf);
643 return read_len;
644 }
645
646 int err = 0;
647 DWORD u8n = 0;
648
649 Py_BEGIN_ALLOW_THREADS
650 if (len < 4) {
651 if (WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
652 self->buf, sizeof(self->buf) / sizeof(self->buf[0]),
653 NULL, NULL))
654 u8n = _copyfrombuf(self, buf, (DWORD)len);
655 } else {
656 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
657 buf, (DWORD)len, NULL, NULL);
658 }
659
660 if (u8n) {
661 read_len += u8n;
662 u8n = 0;
663 } else {
664 err = GetLastError();
665 if (err == ERROR_INSUFFICIENT_BUFFER) {
666 /* Calculate the needed buffer for a more useful error, as this
667 means our "/ 4" logic above is insufficient for some input.
668 */
669 u8n = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
670 NULL, 0, NULL, NULL);
671 }
672 }
673 Py_END_ALLOW_THREADS
674
675 PyMem_Free(wbuf);
676
677 if (u8n) {
678 PyErr_Format(PyExc_SystemError,
679 "Buffer had room for %d bytes but %d bytes required",
680 len, u8n);
681 return -1;
682 }
683 if (err) {
684 PyErr_SetFromWindowsErr(err);
685 return -1;
686 }
687
688 return read_len;
689}
690
691/*[clinic input]
692_io._WindowsConsoleIO.readinto
693 buffer: Py_buffer(accept={rwbuffer})
694 /
695
696Same as RawIOBase.readinto().
697[clinic start generated code]*/
698
699static PyObject *
700_io__WindowsConsoleIO_readinto_impl(winconsoleio *self, Py_buffer *buffer)
701/*[clinic end generated code: output=66d1bdfa3f20af39 input=4ed68da48a6baffe]*/
702{
703 Py_ssize_t len = readinto(self, buffer->buf, buffer->len);
704 if (len < 0)
705 return NULL;
706
707 return PyLong_FromSsize_t(len);
708}
709
710static DWORD
711new_buffersize(winconsoleio *self, DWORD currentsize)
712{
713 DWORD addend;
714
715 /* Expand the buffer by an amount proportional to the current size,
716 giving us amortized linear-time behavior. For bigger sizes, use a
717 less-than-double growth factor to avoid excessive allocation. */
718 if (currentsize > 65536)
719 addend = currentsize >> 3;
720 else
721 addend = 256 + currentsize;
722 if (addend < SMALLCHUNK)
723 /* Avoid tiny read() calls. */
724 addend = SMALLCHUNK;
725 return addend + currentsize;
726}
727
728/*[clinic input]
729_io._WindowsConsoleIO.readall
730
731Read all data from the console, returned as bytes.
732
733Return an empty bytes object at EOF.
734[clinic start generated code]*/
735
736static PyObject *
737_io__WindowsConsoleIO_readall_impl(winconsoleio *self)
738/*[clinic end generated code: output=e6d312c684f6e23b input=4024d649a1006e69]*/
739{
740 wchar_t *buf;
741 DWORD bufsize, n, len = 0;
742 PyObject *bytes;
743 DWORD bytes_size, rn;
744
745 if (self->handle == INVALID_HANDLE_VALUE)
746 return err_closed();
747
748 bufsize = BUFSIZ;
749
750 buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t));
751 if (buf == NULL)
752 return NULL;
753
754 while (1) {
755 wchar_t *subbuf;
756
757 if (len >= (Py_ssize_t)bufsize) {
758 DWORD newsize = new_buffersize(self, len);
759 if (newsize > BUFMAX)
760 break;
761 if (newsize < bufsize) {
762 PyErr_SetString(PyExc_OverflowError,
763 "unbounded read returned more bytes "
764 "than a Python bytes object can hold");
765 PyMem_Free(buf);
766 return NULL;
767 }
768 bufsize = newsize;
769
770 buf = PyMem_Realloc(buf, (bufsize + 1) * sizeof(wchar_t));
771 if (!buf) {
772 PyMem_Free(buf);
773 return NULL;
774 }
775 }
776
777 subbuf = read_console_w(self->handle, bufsize - len, &n);
778
779 if (subbuf == NULL) {
780 PyMem_Free(buf);
781 return NULL;
782 }
783
784 if (n > 0)
785 wcsncpy_s(&buf[len], bufsize - len + 1, subbuf, n);
786
787 PyMem_Free(subbuf);
788
789 /* when the read starts with ^Z or is empty we break */
790 if (n == 0 || buf[len] == '\x1a')
791 break;
792
793 len += n;
794 }
795
Steve Dower27f26ad2016-09-17 13:51:23 -0700796 if (len == 0 || buf[0] == '\x1a' && _buflen(self) == 0) {
Steve Dower39294992016-08-30 21:22:36 -0700797 /* when the result starts with ^Z we return an empty buffer */
798 PyMem_Free(buf);
799 return PyBytes_FromStringAndSize(NULL, 0);
800 }
801
802 Py_BEGIN_ALLOW_THREADS
803 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
804 NULL, 0, NULL, NULL);
805 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700806
Steve Dower39294992016-08-30 21:22:36 -0700807 if (!bytes_size) {
808 DWORD err = GetLastError();
809 PyMem_Free(buf);
810 return PyErr_SetFromWindowsErr(err);
811 }
812
813 bytes_size += _buflen(self);
814 bytes = PyBytes_FromStringAndSize(NULL, bytes_size);
815 rn = _copyfrombuf(self, PyBytes_AS_STRING(bytes), bytes_size);
816
817 Py_BEGIN_ALLOW_THREADS
818 bytes_size = WideCharToMultiByte(CP_UTF8, 0, buf, len,
819 &PyBytes_AS_STRING(bytes)[rn], bytes_size - rn, NULL, NULL);
820 Py_END_ALLOW_THREADS
821
822 if (!bytes_size) {
823 DWORD err = GetLastError();
824 PyMem_Free(buf);
825 Py_CLEAR(bytes);
826 return PyErr_SetFromWindowsErr(err);
827 }
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
839/*[clinic input]
840_io._WindowsConsoleIO.read
841 size: io_ssize_t = -1
842 /
843
844Read at most size bytes, returned as bytes.
845
846Only makes one system call when size is a positive integer,
847so less data may be returned than requested.
848Return an empty bytes object at EOF.
849[clinic start generated code]*/
850
851static PyObject *
852_io__WindowsConsoleIO_read_impl(winconsoleio *self, Py_ssize_t size)
853/*[clinic end generated code: output=57df68af9f4b22d0 input=6c56fceec460f1dd]*/
854{
855 PyObject *bytes;
856 Py_ssize_t bytes_size;
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700857
Steve Dower39294992016-08-30 21:22:36 -0700858 if (self->handle == INVALID_HANDLE_VALUE)
859 return err_closed();
860 if (!self->readable)
861 return err_mode("reading");
862
863 if (size < 0)
864 return _io__WindowsConsoleIO_readall_impl(self);
865 if (size > BUFMAX) {
866 PyErr_Format(PyExc_ValueError, "cannot read more than %d bytes", BUFMAX);
867 return NULL;
868 }
869
870 bytes = PyBytes_FromStringAndSize(NULL, size);
871 if (bytes == NULL)
872 return NULL;
873
874 bytes_size = readinto(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
875 if (bytes_size < 0) {
876 Py_CLEAR(bytes);
877 return NULL;
878 }
879
880 if (bytes_size < PyBytes_GET_SIZE(bytes)) {
881 if (_PyBytes_Resize(&bytes, bytes_size) < 0) {
882 Py_CLEAR(bytes);
883 return NULL;
884 }
885 }
886
887 return bytes;
888}
889
890/*[clinic input]
891_io._WindowsConsoleIO.write
892 b: Py_buffer
893 /
894
895Write buffer b to file, return number of bytes written.
896
897Only makes one system call, so not all of the data may be written.
898The number of bytes actually written is returned.
899[clinic start generated code]*/
900
901static PyObject *
902_io__WindowsConsoleIO_write_impl(winconsoleio *self, Py_buffer *b)
903/*[clinic end generated code: output=775bdb16fbf9137b input=be35fb624f97c941]*/
904{
905 BOOL res = TRUE;
906 wchar_t *wbuf;
907 DWORD len, wlen, n = 0;
908
909 if (self->handle == INVALID_HANDLE_VALUE)
910 return err_closed();
911 if (!self->writable)
912 return err_mode("writing");
913
914 if (b->len > BUFMAX)
915 len = BUFMAX;
916 else
917 len = (DWORD)b->len;
918
919 Py_BEGIN_ALLOW_THREADS
920 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
921
922 /* issue11395 there is an unspecified upper bound on how many bytes
923 can be written at once. We cap at 32k - the caller will have to
924 handle partial writes.
925 Since we don't know how many input bytes are being ignored, we
926 have to reduce and recalculate. */
927 while (wlen > 32766 / sizeof(wchar_t)) {
928 len /= 2;
929 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, NULL, 0);
930 }
931 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700932
Steve Dower39294992016-08-30 21:22:36 -0700933 if (!wlen)
934 return PyErr_SetFromWindowsErr(0);
935
936 wbuf = (wchar_t*)PyMem_Malloc(wlen * sizeof(wchar_t));
937
938 Py_BEGIN_ALLOW_THREADS
939 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len, wbuf, wlen);
940 if (wlen) {
941 res = WriteConsoleW(self->handle, wbuf, wlen, &n, NULL);
942 if (n < wlen) {
943 /* Wrote fewer characters than expected, which means our
944 * len value may be wrong. So recalculate it from the
945 * characters that were written. As this could potentially
946 * result in a different value, we also validate that value.
947 */
948 len = WideCharToMultiByte(CP_UTF8, 0, wbuf, n,
949 NULL, 0, NULL, NULL);
950 if (len) {
951 wlen = MultiByteToWideChar(CP_UTF8, 0, b->buf, len,
952 NULL, 0);
953 assert(wlen == len);
954 }
955 }
956 } else
957 res = 0;
958 Py_END_ALLOW_THREADS
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700959
Steve Dower39294992016-08-30 21:22:36 -0700960 if (!res) {
961 DWORD err = GetLastError();
962 PyMem_Free(wbuf);
963 return PyErr_SetFromWindowsErr(err);
964 }
965
966 PyMem_Free(wbuf);
967 return PyLong_FromSsize_t(len);
968}
969
970static PyObject *
971winconsoleio_repr(winconsoleio *self)
972{
973 if (self->handle == INVALID_HANDLE_VALUE)
974 return PyUnicode_FromFormat("<_io._WindowsConsoleIO [closed]>");
975
976 if (self->readable)
977 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='rb' closefd=%s>",
978 self->closehandle ? "True" : "False");
979 if (self->writable)
980 return PyUnicode_FromFormat("<_io._WindowsConsoleIO mode='wb' closefd=%s>",
981 self->closehandle ? "True" : "False");
982
983 PyErr_SetString(PyExc_SystemError, "_WindowsConsoleIO has invalid mode");
984 return NULL;
985}
986
987/*[clinic input]
988_io._WindowsConsoleIO.isatty
989
990Always True.
991[clinic start generated code]*/
992
993static PyObject *
994_io__WindowsConsoleIO_isatty_impl(winconsoleio *self)
995/*[clinic end generated code: output=9eac09d287c11bd7 input=9b91591dbe356f86]*/
996{
997 if (self->handle == INVALID_HANDLE_VALUE)
998 return err_closed();
Benjamin Petersone2e792d2016-09-19 22:17:16 -0700999
Steve Dower39294992016-08-30 21:22:36 -07001000 Py_RETURN_TRUE;
1001}
1002
1003static PyObject *
1004winconsoleio_getstate(winconsoleio *self)
1005{
1006 PyErr_Format(PyExc_TypeError,
1007 "cannot serialize '%s' object", Py_TYPE(self)->tp_name);
1008 return NULL;
1009}
1010
1011#include "clinic/winconsoleio.c.h"
1012
1013static PyMethodDef winconsoleio_methods[] = {
1014 _IO__WINDOWSCONSOLEIO_READ_METHODDEF
1015 _IO__WINDOWSCONSOLEIO_READALL_METHODDEF
1016 _IO__WINDOWSCONSOLEIO_READINTO_METHODDEF
1017 _IO__WINDOWSCONSOLEIO_WRITE_METHODDEF
1018 _IO__WINDOWSCONSOLEIO_CLOSE_METHODDEF
1019 _IO__WINDOWSCONSOLEIO_READABLE_METHODDEF
1020 _IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
1021 _IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
1022 _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
1023 {"__getstate__", (PyCFunction)winconsoleio_getstate, METH_NOARGS, NULL},
1024 {NULL, NULL} /* sentinel */
1025};
1026
1027/* 'closed' and 'mode' are attributes for compatibility with FileIO. */
1028
1029static PyObject *
1030get_closed(winconsoleio *self, void *closure)
1031{
1032 return PyBool_FromLong((long)(self->handle == INVALID_HANDLE_VALUE));
1033}
1034
1035static PyObject *
1036get_closefd(winconsoleio *self, void *closure)
1037{
1038 return PyBool_FromLong((long)(self->closehandle));
1039}
1040
1041static PyObject *
1042get_mode(winconsoleio *self, void *closure)
1043{
1044 return PyUnicode_FromString(self->readable ? "rb" : "wb");
1045}
1046
1047static PyGetSetDef winconsoleio_getsetlist[] = {
1048 {"closed", (getter)get_closed, NULL, "True if the file is closed"},
1049 {"closefd", (getter)get_closefd, NULL,
1050 "True if the file descriptor will be closed by close()."},
1051 {"mode", (getter)get_mode, NULL, "String giving the file mode"},
1052 {NULL},
1053};
1054
1055static PyMemberDef winconsoleio_members[] = {
1056 {"_blksize", T_UINT, offsetof(winconsoleio, blksize), 0},
1057 {"_finalizing", T_BOOL, offsetof(winconsoleio, finalizing), 0},
1058 {NULL}
1059};
1060
1061PyTypeObject PyWindowsConsoleIO_Type = {
1062 PyVarObject_HEAD_INIT(NULL, 0)
1063 "_io._WindowsConsoleIO",
1064 sizeof(winconsoleio),
1065 0,
1066 (destructor)winconsoleio_dealloc, /* tp_dealloc */
1067 0, /* tp_print */
1068 0, /* tp_getattr */
1069 0, /* tp_setattr */
1070 0, /* tp_reserved */
1071 (reprfunc)winconsoleio_repr, /* tp_repr */
1072 0, /* tp_as_number */
1073 0, /* tp_as_sequence */
1074 0, /* tp_as_mapping */
1075 0, /* tp_hash */
1076 0, /* tp_call */
1077 0, /* tp_str */
1078 PyObject_GenericGetAttr, /* tp_getattro */
1079 0, /* tp_setattro */
1080 0, /* tp_as_buffer */
1081 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE
1082 | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
1083 _io__WindowsConsoleIO___init____doc__, /* tp_doc */
1084 (traverseproc)winconsoleio_traverse, /* tp_traverse */
1085 (inquiry)winconsoleio_clear, /* tp_clear */
1086 0, /* tp_richcompare */
1087 offsetof(winconsoleio, weakreflist), /* tp_weaklistoffset */
1088 0, /* tp_iter */
1089 0, /* tp_iternext */
1090 winconsoleio_methods, /* tp_methods */
1091 winconsoleio_members, /* tp_members */
1092 winconsoleio_getsetlist, /* tp_getset */
1093 0, /* tp_base */
1094 0, /* tp_dict */
1095 0, /* tp_descr_get */
1096 0, /* tp_descr_set */
1097 offsetof(winconsoleio, dict), /* tp_dictoffset */
1098 _io__WindowsConsoleIO___init__, /* tp_init */
1099 PyType_GenericAlloc, /* tp_alloc */
1100 winconsoleio_new, /* tp_new */
1101 PyObject_GC_Del, /* tp_free */
1102 0, /* tp_is_gc */
1103 0, /* tp_bases */
1104 0, /* tp_mro */
1105 0, /* tp_cache */
1106 0, /* tp_subclasses */
1107 0, /* tp_weaklist */
1108 0, /* tp_del */
1109 0, /* tp_version_tag */
1110 0, /* tp_finalize */
1111};
1112
1113#endif /* MS_WINDOWS */