Steffen Daode Nurpmeso <sdao...@googlemail.com> added the comment:
On Sat, Apr 09, 2011 at 02:18:01PM +0000, STINNER Victor wrote:
> I noticied a strange behaviour:
Still fun, but this one could even make it except for termios
flags, multibyte and the real problem, signal handling.
Hm.
----------
Added file: http://bugs.python.org/file21605/11650.termios-1.diff
_______________________________________
Python tracker <rep...@bugs.python.org>
<http://bugs.python.org/issue11650>
_______________________________________
diff --git a/Parser/myreadline.c b/Parser/myreadline.c
--- a/Parser/myreadline.c
+++ b/Parser/myreadline.c
@@ -10,6 +10,10 @@
*/
#include "Python.h"
+#ifdef Py_PYPORT_H
+# define __USE_TERMIOS
+# include "signal.h"
+#endif
#ifdef MS_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
@@ -19,6 +23,18 @@
extern char* vms__StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char
*prompt);
#endif
+typedef struct Args {
+ char *input;
+ FILE *fp;
+ int tios_fd;
+ int tios_is_init;
+#ifdef __USE_TERMIOS
+ int tios_is_set;
+ int __align;
+ struct termios tios_old;
+ struct termios tios_new;
+#endif
+} Args;
PyThreadState* _PyOS_ReadlineTState;
@@ -29,117 +45,230 @@
int (*PyOS_InputHook)(void) = NULL;
-/* This function restarts a fgets() after an EINTR error occurred
- except if PyOS_InterruptOccurred() returns true. */
+/* This function restarts a fgetc() after an EINTR error occurred
+ * except if PyOS_InterruptOccurred() returns true */
+static int my_fgets(Args *args);
+#ifdef __USE_TERMIOS
+static void termios_resume(Args *args);
+static void termios_suspend(Args *args);
+#endif
+
+#ifdef __USE_TERMIOS
+static void
+termios_resume(Args *args)
+{
+ if (!args->tios_is_init) {
+ args->tios_is_init = 1;
+
+ while (tcgetattr(args->tios_fd, &args->tios_old) != 0)
+ if (errno != EINTR) {
+ args->tios_fd = -1;
+ goto jleave;
+ }
+
+ memcpy(&args->tios_new, &args->tios_old, sizeof(args->tios_old));
+ args->tios_new.c_lflag &= ~(/*ECHOCTL |*/ ICANON);
+ args->tios_new.c_cc[VMIN] = 1;
+ }
+
+ if (args->tios_fd < 0)
+ goto jleave;
+
+ while (tcsetattr(args->tios_fd, TCSAFLUSH, &args->tios_new) != 0)
+ ;
+ args->tios_is_set = 1;
+
+jleave:
+ return;
+}
+
+static void
+termios_suspend(Args *args)
+{
+ if (args->tios_is_init && args->tios_is_set) {
+ while (tcsetattr(args->tios_fd, TCSANOW, &args->tios_old) != 0)
+ ;
+ args->tios_is_set = 0;
+ }
+ return;
+}
+#endif
static int
-my_fgets(char *buf, int len, FILE *fp)
+my_fgets(Args *args)
{
- char *p;
+ int estat;
+ char *buf, *cursor;
+ size_t buf_len;
+
+ 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)();
+#ifdef __USE_TERMIOS
+ termios_resume(args);
+#endif
+
+ /* 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(args->fp);
+#ifdef __USE_TERMIOS
+ if (!isprint(c))
+ switch (c) {
+ case '\x04':
+ c = EOF;
+ /* FALLTHROUGH */
+ default:
+ break;
+ case '\x03':
+ estat = SIGINT;
+ goto j_sigit;
+ case '\x1A':
+ estat = SIGTSTP;
+ goto j_sigit;
+ case '\x1C':
+ estat = SIGQUIT;
+ /* FALLTHROUGH */
+j_sigit: termios_suspend(args);
+ 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';
+ args->input = buf;
+jreturn:
+#ifdef __USE_TERMIOS
+ termios_suspend(args);
+#endif
+ 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 */
+
+ if (feof(args->fp)) {
+ estat = -1; /* EOF */
+ goto jfail;
+ }
+#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;
+ char *result;
+ auto Args args;
+
fflush(sys_stdout);
if (prompt)
fprintf(stderr, "%s", prompt);
fflush(stderr);
- switch (my_fgets(p, (int)n, sys_stdin)) {
+
+ args.input = NULL;
+ args.fp = sys_stdin;
+ args.tios_fd = fileno(sys_stdin);
+ args.tios_is_init = 0;
+
+ switch (my_fgets(&args)) {
case 0: /* Normal case */
+ case 1: /* Interrupt */
+ result = args.input;
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);
+
+ return result;
}
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com