• 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"
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
my_fgets(PyThreadState * tstate,char * buf,int len,FILE * fp)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) {
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 
114 extern char _get_console_type(HANDLE handle);
115 
116 char *
_PyOS_WindowsConsoleReadline(PyThreadState * tstate,HANDLE hStdIn)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 *
PyOS_StdioReadline(FILE * sys_stdin,FILE * sys_stdout,const char * prompt)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     if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) {
251         HANDLE hStdIn, hStdErr;
252 
253         _Py_BEGIN_SUPPRESS_IPH
254         hStdIn = (HANDLE)_get_osfhandle(fileno(sys_stdin));
255         hStdErr = (HANDLE)_get_osfhandle(fileno(stderr));
256         _Py_END_SUPPRESS_IPH
257 
258         if (_get_console_type(hStdIn) == 'r') {
259             fflush(sys_stdout);
260             if (prompt) {
261                 if (_get_console_type(hStdErr) == 'w') {
262                     wchar_t *wbuf;
263                     int wlen;
264                     wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
265                             NULL, 0);
266                     if (wlen) {
267                         wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t));
268                         if (wbuf == NULL) {
269                             PyEval_RestoreThread(tstate);
270                             PyErr_NoMemory();
271                             PyEval_SaveThread();
272                             return NULL;
273                         }
274                         wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
275                                 wbuf, wlen);
276                         if (wlen) {
277                             DWORD n;
278                             fflush(stderr);
279                             /* wlen includes null terminator, so subtract 1 */
280                             WriteConsoleW(hStdErr, wbuf, wlen - 1, &n, NULL);
281                         }
282                         PyMem_RawFree(wbuf);
283                     }
284                 } else {
285                     fprintf(stderr, "%s", prompt);
286                     fflush(stderr);
287                 }
288             }
289             clearerr(sys_stdin);
290             return _PyOS_WindowsConsoleReadline(tstate, hStdIn);
291         }
292     }
293 #endif
294 
295     n = 100;
296     p = (char *)PyMem_RawMalloc(n);
297     if (p == NULL) {
298         PyEval_RestoreThread(tstate);
299         PyErr_NoMemory();
300         PyEval_SaveThread();
301         return NULL;
302     }
303 
304     fflush(sys_stdout);
305     if (prompt) {
306         fprintf(stderr, "%s", prompt);
307     }
308     fflush(stderr);
309 
310     switch (my_fgets(tstate, p, (int)n, sys_stdin)) {
311     case 0: /* Normal case */
312         break;
313     case 1: /* Interrupt */
314         PyMem_RawFree(p);
315         return NULL;
316     case -1: /* EOF */
317     case -2: /* Error */
318     default: /* Shouldn't happen */
319         *p = '\0';
320         break;
321     }
322 
323     n = strlen(p);
324     while (n > 0 && p[n-1] != '\n') {
325         size_t incr = n+2;
326         if (incr > INT_MAX) {
327             PyMem_RawFree(p);
328             PyEval_RestoreThread(tstate);
329             PyErr_SetString(PyExc_OverflowError, "input line too long");
330             PyEval_SaveThread();
331             return NULL;
332         }
333 
334         pr = (char *)PyMem_RawRealloc(p, n + incr);
335         if (pr == NULL) {
336             PyMem_RawFree(p);
337             PyEval_RestoreThread(tstate);
338             PyErr_NoMemory();
339             PyEval_SaveThread();
340             return NULL;
341         }
342         p = pr;
343 
344         if (my_fgets(tstate, p+n, (int)incr, sys_stdin) != 0) {
345             break;
346         }
347         n += strlen(p+n);
348     }
349 
350     pr = (char *)PyMem_RawRealloc(p, n+1);
351     if (pr == NULL) {
352         PyMem_RawFree(p);
353         PyEval_RestoreThread(tstate);
354         PyErr_NoMemory();
355         PyEval_SaveThread();
356         return NULL;
357     }
358     return pr;
359 }
360 
361 
362 /* By initializing this function pointer, systems embedding Python can
363    override the readline function.
364 
365    Note: Python expects in return a buffer allocated with PyMem_Malloc. */
366 
367 char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) = NULL;
368 
369 
370 /* Interface used by tokenizer.c and bltinmodule.c */
371 
372 char *
PyOS_Readline(FILE * sys_stdin,FILE * sys_stdout,const char * prompt)373 PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
374 {
375     char *rv, *res;
376     size_t len;
377 
378     PyThreadState *tstate = _PyThreadState_GET();
379     if (_PyOS_ReadlineTState == tstate) {
380         PyErr_SetString(PyExc_RuntimeError,
381                         "can't re-enter readline");
382         return NULL;
383     }
384 
385 
386     if (PyOS_ReadlineFunctionPointer == NULL) {
387         PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
388     }
389 
390     if (_PyOS_ReadlineLock == NULL) {
391         _PyOS_ReadlineLock = PyThread_allocate_lock();
392         if (_PyOS_ReadlineLock == NULL) {
393             PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
394             return NULL;
395         }
396     }
397 
398     _PyOS_ReadlineTState = tstate;
399     Py_BEGIN_ALLOW_THREADS
400     PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
401 
402     /* This is needed to handle the unlikely case that the
403      * interpreter is in interactive mode *and* stdin/out are not
404      * a tty.  This can happen, for example if python is run like
405      * this: python -i < test1.py
406      */
407     if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
408         rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
409     else
410         rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
411                                              prompt);
412     Py_END_ALLOW_THREADS
413 
414     PyThread_release_lock(_PyOS_ReadlineLock);
415 
416     _PyOS_ReadlineTState = NULL;
417 
418     if (rv == NULL)
419         return NULL;
420 
421     len = strlen(rv) + 1;
422     res = PyMem_Malloc(len);
423     if (res != NULL) {
424         memcpy(res, rv, len);
425     }
426     else {
427         PyErr_NoMemory();
428     }
429     PyMem_RawFree(rv);
430 
431     return res;
432 }
433