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 _Py_BEGIN_SUPPRESS_IPH
253 hStdIn = (HANDLE)_get_osfhandle(fileno(sys_stdin));
254 hStdErr = (HANDLE)_get_osfhandle(fileno(stderr));
255 _Py_END_SUPPRESS_IPH
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 fflush(sys_stdout);
295 if (prompt) {
296 fprintf(stderr, "%s", prompt);
297 }
298 fflush(stderr);
299
300 n = 0;
301 p = NULL;
302 do {
303 size_t incr = (n > 0) ? n + 2 : 100;
304 if (incr > INT_MAX) {
305 PyMem_RawFree(p);
306 PyEval_RestoreThread(tstate);
307 PyErr_SetString(PyExc_OverflowError, "input line too long");
308 PyEval_SaveThread();
309 return NULL;
310 }
311 pr = (char *)PyMem_RawRealloc(p, n + incr);
312 if (pr == NULL) {
313 PyMem_RawFree(p);
314 PyEval_RestoreThread(tstate);
315 PyErr_NoMemory();
316 PyEval_SaveThread();
317 return NULL;
318 }
319 p = pr;
320 int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
321 if (err == 1) {
322 // Interrupt
323 PyMem_RawFree(p);
324 return NULL;
325 } else if (err != 0) {
326 // EOF or error
327 p[n] = '\0';
328 break;
329 }
330 n += strlen(p + n);
331 } while (p[n-1] != '\n');
332
333 pr = (char *)PyMem_RawRealloc(p, n+1);
334 if (pr == NULL) {
335 PyMem_RawFree(p);
336 PyEval_RestoreThread(tstate);
337 PyErr_NoMemory();
338 PyEval_SaveThread();
339 return NULL;
340 }
341 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 *
PyOS_Readline(FILE * sys_stdin,FILE * sys_stdout,const char * prompt)356 PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
357 {
358 char *rv, *res;
359 size_t len;
360
361 PyThreadState *tstate = _PyThreadState_GET();
362 if (_PyOS_ReadlineTState == tstate) {
363 PyErr_SetString(PyExc_RuntimeError,
364 "can't re-enter readline");
365 return NULL;
366 }
367
368
369 if (PyOS_ReadlineFunctionPointer == NULL) {
370 PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
371 }
372
373 if (_PyOS_ReadlineLock == NULL) {
374 _PyOS_ReadlineLock = PyThread_allocate_lock();
375 if (_PyOS_ReadlineLock == NULL) {
376 PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
377 return NULL;
378 }
379 }
380
381 _PyOS_ReadlineTState = tstate;
382 Py_BEGIN_ALLOW_THREADS
383 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 if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
391 rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
392 else
393 rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
394 prompt);
395 Py_END_ALLOW_THREADS
396
397 PyThread_release_lock(_PyOS_ReadlineLock);
398
399 _PyOS_ReadlineTState = NULL;
400
401 if (rv == NULL)
402 return NULL;
403
404 len = strlen(rv) + 1;
405 res = PyMem_Malloc(len);
406 if (res != NULL) {
407 memcpy(res, rv, len);
408 }
409 else {
410 PyErr_NoMemory();
411 }
412 PyMem_RawFree(rv);
413
414 return res;
415 }
416