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