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 fflush(sys_stdout);
296 if (prompt) {
297 fprintf(stderr, "%s", prompt);
298 }
299 fflush(stderr);
300
301 n = 0;
302 p = NULL;
303 do {
304 size_t incr = (n > 0) ? n + 2 : 100;
305 if (incr > INT_MAX) {
306 PyMem_RawFree(p);
307 PyEval_RestoreThread(tstate);
308 PyErr_SetString(PyExc_OverflowError, "input line too long");
309 PyEval_SaveThread();
310 return NULL;
311 }
312 pr = (char *)PyMem_RawRealloc(p, n + incr);
313 if (pr == NULL) {
314 PyMem_RawFree(p);
315 PyEval_RestoreThread(tstate);
316 PyErr_NoMemory();
317 PyEval_SaveThread();
318 return NULL;
319 }
320 p = pr;
321 int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
322 if (err == 1) {
323 // Interrupt
324 PyMem_RawFree(p);
325 return NULL;
326 } else if (err != 0) {
327 // EOF or error
328 p[n] = '\0';
329 break;
330 }
331 n += strlen(p + n);
332 } while (p[n-1] != '\n');
333
334 pr = (char *)PyMem_RawRealloc(p, n+1);
335 if (pr == NULL) {
336 PyMem_RawFree(p);
337 PyEval_RestoreThread(tstate);
338 PyErr_NoMemory();
339 PyEval_SaveThread();
340 return NULL;
341 }
342 return pr;
343 }
344
345
346 /* By initializing this function pointer, systems embedding Python can
347 override the readline function.
348
349 Note: Python expects in return a buffer allocated with PyMem_Malloc. */
350
351 char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) = NULL;
352
353
354 /* Interface used by tokenizer.c and bltinmodule.c */
355
356 char *
PyOS_Readline(FILE * sys_stdin,FILE * sys_stdout,const char * prompt)357 PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
358 {
359 char *rv, *res;
360 size_t len;
361
362 PyThreadState *tstate = _PyThreadState_GET();
363 if (_PyOS_ReadlineTState == tstate) {
364 PyErr_SetString(PyExc_RuntimeError,
365 "can't re-enter readline");
366 return NULL;
367 }
368
369
370 if (PyOS_ReadlineFunctionPointer == NULL) {
371 PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
372 }
373
374 if (_PyOS_ReadlineLock == NULL) {
375 _PyOS_ReadlineLock = PyThread_allocate_lock();
376 if (_PyOS_ReadlineLock == NULL) {
377 PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
378 return NULL;
379 }
380 }
381
382 _PyOS_ReadlineTState = tstate;
383 Py_BEGIN_ALLOW_THREADS
384 PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
385
386 /* This is needed to handle the unlikely case that the
387 * interpreter is in interactive mode *and* stdin/out are not
388 * a tty. This can happen, for example if python is run like
389 * this: python -i < test1.py
390 */
391 if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
392 rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
393 else
394 rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
395 prompt);
396 Py_END_ALLOW_THREADS
397
398 PyThread_release_lock(_PyOS_ReadlineLock);
399
400 _PyOS_ReadlineTState = NULL;
401
402 if (rv == NULL)
403 return NULL;
404
405 len = strlen(rv) + 1;
406 res = PyMem_Malloc(len);
407 if (res != NULL) {
408 memcpy(res, rv, len);
409 }
410 else {
411 PyErr_NoMemory();
412 }
413 PyMem_RawFree(rv);
414
415 return res;
416 }
417