ยปCore Development>Code coverage>Modules/_io/winconsoleio.c

Python code coverage for Modules/_io/winconsoleio.c

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