Coverage Report

Created: 2025-07-11 06:59

/src/Python-3.8.3/Parser/myreadline.c
Line
Count
Source (jump to first uncovered line)
1
2
/* Readline interface for tokenizer.c and [raw_]input() in bltinmodule.c.
3
   By default, or when stdin is not a tty device, we have a super
4
   simple my_readline function using fgets.
5
   Optionally, we can use the GNU readline library.
6
   my_readline() has a different return value from GNU readline():
7
   - NULL if an interrupt occurred or if an error occurred
8
   - a malloc'ed empty string if EOF was read
9
   - a malloc'ed string ending in \n normally
10
*/
11
12
#include "Python.h"
13
#include "pycore_pystate.h"
14
#ifdef MS_WINDOWS
15
#define WIN32_LEAN_AND_MEAN
16
#include "windows.h"
17
#endif /* MS_WINDOWS */
18
19
20
PyThreadState* _PyOS_ReadlineTState = NULL;
21
22
#include "pythread.h"
23
static PyThread_type_lock _PyOS_ReadlineLock = NULL;
24
25
int (*PyOS_InputHook)(void) = NULL;
26
27
/* This function restarts a fgets() after an EINTR error occurred
28
   except if PyOS_InterruptOccurred() returns true. */
29
30
static int
31
my_fgets(char *buf, int len, FILE *fp)
32
0
{
33
#ifdef MS_WINDOWS
34
    HANDLE hInterruptEvent;
35
#endif
36
0
    char *p;
37
0
    int err;
38
0
    while (1) {
39
0
        if (PyOS_InputHook != NULL)
40
0
            (void)(PyOS_InputHook)();
41
0
        errno = 0;
42
0
        clearerr(fp);
43
0
        p = fgets(buf, len, fp);
44
0
        if (p != NULL)
45
0
            return 0; /* No error */
46
0
        err = errno;
47
#ifdef MS_WINDOWS
48
        /* Ctrl-C anywhere on the line or Ctrl-Z if the only character
49
           on a line will set ERROR_OPERATION_ABORTED. Under normal
50
           circumstances Ctrl-C will also have caused the SIGINT handler
51
           to fire which will have set the event object returned by
52
           _PyOS_SigintEvent. This signal fires in another thread and
53
           is not guaranteed to have occurred before this point in the
54
           code.
55
56
           Therefore: check whether the event is set with a small timeout.
57
           If it is, assume this is a Ctrl-C and reset the event. If it
58
           isn't set assume that this is a Ctrl-Z on its own and drop
59
           through to check for EOF.
60
        */
61
        if (GetLastError()==ERROR_OPERATION_ABORTED) {
62
            hInterruptEvent = _PyOS_SigintEvent();
63
            switch (WaitForSingleObjectEx(hInterruptEvent, 10, FALSE)) {
64
            case WAIT_OBJECT_0:
65
                ResetEvent(hInterruptEvent);
66
                return 1; /* Interrupt */
67
            case WAIT_FAILED:
68
                return -2; /* Error */
69
            }
70
        }
71
#endif /* MS_WINDOWS */
72
0
        if (feof(fp)) {
73
0
            clearerr(fp);
74
0
            return -1; /* EOF */
75
0
        }
76
0
#ifdef EINTR
77
0
        if (err == EINTR) {
78
0
            int s;
79
0
            PyEval_RestoreThread(_PyOS_ReadlineTState);
80
0
            s = PyErr_CheckSignals();
81
0
            PyEval_SaveThread();
82
0
            if (s < 0)
83
0
                    return 1;
84
        /* try again */
85
0
            continue;
86
0
        }
87
0
#endif
88
0
        if (PyOS_InterruptOccurred()) {
89
0
            return 1; /* Interrupt */
90
0
        }
91
0
        return -2; /* Error */
92
0
    }
93
    /* NOTREACHED */
94
0
}
95
96
#ifdef MS_WINDOWS
97
/* Readline implementation using ReadConsoleW */
98
99
extern char _get_console_type(HANDLE handle);
100
101
char *
102
_PyOS_WindowsConsoleReadline(HANDLE hStdIn)
103
{
104
    static wchar_t wbuf_local[1024 * 16];
105
    const DWORD chunk_size = 1024;
106
107
    DWORD n_read, total_read, wbuflen, u8len;
108
    wchar_t *wbuf;
109
    char *buf = NULL;
110
    int err = 0;
111
112
    n_read = (DWORD)-1;
113
    total_read = 0;
114
    wbuf = wbuf_local;
115
    wbuflen = sizeof(wbuf_local) / sizeof(wbuf_local[0]) - 1;
116
    while (1) {
117
        if (PyOS_InputHook != NULL) {
118
            (void)(PyOS_InputHook)();
119
        }
120
        if (!ReadConsoleW(hStdIn, &wbuf[total_read], wbuflen - total_read, &n_read, NULL)) {
121
            err = GetLastError();
122
            goto exit;
123
        }
124
        if (n_read == (DWORD)-1 && (err = GetLastError()) == ERROR_OPERATION_ABORTED) {
125
            break;
126
        }
127
        if (n_read == 0) {
128
            int s;
129
            err = GetLastError();
130
            if (err != ERROR_OPERATION_ABORTED)
131
                goto exit;
132
            err = 0;
133
            HANDLE hInterruptEvent = _PyOS_SigintEvent();
134
            if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
135
                    == WAIT_OBJECT_0) {
136
                ResetEvent(hInterruptEvent);
137
                PyEval_RestoreThread(_PyOS_ReadlineTState);
138
                s = PyErr_CheckSignals();
139
                PyEval_SaveThread();
140
                if (s < 0)
141
                    goto exit;
142
            }
143
            break;
144
        }
145
146
        total_read += n_read;
147
        if (total_read == 0 || wbuf[total_read - 1] == L'\n') {
148
            break;
149
        }
150
        wbuflen += chunk_size;
151
        if (wbuf == wbuf_local) {
152
            wbuf[total_read] = '\0';
153
            wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t));
154
            if (wbuf)
155
                wcscpy_s(wbuf, wbuflen, wbuf_local);
156
            else {
157
                PyErr_NoMemory();
158
                goto exit;
159
            }
160
        }
161
        else {
162
            wchar_t *tmp = PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t));
163
            if (tmp == NULL) {
164
                PyErr_NoMemory();
165
                goto exit;
166
            }
167
            wbuf = tmp;
168
        }
169
    }
170
171
    if (wbuf[0] == '\x1a') {
172
        buf = PyMem_RawMalloc(1);
173
        if (buf)
174
            buf[0] = '\0';
175
        else {
176
            PyErr_NoMemory();
177
        }
178
        goto exit;
179
    }
180
181
    u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL);
182
    buf = PyMem_RawMalloc(u8len + 1);
183
    if (buf == NULL) {
184
        PyErr_NoMemory();
185
        goto exit;
186
    }
187
    u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL);
188
    buf[u8len] = '\0';
189
190
exit:
191
    if (wbuf != wbuf_local)
192
        PyMem_RawFree(wbuf);
193
194
    if (err) {
195
        PyEval_RestoreThread(_PyOS_ReadlineTState);
196
        PyErr_SetFromWindowsErr(err);
197
        PyEval_SaveThread();
198
    }
199
200
    return buf;
201
}
202
203
#endif
204
205
206
/* Readline implementation using fgets() */
207
208
char *
209
PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
210
0
{
211
0
    size_t n;
212
0
    char *p, *pr;
213
214
#ifdef MS_WINDOWS
215
    if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) {
216
        HANDLE hStdIn, hStdErr;
217
218
        _Py_BEGIN_SUPPRESS_IPH
219
        hStdIn = (HANDLE)_get_osfhandle(fileno(sys_stdin));
220
        hStdErr = (HANDLE)_get_osfhandle(fileno(stderr));
221
        _Py_END_SUPPRESS_IPH
222
223
        if (_get_console_type(hStdIn) == 'r') {
224
            fflush(sys_stdout);
225
            if (prompt) {
226
                if (_get_console_type(hStdErr) == 'w') {
227
                    wchar_t *wbuf;
228
                    int wlen;
229
                    wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
230
                            NULL, 0);
231
                    if (wlen) {
232
                        wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t));
233
                        if (wbuf == NULL) {
234
                            PyErr_NoMemory();
235
                            return NULL;
236
                        }
237
                        wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
238
                                wbuf, wlen);
239
                        if (wlen) {
240
                            DWORD n;
241
                            fflush(stderr);
242
                            /* wlen includes null terminator, so subtract 1 */
243
                            WriteConsoleW(hStdErr, wbuf, wlen - 1, &n, NULL);
244
                        }
245
                        PyMem_RawFree(wbuf);
246
                    }
247
                } else {
248
                    fprintf(stderr, "%s", prompt);
249
                    fflush(stderr);
250
                }
251
            }
252
            clearerr(sys_stdin);
253
            return _PyOS_WindowsConsoleReadline(hStdIn);
254
        }
255
    }
256
#endif
257
258
0
    n = 100;
259
0
    p = (char *)PyMem_RawMalloc(n);
260
0
    if (p == NULL) {
261
0
        PyErr_NoMemory();
262
0
        return NULL;
263
0
    }
264
265
0
    fflush(sys_stdout);
266
0
    if (prompt)
267
0
        fprintf(stderr, "%s", prompt);
268
0
    fflush(stderr);
269
270
0
    switch (my_fgets(p, (int)n, sys_stdin)) {
271
0
    case 0: /* Normal case */
272
0
        break;
273
0
    case 1: /* Interrupt */
274
0
        PyMem_RawFree(p);
275
0
        return NULL;
276
0
    case -1: /* EOF */
277
0
    case -2: /* Error */
278
0
    default: /* Shouldn't happen */
279
0
        *p = '\0';
280
0
        break;
281
0
    }
282
0
    n = strlen(p);
283
0
    while (n > 0 && p[n-1] != '\n') {
284
0
        size_t incr = n+2;
285
0
        if (incr > INT_MAX) {
286
0
            PyMem_RawFree(p);
287
0
            PyErr_SetString(PyExc_OverflowError, "input line too long");
288
0
            return NULL;
289
0
        }
290
0
        pr = (char *)PyMem_RawRealloc(p, n + incr);
291
0
        if (pr == NULL) {
292
0
            PyMem_RawFree(p);
293
0
            PyErr_NoMemory();
294
0
            return NULL;
295
0
        }
296
0
        p = pr;
297
0
        if (my_fgets(p+n, (int)incr, sys_stdin) != 0)
298
0
            break;
299
0
        n += strlen(p+n);
300
0
    }
301
0
    pr = (char *)PyMem_RawRealloc(p, n+1);
302
0
    if (pr == NULL) {
303
0
        PyMem_RawFree(p);
304
0
        PyErr_NoMemory();
305
0
        return NULL;
306
0
    }
307
0
    return pr;
308
0
}
309
310
311
/* By initializing this function pointer, systems embedding Python can
312
   override the readline function.
313
314
   Note: Python expects in return a buffer allocated with PyMem_Malloc. */
315
316
char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) = NULL;
317
318
319
/* Interface used by tokenizer.c and bltinmodule.c */
320
321
char *
322
PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
323
0
{
324
0
    char *rv, *res;
325
0
    size_t len;
326
327
0
    if (_PyOS_ReadlineTState == _PyThreadState_GET()) {
328
0
        PyErr_SetString(PyExc_RuntimeError,
329
0
                        "can't re-enter readline");
330
0
        return NULL;
331
0
    }
332
333
334
0
    if (PyOS_ReadlineFunctionPointer == NULL) {
335
0
        PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
336
0
    }
337
338
0
    if (_PyOS_ReadlineLock == NULL) {
339
0
        _PyOS_ReadlineLock = PyThread_allocate_lock();
340
0
        if (_PyOS_ReadlineLock == NULL) {
341
0
            PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
342
0
            return NULL;
343
0
        }
344
0
    }
345
346
0
    _PyOS_ReadlineTState = _PyThreadState_GET();
347
0
    Py_BEGIN_ALLOW_THREADS
348
0
    PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
349
350
    /* This is needed to handle the unlikely case that the
351
     * interpreter is in interactive mode *and* stdin/out are not
352
     * a tty.  This can happen, for example if python is run like
353
     * this: python -i < test1.py
354
     */
355
0
    if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
356
0
        rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
357
0
    else
358
0
        rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
359
0
                                             prompt);
360
0
    Py_END_ALLOW_THREADS
361
362
0
    PyThread_release_lock(_PyOS_ReadlineLock);
363
364
0
    _PyOS_ReadlineTState = NULL;
365
366
0
    if (rv == NULL)
367
0
        return NULL;
368
369
0
    len = strlen(rv) + 1;
370
0
    res = PyMem_Malloc(len);
371
0
    if (res != NULL) {
372
0
        memcpy(res, rv, len);
373
0
    }
374
0
    else {
375
0
        PyErr_NoMemory();
376
0
    }
377
0
    PyMem_RawFree(rv);
378
379
0
    return res;
380
0
}