diff -r adb6b029b102 Parser/myreadline.c --- a/Parser/myreadline.c Wed Mar 09 15:02:31 2016 +0100 +++ b/Parser/myreadline.c Fri Mar 11 05:59:02 2016 -0600 @@ -13,9 +13,9 @@ #ifdef MS_WINDOWS #define WIN32_LEAN_AND_MEAN #include "windows.h" +extern int winerror_to_errno(int); #endif /* MS_WINDOWS */ - PyThreadState* _PyOS_ReadlineTState; #ifdef WITH_THREAD @@ -25,58 +25,89 @@ int (*PyOS_InputHook)(void) = NULL; +#ifdef MS_WINDOWS +static int +my_cgets(char *buf, int len, int *nRead, HANDLE hInput) +{ + DWORD lasterr, consoleMode; + + if (ReadConsoleA(hInput, buf, len - 1, nRead, NULL) && + *nRead > 0) { + if (*buf == '\x1a' && + GetConsoleMode(hInput, &consoleMode) && + consoleMode & ENABLE_PROCESSED_INPUT) { + *nRead = 0; + } else + buf[*nRead] = '\0'; + return 0; + } + lasterr = GetLastError(); + if (lasterr != ERROR_OPERATION_ABORTED) { + errno = winerror_to_errno(lasterr); + } else { + /* N.B. Ctrl+C or Ctrl+Break anywhere on the line sets the last + error to ERROR_OPERATION_ABORTED. Under normal circumstances + this also causes the SIGINT or SIGBREAK handler to fire, which + sets the event object returned by _PyOS_SigintEvent. + + This signal fires in another thread and is not guaranteed to + have occurred before this point in the code. Therefore check + whether the event is set with a small timeout to provide some + semblance of synchronization. + + BUGBUG: the event is not set for SIGBREAK. + BUGBUG: resetting the event should be implemented in + PyErr_CheckSignals, specifically by calling a + new untrip_signal() function. + */ + HANDLE hInterruptEvent = _PyOS_SigintEvent(); + errno = EINTR; + switch (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)) { + case WAIT_OBJECT_0: + ResetEvent(hInterruptEvent); + } + } + return errno; +} +#endif /* MS_WINDOWS */ + + /* This function restarts a fgets() after an EINTR error occurred except if PyOS_InterruptOccurred() returns true. */ static int my_fgets(char *buf, int len, FILE *fp) { -#ifdef MS_WINDOWS - HANDLE hInterruptEvent; -#endif - char *p; int err; while (1) { if (PyOS_InputHook != NULL) (void)(PyOS_InputHook)(); errno = 0; clearerr(fp); - if (_PyVerify_fd(fileno(fp))) - p = fgets(buf, len, fp); - else - p = NULL; - if (p != NULL) - return 0; /* No error */ + if (_PyVerify_fd(fileno(fp))) { +#ifdef MS_WINDOWS + DWORD consoleMode; + HANDLE hInput = (HANDLE)_get_osfhandle(fileno(fp)); + if (GetConsoleMode(hInput, &consoleMode)) { + int nRead; + err = my_cgets(buf, len, &nRead, hInput); + if (!err) { + if (nRead) + return 0; /* Success */ + return -1; /* EOF */ + } + goto check_signals; + } +#endif /* MS_WINDOWS */ + if (fgets(buf, len, fp)) + return 0; /* Success */ + } err = errno; -#ifdef MS_WINDOWS - /* Ctrl-C anywhere on the line or Ctrl-Z if the only character - on a line will set ERROR_OPERATION_ABORTED. Under normal - circumstances Ctrl-C will also have caused the SIGINT handler - to fire which will have set the event object returned by - _PyOS_SigintEvent. This signal fires in another thread and - is not guaranteed to have occurred before this point in the - code. - - Therefore: check whether the event is set with a small timeout. - If it is, assume this is a Ctrl-C and reset the event. If it - isn't set assume that this is a Ctrl-Z on its own and drop - through to check for EOF. - */ - if (GetLastError()==ERROR_OPERATION_ABORTED) { - hInterruptEvent = _PyOS_SigintEvent(); - switch (WaitForSingleObjectEx(hInterruptEvent, 10, FALSE)) { - case WAIT_OBJECT_0: - ResetEvent(hInterruptEvent); - return 1; /* Interrupt */ - case WAIT_FAILED: - return -2; /* Error */ - } - } -#endif /* MS_WINDOWS */ if (feof(fp)) { clearerr(fp); return -1; /* EOF */ } +check_signals: #ifdef EINTR if (err == EINTR) { int s; @@ -88,17 +119,15 @@ PyEval_SaveThread(); #endif if (s < 0) - return 1; - /* try again */ + return 1; /* Interrupt */ continue; } #endif - if (PyOS_InterruptOccurred()) { + if (PyOS_InterruptOccurred()) return 1; /* Interrupt */ - } return -2; /* Error */ } - /* NOTREACHED */ + /* NOT REACHED */ }