Branch data Line data Source code
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 : 52 : 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 : 0 : while (1) {
46 [ - + ]: 52 : if (PyOS_InputHook != NULL) {
47 : 0 : (void)(PyOS_InputHook)();
48 : : }
49 : :
50 : 52 : errno = 0;
51 : 52 : clearerr(fp);
52 : 52 : char *p = fgets(buf, len, fp);
53 [ + + ]: 52 : if (p != NULL) {
54 : 45 : return 0; /* No error */
55 : : }
56 : 7 : 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 [ + + ]: 7 : if (feof(fp)) {
85 : 6 : clearerr(fp);
86 : 6 : return -1; /* EOF */
87 : : }
88 : :
89 : : #ifdef EINTR
90 [ - + ]: 1 : if (err == EINTR) {
91 : 0 : PyEval_RestoreThread(tstate);
92 : 0 : int s = PyErr_CheckSignals();
93 : 0 : PyEval_SaveThread();
94 : :
95 [ # # ]: 0 : if (s < 0) {
96 : 0 : return 1;
97 : : }
98 : : /* try again */
99 : 0 : continue;
100 : : }
101 : : #endif
102 : :
103 [ - + ]: 1 : if (_PyOS_InterruptOccurred(tstate)) {
104 : 0 : return 1; /* Interrupt */
105 : : }
106 : 1 : 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 : 52 : PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
243 : : {
244 : : size_t n;
245 : : char *p, *pr;
246 : 52 : 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 : 52 : fflush(sys_stdout);
295 [ + - ]: 52 : if (prompt) {
296 : 52 : fprintf(stderr, "%s", prompt);
297 : : }
298 : 52 : fflush(stderr);
299 : :
300 : 52 : n = 0;
301 : 52 : p = NULL;
302 : : do {
303 [ - + ]: 52 : size_t incr = (n > 0) ? n + 2 : 100;
304 [ - + ]: 52 : if (incr > INT_MAX) {
305 : 0 : PyMem_RawFree(p);
306 : 0 : PyEval_RestoreThread(tstate);
307 : 0 : PyErr_SetString(PyExc_OverflowError, "input line too long");
308 : 0 : PyEval_SaveThread();
309 : 0 : return NULL;
310 : : }
311 : 52 : pr = (char *)PyMem_RawRealloc(p, n + incr);
312 [ - + ]: 52 : if (pr == NULL) {
313 : 0 : PyMem_RawFree(p);
314 : 0 : PyEval_RestoreThread(tstate);
315 : : PyErr_NoMemory();
316 : 0 : PyEval_SaveThread();
317 : 0 : return NULL;
318 : : }
319 : 52 : p = pr;
320 : 52 : int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
321 [ - + ]: 52 : if (err == 1) {
322 : : // Interrupt
323 : 0 : PyMem_RawFree(p);
324 : 0 : return NULL;
325 [ + + ]: 52 : } else if (err != 0) {
326 : : // EOF or error
327 : 7 : p[n] = '\0';
328 : 7 : break;
329 : : }
330 : 45 : n += strlen(p + n);
331 [ - + ]: 45 : } while (p[n-1] != '\n');
332 : :
333 : 52 : pr = (char *)PyMem_RawRealloc(p, n+1);
334 [ - + ]: 52 : if (pr == NULL) {
335 : 0 : PyMem_RawFree(p);
336 : 0 : PyEval_RestoreThread(tstate);
337 : : PyErr_NoMemory();
338 : 0 : PyEval_SaveThread();
339 : 0 : return NULL;
340 : : }
341 : 52 : 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 : 56 : PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
357 : : {
358 : : char *rv, *res;
359 : : size_t len;
360 : :
361 : 56 : PyThreadState *tstate = _PyThreadState_GET();
362 [ - + ]: 56 : if (_PyOS_ReadlineTState == tstate) {
363 : 0 : PyErr_SetString(PyExc_RuntimeError,
364 : : "can't re-enter readline");
365 : 0 : return NULL;
366 : : }
367 : :
368 : :
369 [ - + ]: 56 : if (PyOS_ReadlineFunctionPointer == NULL) {
370 : 0 : PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
371 : : }
372 : :
373 [ + + ]: 56 : if (_PyOS_ReadlineLock == NULL) {
374 : 16 : _PyOS_ReadlineLock = PyThread_allocate_lock();
375 [ - + ]: 16 : if (_PyOS_ReadlineLock == NULL) {
376 : 0 : PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
377 : 0 : return NULL;
378 : : }
379 : : }
380 : :
381 : 56 : _PyOS_ReadlineTState = tstate;
382 : 56 : Py_BEGIN_ALLOW_THREADS
383 : 56 : 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 [ + + - + ]: 56 : if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
391 : 52 : rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
392 : : else
393 : 4 : rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
394 : : prompt);
395 : 56 : Py_END_ALLOW_THREADS
396 : :
397 : 56 : PyThread_release_lock(_PyOS_ReadlineLock);
398 : :
399 : 56 : _PyOS_ReadlineTState = NULL;
400 : :
401 [ - + ]: 56 : if (rv == NULL)
402 : 0 : return NULL;
403 : :
404 : 56 : len = strlen(rv) + 1;
405 : 56 : res = PyMem_Malloc(len);
406 [ + - ]: 56 : if (res != NULL) {
407 : 56 : memcpy(res, rv, len);
408 : : }
409 : : else {
410 : : PyErr_NoMemory();
411 : : }
412 : 56 : PyMem_RawFree(rv);
413 : :
414 : 56 : return res;
415 : : }
|