LCOV - code coverage report
Current view: top level - Parser - myreadline.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit acb105a7c1f] Lines: 60 90 66.7 %
Date: 2022-07-20 13:12:14 Functions: 3 3 100.0 %
Branches: 26 44 59.1 %

           Branch data     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                 :            :     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                 :            :             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                 :            :         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                 :            :         PyErr_NoMemory();
     411                 :            :     }
     412                 :         56 :     PyMem_RawFree(rv);
     413                 :            : 
     414                 :         56 :     return res;
     415                 :            : }

Generated by: LCOV version 1.14