Home | History | Annotate | Download | only in Parser
      1 
      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
      4    simple my_readline function using fgets.
      5    Optionally, we can use the GNU readline library.
      6    my_readline() has a different return value from GNU readline():
      7    - NULL if an interrupt occurred or if an error occurred
      8    - a malloc'ed empty string if EOF was read
      9    - a malloc'ed string ending in \n normally
     10 */
     11 
     12 #include "Python.h"
     13 #ifdef MS_WINDOWS
     14 #define WIN32_LEAN_AND_MEAN
     15 #include "windows.h"
     16 #endif /* MS_WINDOWS */
     17 
     18 
     19 PyThreadState* _PyOS_ReadlineTState;
     20 
     21 #ifdef WITH_THREAD
     22 #include "pythread.h"
     23 static PyThread_type_lock _PyOS_ReadlineLock = NULL;
     24 #endif
     25 
     26 int (*PyOS_InputHook)(void) = NULL;
     27 
     28 /* This function restarts a fgets() after an EINTR error occurred
     29    except if PyOS_InterruptOccurred() returns true. */
     30 
     31 static int
     32 my_fgets(char *buf, int len, FILE *fp)
     33 {
     34 #ifdef MS_WINDOWS
     35     HANDLE hInterruptEvent;
     36 #endif
     37     char *p;
     38     int err;
     39     while (1) {
     40         if (PyOS_InputHook != NULL)
     41             (void)(PyOS_InputHook)();
     42         errno = 0;
     43         clearerr(fp);
     44         p = fgets(buf, len, fp);
     45         if (p != NULL)
     46             return 0; /* No error */
     47         err = errno;
     48 #ifdef MS_WINDOWS
     49         /* Ctrl-C anywhere on the line or Ctrl-Z if the only character
     50            on a line will set ERROR_OPERATION_ABORTED. Under normal
     51            circumstances Ctrl-C will also have caused the SIGINT handler
     52            to fire which will have set the event object returned by
     53            _PyOS_SigintEvent. This signal fires in another thread and
     54            is not guaranteed to have occurred before this point in the
     55            code.
     56 
     57            Therefore: check whether the event is set with a small timeout.
     58            If it is, assume this is a Ctrl-C and reset the event. If it
     59            isn't set assume that this is a Ctrl-Z on its own and drop
     60            through to check for EOF.
     61         */
     62         if (GetLastError()==ERROR_OPERATION_ABORTED) {
     63             hInterruptEvent = _PyOS_SigintEvent();
     64             switch (WaitForSingleObjectEx(hInterruptEvent, 10, FALSE)) {
     65             case WAIT_OBJECT_0:
     66                 ResetEvent(hInterruptEvent);
     67                 return 1; /* Interrupt */
     68             case WAIT_FAILED:
     69                 return -2; /* Error */
     70             }
     71         }
     72 #endif /* MS_WINDOWS */
     73         if (feof(fp)) {
     74             clearerr(fp);
     75             return -1; /* EOF */
     76         }
     77 #ifdef EINTR
     78         if (err == EINTR) {
     79             int s;
     80 #ifdef WITH_THREAD
     81             PyEval_RestoreThread(_PyOS_ReadlineTState);
     82 #endif
     83             s = PyErr_CheckSignals();
     84 #ifdef WITH_THREAD
     85             PyEval_SaveThread();
     86 #endif
     87             if (s < 0)
     88                     return 1;
     89         /* try again */
     90             continue;
     91         }
     92 #endif
     93         if (PyOS_InterruptOccurred()) {
     94             return 1; /* Interrupt */
     95         }
     96         return -2; /* Error */
     97     }
     98     /* NOTREACHED */
     99 }
    100 
    101 #ifdef MS_WINDOWS
    102 /* Readline implementation using ReadConsoleW */
    103 
    104 extern char _get_console_type(HANDLE handle);
    105 
    106 char *
    107 _PyOS_WindowsConsoleReadline(HANDLE hStdIn)
    108 {
    109     static wchar_t wbuf_local[1024 * 16];
    110     const DWORD chunk_size = 1024;
    111 
    112     DWORD n_read, total_read, wbuflen, u8len;
    113     wchar_t *wbuf;
    114     char *buf = NULL;
    115     int err = 0;
    116 
    117     n_read = 0;
    118     total_read = 0;
    119     wbuf = wbuf_local;
    120     wbuflen = sizeof(wbuf_local) / sizeof(wbuf_local[0]) - 1;
    121     while (1) {
    122         if (!ReadConsoleW(hStdIn, &wbuf[total_read], wbuflen - total_read, &n_read, NULL)) {
    123             err = GetLastError();
    124             goto exit;
    125         }
    126         if (n_read == 0) {
    127             int s;
    128             err = GetLastError();
    129             if (err != ERROR_OPERATION_ABORTED)
    130                 goto exit;
    131             err = 0;
    132             HANDLE hInterruptEvent = _PyOS_SigintEvent();
    133             if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
    134                     == WAIT_OBJECT_0) {
    135                 ResetEvent(hInterruptEvent);
    136 #ifdef WITH_THREAD
    137                 PyEval_RestoreThread(_PyOS_ReadlineTState);
    138 #endif
    139                 s = PyErr_CheckSignals();
    140 #ifdef WITH_THREAD
    141                 PyEval_SaveThread();
    142 #endif
    143                 if (s < 0)
    144                     goto exit;
    145             }
    146             break;
    147         }
    148 
    149         total_read += n_read;
    150         if (total_read == 0 || wbuf[total_read - 1] == L'\n') {
    151             break;
    152         }
    153         wbuflen += chunk_size;
    154         if (wbuf == wbuf_local) {
    155             wbuf[total_read] = '\0';
    156             wbuf = (wchar_t*)PyMem_RawMalloc(wbuflen * sizeof(wchar_t));
    157             if (wbuf)
    158                 wcscpy_s(wbuf, wbuflen, wbuf_local);
    159         }
    160         else
    161             wbuf = (wchar_t*)PyMem_RawRealloc(wbuf, wbuflen * sizeof(wchar_t));
    162     }
    163 
    164     if (wbuf[0] == '\x1a') {
    165         buf = PyMem_RawMalloc(1);
    166         if (buf)
    167             buf[0] = '\0';
    168         goto exit;
    169     }
    170 
    171     u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, NULL, 0, NULL, NULL);
    172     buf = PyMem_RawMalloc(u8len + 1);
    173     u8len = WideCharToMultiByte(CP_UTF8, 0, wbuf, total_read, buf, u8len, NULL, NULL);
    174     buf[u8len] = '\0';
    175 
    176 exit:
    177     if (wbuf != wbuf_local)
    178         PyMem_RawFree(wbuf);
    179 
    180     if (err) {
    181 #ifdef WITH_THREAD
    182         PyEval_RestoreThread(_PyOS_ReadlineTState);
    183 #endif
    184         PyErr_SetFromWindowsErr(err);
    185 #ifdef WITH_THREAD
    186         PyEval_SaveThread();
    187 #endif
    188     }
    189 
    190     return buf;
    191 }
    192 
    193 #endif
    194 
    195 
    196 /* Readline implementation using fgets() */
    197 
    198 char *
    199 PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
    200 {
    201     size_t n;
    202     char *p, *pr;
    203 
    204 #ifdef MS_WINDOWS
    205     if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) {
    206         HANDLE hStdIn, hStdErr;
    207 
    208         _Py_BEGIN_SUPPRESS_IPH
    209         hStdIn = (HANDLE)_get_osfhandle(fileno(sys_stdin));
    210         hStdErr = (HANDLE)_get_osfhandle(fileno(stderr));
    211         _Py_END_SUPPRESS_IPH
    212 
    213         if (_get_console_type(hStdIn) == 'r') {
    214             fflush(sys_stdout);
    215             if (prompt) {
    216                 if (_get_console_type(hStdErr) == 'w') {
    217                     wchar_t *wbuf;
    218                     int wlen;
    219                     wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
    220                             NULL, 0);
    221                     if (wlen &&
    222                         (wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t)))) {
    223                         wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
    224                                 wbuf, wlen);
    225                         if (wlen) {
    226                             DWORD n;
    227                             fflush(stderr);
    228                             /* wlen includes null terminator, so subtract 1 */
    229                             WriteConsoleW(hStdErr, wbuf, wlen - 1, &n, NULL);
    230                         }
    231                         PyMem_RawFree(wbuf);
    232                     }
    233                 } else {
    234                     fprintf(stderr, "%s", prompt);
    235                     fflush(stderr);
    236                 }
    237             }
    238             clearerr(sys_stdin);
    239             return _PyOS_WindowsConsoleReadline(hStdIn);
    240         }
    241     }
    242 #endif
    243 
    244     n = 100;
    245     p = (char *)PyMem_RawMalloc(n);
    246     if (p == NULL)
    247         return NULL;
    248 
    249     fflush(sys_stdout);
    250     if (prompt)
    251         fprintf(stderr, "%s", prompt);
    252     fflush(stderr);
    253 
    254     switch (my_fgets(p, (int)n, sys_stdin)) {
    255     case 0: /* Normal case */
    256         break;
    257     case 1: /* Interrupt */
    258         PyMem_RawFree(p);
    259         return NULL;
    260     case -1: /* EOF */
    261     case -2: /* Error */
    262     default: /* Shouldn't happen */
    263         *p = '\0';
    264         break;
    265     }
    266     n = strlen(p);
    267     while (n > 0 && p[n-1] != '\n') {
    268         size_t incr = n+2;
    269         if (incr > INT_MAX) {
    270             PyMem_RawFree(p);
    271             PyErr_SetString(PyExc_OverflowError, "input line too long");
    272             return NULL;
    273         }
    274         pr = (char *)PyMem_RawRealloc(p, n + incr);
    275         if (pr == NULL) {
    276             PyMem_RawFree(p);
    277             PyErr_NoMemory();
    278             return NULL;
    279         }
    280         p = pr;
    281         if (my_fgets(p+n, (int)incr, sys_stdin) != 0)
    282             break;
    283         n += strlen(p+n);
    284     }
    285     pr = (char *)PyMem_RawRealloc(p, n+1);
    286     if (pr == NULL) {
    287         PyMem_RawFree(p);
    288         PyErr_NoMemory();
    289         return NULL;
    290     }
    291     return pr;
    292 }
    293 
    294 
    295 /* By initializing this function pointer, systems embedding Python can
    296    override the readline function.
    297 
    298    Note: Python expects in return a buffer allocated with PyMem_Malloc. */
    299 
    300 char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *);
    301 
    302 
    303 /* Interface used by tokenizer.c and bltinmodule.c */
    304 
    305 char *
    306 PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
    307 {
    308     char *rv, *res;
    309     size_t len;
    310 
    311     if (_PyOS_ReadlineTState == PyThreadState_GET()) {
    312         PyErr_SetString(PyExc_RuntimeError,
    313                         "can't re-enter readline");
    314         return NULL;
    315     }
    316 
    317 
    318     if (PyOS_ReadlineFunctionPointer == NULL) {
    319         PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
    320     }
    321 
    322 #ifdef WITH_THREAD
    323     if (_PyOS_ReadlineLock == NULL) {
    324         _PyOS_ReadlineLock = PyThread_allocate_lock();
    325     }
    326 #endif
    327 
    328     _PyOS_ReadlineTState = PyThreadState_GET();
    329     Py_BEGIN_ALLOW_THREADS
    330 #ifdef WITH_THREAD
    331     PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
    332 #endif
    333 
    334     /* This is needed to handle the unlikely case that the
    335      * interpreter is in interactive mode *and* stdin/out are not
    336      * a tty.  This can happen, for example if python is run like
    337      * this: python -i < test1.py
    338      */
    339     if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
    340         rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
    341     else
    342         rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
    343                                              prompt);
    344     Py_END_ALLOW_THREADS
    345 
    346 #ifdef WITH_THREAD
    347     PyThread_release_lock(_PyOS_ReadlineLock);
    348 #endif
    349 
    350     _PyOS_ReadlineTState = NULL;
    351 
    352     if (rv == NULL)
    353         return NULL;
    354 
    355     len = strlen(rv) + 1;
    356     res = PyMem_Malloc(len);
    357     if (res != NULL)
    358         memcpy(res, rv, len);
    359     PyMem_RawFree(rv);
    360 
    361     return res;
    362 }
    363