(root)/
Python-3.11.7/
Parser/
myreadline.c
       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  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      while (1) {
      46          if (PyOS_InputHook != NULL) {
      47              (void)(PyOS_InputHook)();
      48          }
      49  
      50          errno = 0;
      51          clearerr(fp);
      52          char *p = fgets(buf, len, fp);
      53          if (p != NULL) {
      54              return 0; /* No error */
      55          }
      56          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          if (feof(fp)) {
      85              clearerr(fp);
      86              return -1; /* EOF */
      87          }
      88  
      89  #ifdef EINTR
      90          if (err == EINTR) {
      91              PyEval_RestoreThread(tstate);
      92              int s = PyErr_CheckSignals();
      93              PyEval_SaveThread();
      94  
      95              if (s < 0) {
      96                  return 1;
      97              }
      98              /* try again */
      99              continue;
     100          }
     101  #endif
     102  
     103          if (_PyOS_InterruptOccurred(tstate)) {
     104              return 1; /* Interrupt */
     105          }
     106          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  PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
     243  {
     244      size_t n;
     245      char *p, *pr;
     246      PyThreadState *tstate = _PyOS_ReadlineTState;
     247      assert(tstate != NULL);
     248  
     249  #ifdef MS_WINDOWS
     250      if (!Py_LegacyWindowsStdioFlag && sys_stdin == stdin) {
     251          HANDLE hStdIn, hStdErr;
     252  
     253          hStdIn = _Py_get_osfhandle_noraise(fileno(sys_stdin));
     254          hStdErr = _Py_get_osfhandle_noraise(fileno(stderr));
     255  
     256          if (_get_console_type(hStdIn) == 'r') {
     257              fflush(sys_stdout);
     258              if (prompt) {
     259                  if (_get_console_type(hStdErr) == 'w') {
     260                      wchar_t *wbuf;
     261                      int wlen;
     262                      wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
     263                              NULL, 0);
     264                      if (wlen) {
     265                          wbuf = PyMem_RawMalloc(wlen * sizeof(wchar_t));
     266                          if (wbuf == NULL) {
     267                              PyEval_RestoreThread(tstate);
     268                              PyErr_NoMemory();
     269                              PyEval_SaveThread();
     270                              return NULL;
     271                          }
     272                          wlen = MultiByteToWideChar(CP_UTF8, 0, prompt, -1,
     273                                  wbuf, wlen);
     274                          if (wlen) {
     275                              DWORD n;
     276                              fflush(stderr);
     277                              /* wlen includes null terminator, so subtract 1 */
     278                              WriteConsoleW(hStdErr, wbuf, wlen - 1, &n, NULL);
     279                          }
     280                          PyMem_RawFree(wbuf);
     281                      }
     282                  } else {
     283                      fprintf(stderr, "%s", prompt);
     284                      fflush(stderr);
     285                  }
     286              }
     287              clearerr(sys_stdin);
     288              return _PyOS_WindowsConsoleReadline(tstate, hStdIn);
     289          }
     290      }
     291  #endif
     292  
     293      fflush(sys_stdout);
     294      if (prompt) {
     295          fprintf(stderr, "%s", prompt);
     296      }
     297      fflush(stderr);
     298  
     299      n = 0;
     300      p = NULL;
     301      do {
     302          size_t incr = (n > 0) ? n + 2 : 100;
     303          if (incr > INT_MAX) {
     304              PyMem_RawFree(p);
     305              PyEval_RestoreThread(tstate);
     306              PyErr_SetString(PyExc_OverflowError, "input line too long");
     307              PyEval_SaveThread();
     308              return NULL;
     309          }
     310          pr = (char *)PyMem_RawRealloc(p, n + incr);
     311          if (pr == NULL) {
     312              PyMem_RawFree(p);
     313              PyEval_RestoreThread(tstate);
     314              PyErr_NoMemory();
     315              PyEval_SaveThread();
     316              return NULL;
     317          }
     318          p = pr;
     319          int err = my_fgets(tstate, p + n, (int)incr, sys_stdin);
     320          if (err == 1) {
     321              // Interrupt
     322              PyMem_RawFree(p);
     323              return NULL;
     324          } else if (err != 0) {
     325              // EOF or error
     326              p[n] = '\0';
     327              break;
     328          }
     329          n += strlen(p + n);
     330      } while (p[n-1] != '\n');
     331  
     332      pr = (char *)PyMem_RawRealloc(p, n+1);
     333      if (pr == NULL) {
     334          PyMem_RawFree(p);
     335          PyEval_RestoreThread(tstate);
     336          PyErr_NoMemory();
     337          PyEval_SaveThread();
     338          return NULL;
     339      }
     340      return pr;
     341  }
     342  
     343  
     344  /* By initializing this function pointer, systems embedding Python can
     345     override the readline function.
     346  
     347     Note: Python expects in return a buffer allocated with PyMem_Malloc. */
     348  
     349  char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, const char *) = NULL;
     350  
     351  
     352  /* Interface used by tokenizer.c and bltinmodule.c */
     353  
     354  char *
     355  PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
     356  {
     357      char *rv, *res;
     358      size_t len;
     359  
     360      PyThreadState *tstate = _PyThreadState_GET();
     361      if (_PyOS_ReadlineTState == tstate) {
     362          PyErr_SetString(PyExc_RuntimeError,
     363                          "can't re-enter readline");
     364          return NULL;
     365      }
     366  
     367  
     368      if (PyOS_ReadlineFunctionPointer == NULL) {
     369          PyOS_ReadlineFunctionPointer = PyOS_StdioReadline;
     370      }
     371  
     372      if (_PyOS_ReadlineLock == NULL) {
     373          _PyOS_ReadlineLock = PyThread_allocate_lock();
     374          if (_PyOS_ReadlineLock == NULL) {
     375              PyErr_SetString(PyExc_MemoryError, "can't allocate lock");
     376              return NULL;
     377          }
     378      }
     379  
     380      _PyOS_ReadlineTState = tstate;
     381      Py_BEGIN_ALLOW_THREADS
     382      PyThread_acquire_lock(_PyOS_ReadlineLock, 1);
     383  
     384      /* This is needed to handle the unlikely case that the
     385       * interpreter is in interactive mode *and* stdin/out are not
     386       * a tty.  This can happen, for example if python is run like
     387       * this: python -i < test1.py
     388       */
     389      if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
     390          rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
     391      else
     392          rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
     393                                               prompt);
     394      Py_END_ALLOW_THREADS
     395  
     396      PyThread_release_lock(_PyOS_ReadlineLock);
     397  
     398      _PyOS_ReadlineTState = NULL;
     399  
     400      if (rv == NULL)
     401          return NULL;
     402  
     403      len = strlen(rv) + 1;
     404      res = PyMem_Malloc(len);
     405      if (res != NULL) {
     406          memcpy(res, rv, len);
     407      }
     408      else {
     409          PyErr_NoMemory();
     410      }
     411      PyMem_RawFree(rv);
     412  
     413      return res;
     414  }