xref: /third_party/python/Parser/myreadline.c (revision 7db96d56)
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
21PyThreadState* _PyOS_ReadlineTState = NULL;
22
23static PyThread_type_lock _PyOS_ReadlineLock = NULL;
24
25int (*PyOS_InputHook)(void) = NULL;
26
27/* This function restarts a fgets() after an EINTR error occurred
28   except if _PyOS_InterruptOccurred() returns true. */
29
30static int
31my_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) {
46        if (PyOS_InputHook != NULL) {
47            (void)(PyOS_InputHook)();
48        }
49
50        errno = 0;
51        clearerr(fp);
52        char *p = fgets(buf, len, fp);
53        if (p != NULL) {
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)) {
85            clearerr(fp);
86            return -1; /* EOF */
87        }
88
89#ifdef EINTR
90        if (err == EINTR) {
91            PyEval_RestoreThread(tstate);
92            int s = PyErr_CheckSignals();
93            PyEval_SaveThread();
94
95            if (s < 0) {
96                return 1;
97            }
98            /* try again */
99            continue;
100        }
101#endif
102
103        if (_PyOS_InterruptOccurred(tstate)) {
104            return 1; /* Interrupt */
105        }
106        return -2; /* Error */
107    }
108    /* NOTREACHED */
109}
110
111#ifdef MS_WINDOWS
112/* Readline implementation using ReadConsoleW */
113
114extern char _get_console_type(HANDLE handle);
115
116char *
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
223exit:
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
241char *
242PyOS_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    if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) {
251        HANDLE hStdIn, hStdErr;
252
253        hStdIn = _Py_get_osfhandle_noraise(fileno(sys_stdin));
254        hStdErr = _Py_get_osfhandle_noraise(fileno(stderr));
255
256        if (_get_console_type(hStdIn) == 'r') {
257            fflush(sys_stdout);
258            if (prompt) {
259                if (_get_console_type(hStdErr) == 'w') {
260                    wchar_t *wbuf;
261                    int wlen;
262                    wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
263                            NULL, 0);
264                    if (wlen) {
265                        wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t));
266                        if (wbuf == NULL) {
267                            PyEval_RestoreThread(tstate);
268                            PyErr_NoMemory();
269                            PyEval_SaveThread();
270                            return NULL;
271                        }
272                        wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
273                                wbuf, wlen);
274                        if (wlen) {
275                            DWORD n;
276                            fflush(stderr);
277                            /* wlen includes null terminator, so subtract 1 */
278                            WriteConsoleW(hStdErr, wbuf, wlen - 1, &n, NULL);
279                        }
280                        PyMem_RawFree(wbuf);
281                    }
282                } else {
283                    fprintf(stderr, "%s", prompt);
284                    fflush(stderr);
285                }
286            }
287            clearerr(sys_stdin);
288            return _PyOS_WindowsConsoleReadline(tstate, hStdIn);
289        }
290    }
291#endif
292
293    fflush(sys_stdout);
294    if (prompt) {
295        fprintf(stderr, "%s", prompt);
296    }
297    fflush(stderr);
298
299    n = 0;
300    p = NULL;
301    do {
302        size_t incr = (n > 0) ? n + 2 : 100;
303        if (incr > INT_MAX) {
304            PyMem_RawFree(p);
305            PyEval_RestoreThread(tstate);
306            PyErr_SetString(PyExc_OverflowError, "input line too long");
307            PyEval_SaveThread();
308            return NULL;
309        }
310        pr = (char *)PyMem_RawRealloc(p, n + incr);
311        if (pr == NULL) {
312            PyMem_RawFree(p);
313            PyEval_RestoreThread(tstate);
314            PyErr_NoMemory();
315            PyEval_SaveThread();
316            return NULL;
317        }
318        p = pr;
319        int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
320        if (err == 1) {
321            // Interrupt
322            PyMem_RawFree(p);
323            return NULL;
324        } else if (err != 0) {
325            // EOF or error
326            p[n] = '\0';
327            break;
328        }
329        n += strlen(p + n);
330    } while (p[n-1] != '\n');
331
332    pr = (char *)PyMem_RawRealloc(p, n+1);
333    if (pr == NULL) {
334        PyMem_RawFree(p);
335        PyEval_RestoreThread(tstate);
336        PyErr_NoMemory();
337        PyEval_SaveThread();
338        return NULL;
339    }
340    return pr;
341}
342
343
344/* By initializing this function pointer, systems embedding Python can
345   override the readline function.
346
347   Note: Python expects in return a buffer allocated with PyMem_Malloc. */
348
349char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) = NULL;
350
351
352/* Interface used by tokenizer.c and bltinmodule.c */
353
354char *
355PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
356{
357    char *rv, *res;
358    size_t len;
359
360    PyThreadState *tstate = _PyThreadState_GET();
361    if (_PyOS_ReadlineTState == tstate) {
362        PyErr_SetString(PyExc_RuntimeError,
363                        "can't re-enter readline");
364        return NULL;
365    }
366
367
368    if (PyOS_ReadlineFunctionPointer == NULL) {
369        PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
370    }
371
372    if (_PyOS_ReadlineLock == NULL) {
373        _PyOS_ReadlineLock = PyThread_allocate_lock();
374        if (_PyOS_ReadlineLock == NULL) {
375            PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
376            return NULL;
377        }
378    }
379
380    _PyOS_ReadlineTState = tstate;
381    Py_BEGIN_ALLOW_THREADS
382    PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
383
384    /* This is needed to handle the unlikely case that the
385     * interpreter is in interactive mode *and* stdin/out are not
386     * a tty.  This can happen, for example if python is run like
387     * this: python -i < test1.py
388     */
389    if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
390        rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
391    else
392        rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
393                                             prompt);
394    Py_END_ALLOW_THREADS
395
396    PyThread_release_lock(_PyOS_ReadlineLock);
397
398    _PyOS_ReadlineTState = NULL;
399
400    if (rv == NULL)
401        return NULL;
402
403    len = strlen(rv) + 1;
404    res = PyMem_Malloc(len);
405    if (res != NULL) {
406        memcpy(res, rv, len);
407    }
408    else {
409        PyErr_NoMemory();
410    }
411    PyMem_RawFree(rv);
412
413    return res;
414}
415