Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(26426)

Side by Side Diff: Parser/myreadline.c

Issue 18597: On Windows sys.stdin.readline() doesn't handle Ctrl-C properly
Patch Set: Created 3 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 1
2 /* Readline interface for tokenizer.c and [raw_]input() in bltinmodule.c. 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 3 By default, or when stdin is not a tty device, we have a super
4 simple my_readline function using fgets. 4 simple my_readline function using fgets.
5 Optionally, we can use the GNU readline library. 5 Optionally, we can use the GNU readline library.
6 my_readline() has a different return value from GNU readline(): 6 my_readline() has a different return value from GNU readline():
7 - NULL if an interrupt occurred or if an error occurred 7 - NULL if an interrupt occurred or if an error occurred
8 - a malloc'ed empty string if EOF was read 8 - a malloc'ed empty string if EOF was read
9 - a malloc'ed string ending in \n normally 9 - a malloc'ed string ending in \n normally
10 */ 10 */
11 11
12 #include "Python.h" 12 #include "Python.h"
13 #ifdef MS_WINDOWS 13 #ifdef MS_WINDOWS
14 #define WIN32_LEAN_AND_MEAN 14 #define WIN32_LEAN_AND_MEAN
15 #include "windows.h" 15 #include "windows.h"
16 extern int winerror_to_errno(int);
16 #endif /* MS_WINDOWS */ 17 #endif /* MS_WINDOWS */
17
18 18
19 PyThreadState* _PyOS_ReadlineTState; 19 PyThreadState* _PyOS_ReadlineTState;
20 20
21 #ifdef WITH_THREAD 21 #ifdef WITH_THREAD
22 #include "pythread.h" 22 #include "pythread.h"
23 static PyThread_type_lock _PyOS_ReadlineLock = NULL; 23 static PyThread_type_lock _PyOS_ReadlineLock = NULL;
24 #endif 24 #endif
25 25
26 int (*PyOS_InputHook)(void) = NULL; 26 int (*PyOS_InputHook)(void) = NULL;
27
28 #ifdef MS_WINDOWS
29 static int
30 my_cgets(char *buf, int len, int *nRead, HANDLE hInput)
31 {
32 DWORD lasterr, consoleMode;
33
34 if (ReadConsoleA(hInput, buf, len - 1, nRead, NULL) &&
35 *nRead > 0) {
36 if (*buf == '\x1a' &&
37 GetConsoleMode(hInput, &consoleMode) &&
38 consoleMode & ENABLE_PROCESSED_INPUT) {
39 *nRead = 0;
40 } else
41 buf[*nRead] = '\0';
42 return 0;
43 }
44 lasterr = GetLastError();
45 if (lasterr != ERROR_OPERATION_ABORTED) {
46 errno = winerror_to_errno(lasterr);
47 } else {
48 /* N.B. Ctrl+C or Ctrl+Break anywhere on the line sets the last
49 error to ERROR_OPERATION_ABORTED. Under normal circumstances
50 this also causes the SIGINT or SIGBREAK handler to fire, which
51 sets the event object returned by _PyOS_SigintEvent.
52
53 This signal fires in another thread and is not guaranteed to
54 have occurred before this point in the code. Therefore check
55 whether the event is set with a small timeout to provide some
56 semblance of synchronization.
57
58 BUGBUG: the event is not set for SIGBREAK.
59 BUGBUG: resetting the event should be implemented in
60 PyErr_CheckSignals, specifically by calling a
61 new untrip_signal() function.
62 */
63 HANDLE hInterruptEvent = _PyOS_SigintEvent();
64 errno = EINTR;
65 switch (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)) {
66 case WAIT_OBJECT_0:
67 ResetEvent(hInterruptEvent);
68 }
69 }
70 return errno;
71 }
72 #endif /* MS_WINDOWS */
73
27 74
28 /* This function restarts a fgets() after an EINTR error occurred 75 /* This function restarts a fgets() after an EINTR error occurred
29 except if PyOS_InterruptOccurred() returns true. */ 76 except if PyOS_InterruptOccurred() returns true. */
30 77
31 static int 78 static int
32 my_fgets(char *buf, int len, FILE *fp) 79 my_fgets(char *buf, int len, FILE *fp)
33 { 80 {
34 #ifdef MS_WINDOWS
35 HANDLE hInterruptEvent;
36 #endif
37 char *p;
38 int err; 81 int err;
39 while (1) { 82 while (1) {
40 if (PyOS_InputHook != NULL) 83 if (PyOS_InputHook != NULL)
41 (void)(PyOS_InputHook)(); 84 (void)(PyOS_InputHook)();
42 errno = 0; 85 errno = 0;
43 clearerr(fp); 86 clearerr(fp);
44 if (_PyVerify_fd(fileno(fp))) 87 if (_PyVerify_fd(fileno(fp))) {
45 p = fgets(buf, len, fp); 88 #ifdef MS_WINDOWS
46 else 89 DWORD consoleMode;
47 p = NULL; 90 HANDLE hInput = (HANDLE)_get_osfhandle(fileno(fp));
48 if (p != NULL) 91 if (GetConsoleMode(hInput, &consoleMode)) {
49 return 0; /* No error */ 92 int nRead;
93 err = my_cgets(buf, len, &nRead, hInput);
94 if (!err) {
95 if (nRead)
96 return 0; /* Success */
97 return -1; /* EOF */
98 }
99 goto check_signals;
100 }
101 #endif /* MS_WINDOWS */
102 if (fgets(buf, len, fp))
103 return 0; /* Success */
104 }
50 err = errno; 105 err = errno;
51 #ifdef MS_WINDOWS
52 /* Ctrl-C anywhere on the line or Ctrl-Z if the only character
53 on a line will set ERROR_OPERATION_ABORTED. Under normal
54 circumstances Ctrl-C will also have caused the SIGINT handler
55 to fire which will have set the event object returned by
56 _PyOS_SigintEvent. This signal fires in another thread and
57 is not guaranteed to have occurred before this point in the
58 code.
59
60 Therefore: check whether the event is set with a small timeout.
61 If it is, assume this is a Ctrl-C and reset the event. If it
62 isn't set assume that this is a Ctrl-Z on its own and drop
63 through to check for EOF.
64 */
65 if (GetLastError()==ERROR_OPERATION_ABORTED) {
66 hInterruptEvent = _PyOS_SigintEvent();
67 switch (WaitForSingleObjectEx(hInterruptEvent, 10, FALSE)) {
68 case WAIT_OBJECT_0:
69 ResetEvent(hInterruptEvent);
70 return 1; /* Interrupt */
71 case WAIT_FAILED:
72 return -2; /* Error */
73 }
74 }
75 #endif /* MS_WINDOWS */
76 if (feof(fp)) { 106 if (feof(fp)) {
77 clearerr(fp); 107 clearerr(fp);
78 return -1; /* EOF */ 108 return -1; /* EOF */
79 } 109 }
110 check_signals:
80 #ifdef EINTR 111 #ifdef EINTR
81 if (err == EINTR) { 112 if (err == EINTR) {
82 int s; 113 int s;
83 #ifdef WITH_THREAD 114 #ifdef WITH_THREAD
84 PyEval_RestoreThread(_PyOS_ReadlineTState); 115 PyEval_RestoreThread(_PyOS_ReadlineTState);
85 #endif 116 #endif
86 s = PyErr_CheckSignals(); 117 s = PyErr_CheckSignals();
87 #ifdef WITH_THREAD 118 #ifdef WITH_THREAD
88 PyEval_SaveThread(); 119 PyEval_SaveThread();
89 #endif 120 #endif
90 if (s < 0) 121 if (s < 0)
91 return 1; 122 return 1; /* Interrupt */
92 /* try again */
93 continue; 123 continue;
94 } 124 }
95 #endif 125 #endif
96 if (PyOS_InterruptOccurred()) { 126 if (PyOS_InterruptOccurred())
97 return 1; /* Interrupt */ 127 return 1; /* Interrupt */
98 }
99 return -2; /* Error */ 128 return -2; /* Error */
100 } 129 }
101 /* NOTREACHED */ 130 /* NOT REACHED */
102 } 131 }
103 132
104 133
105 /* Readline implementation using fgets() */ 134 /* Readline implementation using fgets() */
106 135
107 char * 136 char *
108 PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt) 137 PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
109 { 138 {
110 size_t n; 139 size_t n;
111 char *p, *pr; 140 char *p, *pr;
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 return NULL; 251 return NULL;
223 252
224 len = strlen(rv) + 1; 253 len = strlen(rv) + 1;
225 res = PyMem_Malloc(len); 254 res = PyMem_Malloc(len);
226 if (res != NULL) 255 if (res != NULL)
227 memcpy(res, rv, len); 256 memcpy(res, rv, len);
228 PyMem_RawFree(rv); 257 PyMem_RawFree(rv);
229 258
230 return res; 259 return res;
231 } 260 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+