LCOV - code coverage report
Current view: top level - Parser - myreadline.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 61 94 64.9 %
Date: 2022-07-07 18:19:46 Functions: 3 3 100.0 %

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

Generated by: LCOV version 1.14