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