diff --git a/Parser/myreadline.c b/Parser/myreadline.c --- a/Parser/myreadline.c +++ b/Parser/myreadline.c @@ -10,6 +10,9 @@ */ #include "Python.h" +#ifdef Py_PYPORT_H +# include "signal.h" +#endif #ifdef MS_WINDOWS #define WIN32_LEAN_AND_MEAN #include "windows.h" @@ -29,117 +32,187 @@ int (*PyOS_InputHook)(void) = NULL; -/* This function restarts a fgets() after an EINTR error occurred +/* This function restarts a fgetc() after an EINTR error occurred except if PyOS_InterruptOccurred() returns true. */ +static int +my_fgets(char **store, FILE *fp) +{ + int estat; + char *buf, *cursor; + size_t buf_len; -static int -my_fgets(char *buf, int len, FILE *fp) -{ - char *p; + buf = (char*)PyMem_MALLOC(2*80); + estat = 1; + if (buf == NULL) + goto jreturn; + + cursor = buf; + buf_len = 2*80 - 2; +jrestart_input: + estat = 0; + + if (PyOS_InputHook != NULL) + (void)(PyOS_InputHook)(); + + /* Fetch bytes until error or newline */ + errno = 0; while (1) { - if (PyOS_InputHook != NULL) - (void)(PyOS_InputHook)(); - errno = 0; - p = fgets(buf, len, fp); - if (p != NULL) - return 0; /* No error */ + int c = fgetc(fp); +#ifdef Py_PYPORT_H + if (!isprint(c)) + switch (c) { + case '': + c = EOF; + /* FALLTHROUGH */ + default: + break; + case '': + estat = SIGINT; + goto j_sigit; + case '': + estat = SIGTSTP; + goto j_sigit; + case '': + estat = SIGQUIT; + /* FALLTHROUGH */ +j_sigit: kill(getpid(), estat); + errno = EINTR; + goto jcheck_fail; + } +#endif + if (c == EOF) + goto jcheck_fail; + *(cursor++) = (char)c; + if (c == '\n') + break; + + if ((size_t)(cursor-buf) >= buf_len) { + buf_len += 2+32; + cursor = buf = (char*)PyMem_REALLOC(buf, buf_len); + if (buf == NULL) { + estat = 1; + goto jreturn; + } + buf_len -= 2+32; + cursor += buf_len; + buf_len += 32; + } + } + + *cursor = '\0'; + *store = buf; +jreturn: + return estat; + +jcheck_fail: #ifdef MS_WINDOWS - /* In the case of a Ctrl+C or some other external event - interrupting the operation: - Win2k/NT: ERROR_OPERATION_ABORTED is the most recent Win32 - error code (and feof() returns TRUE). - Win9x: Ctrl+C seems to have no effect on fgets() returning - early - the signal handler is called, but the fgets() - only returns "normally" (ie, when Enter hit or feof()) + /* In the case of a Ctrl+C or some other external event + interrupting the operation: + Win2k/NT: ERROR_OPERATION_ABORTED is the most recent Win32 + error code (and feof() returns TRUE). + Win9x: Ctrl+C seems to have no effect on fgets() returning + early - the signal handler is called, but the fgets() + only returns "normally" (ie, when Enter hit or feof()) + */ + if (GetLastError()==ERROR_OPERATION_ABORTED) { + /* Signals come asynchronously, so we sleep a brief + moment before checking if the handler has been + triggered (we cant just return 1 before the + signal handler has been called, as the later + signal may be treated as a separate interrupt). */ - if (GetLastError()==ERROR_OPERATION_ABORTED) { - /* Signals come asynchronously, so we sleep a brief - moment before checking if the handler has been - triggered (we cant just return 1 before the - signal handler has been called, as the later - signal may be treated as a separate interrupt). - */ - Sleep(1); - if (PyOS_InterruptOccurred()) { - return 1; /* Interrupt */ - } - /* Either the sleep wasn't long enough (need a - short loop retrying?) or not interrupted at all - (in which case we should revisit the whole thing!) - Logging some warning would be nice. assert is not - viable as under the debugger, the various dialogs - mean the condition is not true. - */ + Sleep(1); + if (PyOS_InterruptOccurred()) { + estat = 1; + goto jfail; } + /* Either the sleep wasn't long enough (need a + short loop retrying?) or not interrupted at all + (in which case we should revisit the whole thing!) + Logging some warning would be nice. assert is not + viable as under the debugger, the various dialogs + mean the condition is not true. + */ + } #endif /* MS_WINDOWS */ - if (feof(fp)) { - return -1; /* EOF */ + +#ifndef Py_PYPORT_H + if (feof(fp)) { + estat = -1; /* EOF */ + goto jfail; + } +#endif +#ifdef EINTR + if (errno == EINTR) { +# ifdef WITH_THREAD + PyEval_RestoreThread(_PyOS_ReadlineTState); +# endif + estat = PyErr_CheckSignals(); +# ifdef WITH_THREAD + PyEval_SaveThread(); +# endif + if (estat < 0) { + estat = 1; + goto jfail; } -#ifdef EINTR - if (errno == EINTR) { - int s; -#ifdef WITH_THREAD - PyEval_RestoreThread(_PyOS_ReadlineTState); + /* EINTR is restarted */ + goto jrestart_input; + } #endif - s = PyErr_CheckSignals(); -#ifdef WITH_THREAD - PyEval_SaveThread(); -#endif - if (s < 0) - return 1; - /* try again */ - continue; - } -#endif - if (PyOS_InterruptOccurred()) { - return 1; /* Interrupt */ - } - return -2; /* Error */ + + if (PyOS_InterruptOccurred()) { + estat = 1; /* Interrupt */ + goto jfail; } - /* NOTREACHED */ + estat = -2; /* Error */ + /* FALLTHROUGH */ + +jfail: + PyMem_Free(buf); + goto jreturn; } - -/* Readline implementation using fgets() */ - +/* Readline implementation using my_fgets() */ char * PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) { - size_t n; - char *p; - n = 100; - if ((p = (char *)PyMem_MALLOC(n)) == NULL) - return NULL; + auto char *result = NULL; +#ifdef Py_PYPORT_H + auto struct termios told, tnew; + int infd = fileno(sys_stdin); + + while (tcgetattr(infd, &told) != 0) + ; + memcpy(&tnew, &told, sizeof(told)); + tnew.c_lflag &= ~(ECHOCTL | ICANON); + tnew.c_cc[VMIN] = 1; + while (tcsetattr(infd, TCSAFLUSH, &tnew) != 0) + ; +#endif + fflush(sys_stdout); if (prompt) fprintf(stderr, "%s", prompt); fflush(stderr); - switch (my_fgets(p, (int)n, sys_stdin)) { + + switch (my_fgets(&result, sys_stdin)) { case 0: /* Normal case */ + case 1: /* Interrupt */ break; - case 1: /* Interrupt */ - PyMem_FREE(p); - return NULL; case -1: /* EOF */ case -2: /* Error */ default: /* Shouldn't happen */ - *p = '\0'; + result = (char*)PyMem_MALLOC(sizeof(void*)); + if (result != NULL) + *result = '\0'; break; } - n = strlen(p); - while (n > 0 && p[n-1] != '\n') { - size_t incr = n+2; - p = (char *)PyMem_REALLOC(p, n + incr); - if (p == NULL) - return NULL; - if (incr > INT_MAX) { - PyErr_SetString(PyExc_OverflowError, "input line too long"); - } - if (my_fgets(p+n, (int)incr, sys_stdin) != 0) - break; - n += strlen(p+n); - } - return (char *)PyMem_REALLOC(p, n+1); + +#ifdef Py_PYPORT_H + while (tcsetattr(infd, TCSANOW, &told) != 0) + ; +#endif + return result; }