Coverage Report

Created: 2022-07-08 09:39

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