LCOV - code coverage report
Current view: | top level - Parser - myreadline.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 0 | 93 | 0.0 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 0 | 3 | 0.0 %
|  |  | Branches: | 0 | 44 | 0.0 %

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

Generated by: LCOV version 1.14