myreadline.c 5.22 KB
Newer Older
Guido van Rossum's avatar
Guido van Rossum committed
1

2 3 4 5
/* Readline interface for tokenizer.c and [raw_]input() in bltinmodule.c.
   By default, or when stdin is not a tty device, we have a super
   simple my_readline function using fgets.
   Optionally, we can use the GNU readline library.
Guido van Rossum's avatar
Guido van Rossum committed
6 7 8 9 10 11
   my_readline() has a different return value from GNU readline():
   - NULL if an interrupt occurred or if an error occurred
   - a malloc'ed empty string if EOF was read
   - a malloc'ed string ending in \n normally
*/

12
#include "Python.h"
13 14 15 16
#ifdef MS_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif /* MS_WINDOWS */
Guido van Rossum's avatar
Guido van Rossum committed
17

18 19 20 21
#ifdef __VMS
extern char* vms__StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt);
#endif

Michael W. Hudson's avatar
Michael W. Hudson committed
22 23 24

PyThreadState* _PyOS_ReadlineTState;

25
#ifdef WITH_THREAD
Michael W. Hudson's avatar
Michael W. Hudson committed
26 27 28 29
#include "pythread.h"
static PyThread_type_lock _PyOS_ReadlineLock = NULL;
#endif

Thomas Wouters's avatar
Thomas Wouters committed
30
int (*PyOS_InputHook)(void) = NULL;
31 32 33 34 35

/* This function restarts a fgets() after an EINTR error occurred
   except if PyOS_InterruptOccurred() returns true. */

static int
Thomas Wouters's avatar
Thomas Wouters committed
36
my_fgets(char *buf, int len, FILE *fp)
37 38 39
{
	char *p;
	for (;;) {
40 41
		if (PyOS_InputHook != NULL)
			(void)(PyOS_InputHook)();
42 43 44 45
		errno = 0;
		p = fgets(buf, len, fp);
		if (p != NULL)
			return 0; /* No error */
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#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())
		*/
		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.
			*/
		}
#endif /* MS_WINDOWS */
75 76 77 78 79
		if (feof(fp)) {
			return -1; /* EOF */
		}
#ifdef EINTR
		if (errno == EINTR) {
Michael W. Hudson's avatar
Michael W. Hudson committed
80
			int s;
81
#ifdef WITH_THREAD
Michael W. Hudson's avatar
Michael W. Hudson committed
82
			PyEval_RestoreThread(_PyOS_ReadlineTState);
83
#endif
Michael W. Hudson's avatar
Michael W. Hudson committed
84
			s = PyErr_CheckSignals();
85
#ifdef WITH_THREAD
86
			PyEval_SaveThread();
87
#endif
Michael W. Hudson's avatar
Michael W. Hudson committed
88 89
			if (s < 0) {
				return 1;
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
			}
		}
#endif
		if (PyOS_InterruptOccurred()) {
			return 1; /* Interrupt */
		}
		return -2; /* Error */
	}
	/* NOTREACHED */
}


/* Readline implementation using fgets() */

char *
105
PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
106
{
107
	size_t n;
108
	char *p;
Guido van Rossum's avatar
Guido van Rossum committed
109
	n = 100;
110
	if ((p = (char *)PyMem_MALLOC(n)) == NULL)
Guido van Rossum's avatar
Guido van Rossum committed
111
		return NULL;
112
	fflush(sys_stdout);
Guido van Rossum's avatar
Guido van Rossum committed
113 114
	if (prompt)
		fprintf(stderr, "%s", prompt);
115
	fflush(stderr);
116
	switch (my_fgets(p, (int)n, sys_stdin)) {
117 118 119
	case 0: /* Normal case */
		break;
	case 1: /* Interrupt */
120
		PyMem_FREE(p);
Guido van Rossum's avatar
Guido van Rossum committed
121
		return NULL;
122 123 124 125 126
	case -1: /* EOF */
	case -2: /* Error */
	default: /* Shouldn't happen */
		*p = '\0';
		break;
Guido van Rossum's avatar
Guido van Rossum committed
127 128 129
	}
	n = strlen(p);
	while (n > 0 && p[n-1] != '\n') {
130
		size_t incr = n+2;
131
		p = (char *)PyMem_REALLOC(p, n + incr);
Guido van Rossum's avatar
Guido van Rossum committed
132 133
		if (p == NULL)
			return NULL;
134 135 136
		if (incr > INT_MAX) {
			PyErr_SetString(PyExc_OverflowError, "input line too long");
		}
137
		if (my_fgets(p+n, (int)incr, sys_stdin) != 0)
Guido van Rossum's avatar
Guido van Rossum committed
138 139 140
			break;
		n += strlen(p+n);
	}
141
	return (char *)PyMem_REALLOC(p, n+1);
142 143 144 145
}


/* By initializing this function pointer, systems embedding Python can
146 147 148
   override the readline function.

   Note: Python expects in return a buffer allocated with PyMem_Malloc. */
149

150
char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, char *);
151 152 153 154 155


/* Interface used by tokenizer.c and bltinmodule.c */

char *
156
PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
157
{
158
	char *rv;
159

Michael W. Hudson's avatar
Michael W. Hudson committed
160 161 162 163 164 165 166
	if (_PyOS_ReadlineTState == PyThreadState_GET()) {
		PyErr_SetString(PyExc_RuntimeError,
				"can't re-enter readline");
		return NULL;
	}
	

167
	if (PyOS_ReadlineFunctionPointer == NULL) {
168 169 170
#ifdef __VMS
                PyOS_ReadlineFunctionPointer = vms__StdioReadline;
#else
171
                PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
172
#endif
173
	}
Michael W. Hudson's avatar
Michael W. Hudson committed
174
	
175
#ifdef WITH_THREAD
Michael W. Hudson's avatar
Michael W. Hudson committed
176 177 178 179
	if (_PyOS_ReadlineLock == NULL) {
		_PyOS_ReadlineLock = PyThread_allocate_lock();		
	}
#endif
180

Michael W. Hudson's avatar
Michael W. Hudson committed
181
	_PyOS_ReadlineTState = PyThreadState_GET();
182
	Py_BEGIN_ALLOW_THREADS
183
#ifdef WITH_THREAD
Michael W. Hudson's avatar
Michael W. Hudson committed
184 185
	PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
#endif
186 187 188 189 190 191 192 193 194 195 196

        /* This is needed to handle the unlikely case that the
         * interpreter is in interactive mode *and* stdin/out are not
         * a tty.  This can happen, for example if python is run like
         * this: python -i < test1.py
         */
        if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
                rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
        else
                rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
                                                     prompt);
197
	Py_END_ALLOW_THREADS
Michael W. Hudson's avatar
Michael W. Hudson committed
198

199
#ifdef WITH_THREAD
Michael W. Hudson's avatar
Michael W. Hudson committed
200 201 202 203 204
	PyThread_release_lock(_PyOS_ReadlineLock);
#endif

	_PyOS_ReadlineTState = NULL;

205
	return rv;
Guido van Rossum's avatar
Guido van Rossum committed
206
}