1  #include "Python.h"
       2  #include "pycore_fileutils.h"     // DECODE_LOCALE_ERR
       3  #include "pycore_getopt.h"        // _PyOS_GetOpt()
       4  #include "pycore_initconfig.h"    // _PyArgv
       5  #include "pycore_pymem.h"         // _PyMem_GetAllocatorName()
       6  #include "pycore_runtime.h"       // _PyRuntime_Initialize()
       7  
       8  #include <locale.h>               // setlocale()
       9  #include <stdlib.h>               // getenv()
      10  
      11  
      12  /* Forward declarations */
      13  static void
      14  preconfig_copy(PyPreConfig *config, const PyPreConfig *config2);
      15  
      16  
      17  /* --- File system encoding/errors -------------------------------- */
      18  
      19  const char *Py_FileSystemDefaultEncoding = NULL;
      20  int Py_HasFileSystemDefaultEncoding = 0;
      21  const char *Py_FileSystemDefaultEncodeErrors = NULL;
      22  int _Py_HasFileSystemDefaultEncodeErrors = 0;
      23  
      24  void
      25  _Py_ClearFileSystemEncoding(void)
      26  {
      27      if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) {
      28          PyMem_RawFree((char*)Py_FileSystemDefaultEncoding);
      29          Py_FileSystemDefaultEncoding = NULL;
      30      }
      31      if (!_Py_HasFileSystemDefaultEncodeErrors && Py_FileSystemDefaultEncodeErrors) {
      32          PyMem_RawFree((char*)Py_FileSystemDefaultEncodeErrors);
      33          Py_FileSystemDefaultEncodeErrors = NULL;
      34      }
      35  }
      36  
      37  
      38  /* Set Py_FileSystemDefaultEncoding and Py_FileSystemDefaultEncodeErrors
      39     global configuration variables to PyConfig.filesystem_encoding and
      40     PyConfig.filesystem_errors (encoded to UTF-8).
      41  
      42     Function called by _PyUnicode_InitEncodings(). */
      43  int
      44  _Py_SetFileSystemEncoding(const char *encoding, const char *errors)
      45  {
      46      char *encoding2 = _PyMem_RawStrdup(encoding);
      47      if (encoding2 == NULL) {
      48          return -1;
      49      }
      50  
      51      char *errors2 = _PyMem_RawStrdup(errors);
      52      if (errors2 == NULL) {
      53          PyMem_RawFree(encoding2);
      54          return -1;
      55      }
      56  
      57      _Py_ClearFileSystemEncoding();
      58  
      59      Py_FileSystemDefaultEncoding = encoding2;
      60      Py_HasFileSystemDefaultEncoding = 0;
      61  
      62      Py_FileSystemDefaultEncodeErrors = errors2;
      63      _Py_HasFileSystemDefaultEncodeErrors = 0;
      64      return 0;
      65  }
      66  
      67  
      68  /* --- _PyArgv ---------------------------------------------------- */
      69  
      70  /* Decode bytes_argv using Py_DecodeLocale() */
      71  PyStatus
      72  _PyArgv_AsWstrList(const _PyArgv *args, PyWideStringList *list)
      73  {
      74      PyWideStringList wargv = _PyWideStringList_INIT;
      75      if (args->use_bytes_argv) {
      76          size_t size = sizeof(wchar_t*) * args->argc;
      77          wargv.items = (wchar_t **)PyMem_RawMalloc(size);
      78          if (wargv.items == NULL) {
      79              return _PyStatus_NO_MEMORY();
      80          }
      81  
      82          for (Py_ssize_t i = 0; i < args->argc; i++) {
      83              size_t len;
      84              wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
      85              if (arg == NULL) {
      86                  _PyWideStringList_Clear(&wargv);
      87                  return DECODE_LOCALE_ERR("command line arguments", len);
      88              }
      89              wargv.items[i] = arg;
      90              wargv.length++;
      91          }
      92  
      93          _PyWideStringList_Clear(list);
      94          *list = wargv;
      95      }
      96      else {
      97          wargv.length = args->argc;
      98          wargv.items = (wchar_t **)args->wchar_argv;
      99          if (_PyWideStringList_Copy(list, &wargv) < 0) {
     100              return _PyStatus_NO_MEMORY();
     101          }
     102      }
     103      return _PyStatus_OK();
     104  }
     105  
     106  
     107  /* --- _PyPreCmdline ------------------------------------------------- */
     108  
     109  void
     110  _PyPreCmdline_Clear(_PyPreCmdline *cmdline)
     111  {
     112      _PyWideStringList_Clear(&cmdline->argv);
     113      _PyWideStringList_Clear(&cmdline->xoptions);
     114  }
     115  
     116  
     117  PyStatus
     118  _PyPreCmdline_SetArgv(_PyPreCmdline *cmdline, const _PyArgv *args)
     119  {
     120      return _PyArgv_AsWstrList(args, &cmdline->argv);
     121  }
     122  
     123  
     124  static void
     125  precmdline_get_preconfig(_PyPreCmdline *cmdline, const PyPreConfig *config)
     126  {
     127  #define COPY_ATTR(ATTR) \
     128      if (config->ATTR != -1) { \
     129          cmdline->ATTR = config->ATTR; \
     130      }
     131  
     132      COPY_ATTR(isolated);
     133      COPY_ATTR(use_environment);
     134      COPY_ATTR(dev_mode);
     135  
     136  #undef COPY_ATTR
     137  }
     138  
     139  
     140  static void
     141  precmdline_set_preconfig(const _PyPreCmdline *cmdline, PyPreConfig *config)
     142  {
     143  #define COPY_ATTR(ATTR) \
     144      config->ATTR = cmdline->ATTR
     145  
     146      COPY_ATTR(isolated);
     147      COPY_ATTR(use_environment);
     148      COPY_ATTR(dev_mode);
     149  
     150  #undef COPY_ATTR
     151  }
     152  
     153  
     154  PyStatus
     155  _PyPreCmdline_SetConfig(const _PyPreCmdline *cmdline, PyConfig *config)
     156  {
     157  #define COPY_ATTR(ATTR) \
     158      config->ATTR = cmdline->ATTR
     159  
     160      PyStatus status = _PyWideStringList_Extend(&config->xoptions, &cmdline->xoptions);
     161      if (_PyStatus_EXCEPTION(status)) {
     162          return status;
     163      }
     164  
     165      COPY_ATTR(isolated);
     166      COPY_ATTR(use_environment);
     167      COPY_ATTR(dev_mode);
     168      COPY_ATTR(warn_default_encoding);
     169      return _PyStatus_OK();
     170  
     171  #undef COPY_ATTR
     172  }
     173  
     174  
     175  /* Parse the command line arguments */
     176  static PyStatus
     177  precmdline_parse_cmdline(_PyPreCmdline *cmdline)
     178  {
     179      const PyWideStringList *argv = &cmdline->argv;
     180  
     181      _PyOS_ResetGetOpt();
     182      /* Don't log parsing errors into stderr here: PyConfig_Read()
     183         is responsible for that */
     184      _PyOS_opterr = 0;
     185      do {
     186          int longindex = -1;
     187          int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);
     188  
     189          if (c == EOF || c == 'c' || c == 'm') {
     190              break;
     191          }
     192  
     193          switch (c) {
     194          case 'E':
     195              cmdline->use_environment = 0;
     196              break;
     197  
     198          case 'I':
     199              cmdline->isolated = 1;
     200              break;
     201  
     202          case 'X':
     203          {
     204              PyStatus status = PyWideStringList_Append(&cmdline->xoptions,
     205                                                        _PyOS_optarg);
     206              if (_PyStatus_EXCEPTION(status)) {
     207                  return status;
     208              }
     209              break;
     210          }
     211  
     212          default:
     213              /* ignore other argument:
     214                 handled by PyConfig_Read() */
     215              break;
     216          }
     217      } while (1);
     218  
     219      return _PyStatus_OK();
     220  }
     221  
     222  
     223  PyStatus
     224  _PyPreCmdline_Read(_PyPreCmdline *cmdline, const PyPreConfig *preconfig)
     225  {
     226      precmdline_get_preconfig(cmdline, preconfig);
     227  
     228      if (preconfig->parse_argv) {
     229          PyStatus status = precmdline_parse_cmdline(cmdline);
     230          if (_PyStatus_EXCEPTION(status)) {
     231              return status;
     232          }
     233      }
     234  
     235      /* isolated, use_environment */
     236      if (cmdline->isolated < 0) {
     237          cmdline->isolated = 0;
     238      }
     239      if (cmdline->isolated > 0) {
     240          cmdline->use_environment = 0;
     241      }
     242      if (cmdline->use_environment < 0) {
     243          cmdline->use_environment = 0;
     244      }
     245  
     246      /* dev_mode */
     247      if ((cmdline->dev_mode < 0)
     248          && (_Py_get_xoption(&cmdline->xoptions, L"dev")
     249              || _Py_GetEnv(cmdline->use_environment, "PYTHONDEVMODE")))
     250      {
     251          cmdline->dev_mode = 1;
     252      }
     253      if (cmdline->dev_mode < 0) {
     254          cmdline->dev_mode = 0;
     255      }
     256  
     257      // warn_default_encoding
     258      if (_Py_get_xoption(&cmdline->xoptions, L"warn_default_encoding")
     259              || _Py_GetEnv(cmdline->use_environment, "PYTHONWARNDEFAULTENCODING"))
     260      {
     261          cmdline->warn_default_encoding = 1;
     262      }
     263  
     264      assert(cmdline->use_environment >= 0);
     265      assert(cmdline->isolated >= 0);
     266      assert(cmdline->dev_mode >= 0);
     267      assert(cmdline->warn_default_encoding >= 0);
     268  
     269      return _PyStatus_OK();
     270  }
     271  
     272  
     273  /* --- PyPreConfig ----------------------------------------------- */
     274  
     275  
     276  void
     277  _PyPreConfig_InitCompatConfig(PyPreConfig *config)
     278  {
     279      memset(config, 0, sizeof(*config));
     280  
     281      config->_config_init = (int)_PyConfig_INIT_COMPAT;
     282      config->parse_argv = 0;
     283      config->isolated = -1;
     284      config->use_environment = -1;
     285      config->configure_locale = 1;
     286  
     287      /* bpo-36443: C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
     288         are disabled by default using the Compat configuration.
     289  
     290         Py_UTF8Mode=1 enables the UTF-8 mode. PYTHONUTF8 environment variable
     291         is ignored (even if use_environment=1). */
     292      config->utf8_mode = 0;
     293      config->coerce_c_locale = 0;
     294      config->coerce_c_locale_warn = 0;
     295  
     296      config->dev_mode = -1;
     297      config->allocator = PYMEM_ALLOCATOR_NOT_SET;
     298  #ifdef MS_WINDOWS
     299      config->legacy_windows_fs_encoding = -1;
     300  #endif
     301  }
     302  
     303  
     304  void
     305  PyPreConfig_InitPythonConfig(PyPreConfig *config)
     306  {
     307      _PyPreConfig_InitCompatConfig(config);
     308  
     309      config->_config_init = (int)_PyConfig_INIT_PYTHON;
     310      config->isolated = 0;
     311      config->parse_argv = 1;
     312      config->use_environment = 1;
     313      /* Set to -1 to enable C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
     314         depending on the LC_CTYPE locale, PYTHONUTF8 and PYTHONCOERCECLOCALE
     315         environment variables. */
     316      config->coerce_c_locale = -1;
     317      config->coerce_c_locale_warn = -1;
     318      config->utf8_mode = -1;
     319  #ifdef MS_WINDOWS
     320      config->legacy_windows_fs_encoding = 0;
     321  #endif
     322  }
     323  
     324  
     325  void
     326  PyPreConfig_InitIsolatedConfig(PyPreConfig *config)
     327  {
     328      _PyPreConfig_InitCompatConfig(config);
     329  
     330      config->_config_init = (int)_PyConfig_INIT_ISOLATED;
     331      config->configure_locale = 0;
     332      config->isolated = 1;
     333      config->use_environment = 0;
     334      config->utf8_mode = 0;
     335      config->dev_mode = 0;
     336  #ifdef MS_WINDOWS
     337      config->legacy_windows_fs_encoding = 0;
     338  #endif
     339  }
     340  
     341  
     342  PyStatus
     343  _PyPreConfig_InitFromPreConfig(PyPreConfig *config,
     344                                 const PyPreConfig *config2)
     345  {
     346      PyPreConfig_InitPythonConfig(config);
     347      preconfig_copy(config, config2);
     348      return _PyStatus_OK();
     349  }
     350  
     351  
     352  void
     353  _PyPreConfig_InitFromConfig(PyPreConfig *preconfig, const PyConfig *config)
     354  {
     355      _PyConfigInitEnum config_init = (_PyConfigInitEnum)config->_config_init;
     356      switch (config_init) {
     357      case _PyConfig_INIT_PYTHON:
     358          PyPreConfig_InitPythonConfig(preconfig);
     359          break;
     360      case _PyConfig_INIT_ISOLATED:
     361          PyPreConfig_InitIsolatedConfig(preconfig);
     362          break;
     363      case _PyConfig_INIT_COMPAT:
     364      default:
     365          _PyPreConfig_InitCompatConfig(preconfig);
     366      }
     367  
     368      _PyPreConfig_GetConfig(preconfig, config);
     369  }
     370  
     371  
     372  static void
     373  preconfig_copy(PyPreConfig *config, const PyPreConfig *config2)
     374  {
     375  #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
     376  
     377      COPY_ATTR(_config_init);
     378      COPY_ATTR(parse_argv);
     379      COPY_ATTR(isolated);
     380      COPY_ATTR(use_environment);
     381      COPY_ATTR(configure_locale);
     382      COPY_ATTR(dev_mode);
     383      COPY_ATTR(coerce_c_locale);
     384      COPY_ATTR(coerce_c_locale_warn);
     385      COPY_ATTR(utf8_mode);
     386      COPY_ATTR(allocator);
     387  #ifdef MS_WINDOWS
     388      COPY_ATTR(legacy_windows_fs_encoding);
     389  #endif
     390  
     391  #undef COPY_ATTR
     392  }
     393  
     394  
     395  PyObject*
     396  _PyPreConfig_AsDict(const PyPreConfig *config)
     397  {
     398      PyObject *dict;
     399  
     400      dict = PyDict_New();
     401      if (dict == NULL) {
     402          return NULL;
     403      }
     404  
     405  #define SET_ITEM_INT(ATTR) \
     406          do { \
     407              PyObject *obj = PyLong_FromLong(config->ATTR); \
     408              if (obj == NULL) { \
     409                  goto fail; \
     410              } \
     411              int res = PyDict_SetItemString(dict, #ATTR, obj); \
     412              Py_DECREF(obj); \
     413              if (res < 0) { \
     414                  goto fail; \
     415              } \
     416          } while (0)
     417  
     418      SET_ITEM_INT(_config_init);
     419      SET_ITEM_INT(parse_argv);
     420      SET_ITEM_INT(isolated);
     421      SET_ITEM_INT(use_environment);
     422      SET_ITEM_INT(configure_locale);
     423      SET_ITEM_INT(coerce_c_locale);
     424      SET_ITEM_INT(coerce_c_locale_warn);
     425      SET_ITEM_INT(utf8_mode);
     426  #ifdef MS_WINDOWS
     427      SET_ITEM_INT(legacy_windows_fs_encoding);
     428  #endif
     429      SET_ITEM_INT(dev_mode);
     430      SET_ITEM_INT(allocator);
     431      return dict;
     432  
     433  fail:
     434      Py_DECREF(dict);
     435      return NULL;
     436  
     437  #undef SET_ITEM_INT
     438  }
     439  
     440  
     441  void
     442  _PyPreConfig_GetConfig(PyPreConfig *preconfig, const PyConfig *config)
     443  {
     444  #define COPY_ATTR(ATTR) \
     445      if (config->ATTR != -1) { \
     446          preconfig->ATTR = config->ATTR; \
     447      }
     448  
     449      COPY_ATTR(parse_argv);
     450      COPY_ATTR(isolated);
     451      COPY_ATTR(use_environment);
     452      COPY_ATTR(dev_mode);
     453  
     454  #undef COPY_ATTR
     455  }
     456  
     457  
     458  static void
     459  preconfig_get_global_vars(PyPreConfig *config)
     460  {
     461      if (config->_config_init != _PyConfig_INIT_COMPAT) {
     462          /* Python and Isolated configuration ignore global variables */
     463          return;
     464      }
     465  
     466  #define COPY_FLAG(ATTR, VALUE) \
     467      if (config->ATTR < 0) { \
     468          config->ATTR = VALUE; \
     469      }
     470  #define COPY_NOT_FLAG(ATTR, VALUE) \
     471      if (config->ATTR < 0) { \
     472          config->ATTR = !(VALUE); \
     473      }
     474  
     475      COPY_FLAG(isolated, Py_IsolatedFlag);
     476      COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
     477      if (Py_UTF8Mode > 0) {
     478          config->utf8_mode = Py_UTF8Mode;
     479      }
     480  #ifdef MS_WINDOWS
     481      COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
     482  #endif
     483  
     484  #undef COPY_FLAG
     485  #undef COPY_NOT_FLAG
     486  }
     487  
     488  
     489  static void
     490  preconfig_set_global_vars(const PyPreConfig *config)
     491  {
     492  #define COPY_FLAG(ATTR, VAR) \
     493      if (config->ATTR >= 0) { \
     494          VAR = config->ATTR; \
     495      }
     496  #define COPY_NOT_FLAG(ATTR, VAR) \
     497      if (config->ATTR >= 0) { \
     498          VAR = !config->ATTR; \
     499      }
     500  
     501      COPY_FLAG(isolated, Py_IsolatedFlag);
     502      COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
     503  #ifdef MS_WINDOWS
     504      COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
     505  #endif
     506      COPY_FLAG(utf8_mode, Py_UTF8Mode);
     507  
     508  #undef COPY_FLAG
     509  #undef COPY_NOT_FLAG
     510  }
     511  
     512  
     513  const char*
     514  _Py_GetEnv(int use_environment, const char *name)
     515  {
     516      assert(use_environment >= 0);
     517  
     518      if (!use_environment) {
     519          return NULL;
     520      }
     521  
     522      const char *var = getenv(name);
     523      if (var && var[0] != '\0') {
     524          return var;
     525      }
     526      else {
     527          return NULL;
     528      }
     529  }
     530  
     531  
     532  int
     533  _Py_str_to_int(const char *str, int *result)
     534  {
     535      const char *endptr = str;
     536      errno = 0;
     537      long value = strtol(str, (char **)&endptr, 10);
     538      if (*endptr != '\0' || errno == ERANGE) {
     539          return -1;
     540      }
     541      if (value < INT_MIN || value > INT_MAX) {
     542          return -1;
     543      }
     544  
     545      *result = (int)value;
     546      return 0;
     547  }
     548  
     549  
     550  void
     551  _Py_get_env_flag(int use_environment, int *flag, const char *name)
     552  {
     553      const char *var = _Py_GetEnv(use_environment, name);
     554      if (!var) {
     555          return;
     556      }
     557      int value;
     558      if (_Py_str_to_int(var, &value) < 0 || value < 0) {
     559          /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */
     560          value = 1;
     561      }
     562      if (*flag < value) {
     563          *flag = value;
     564      }
     565  }
     566  
     567  
     568  const wchar_t*
     569  _Py_get_xoption(const PyWideStringList *xoptions, const wchar_t *name)
     570  {
     571      for (Py_ssize_t i=0; i < xoptions->length; i++) {
     572          const wchar_t *option = xoptions->items[i];
     573          size_t len;
     574          wchar_t *sep = wcschr(option, L'=');
     575          if (sep != NULL) {
     576              len = (sep - option);
     577          }
     578          else {
     579              len = wcslen(option);
     580          }
     581          if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') {
     582              return option;
     583          }
     584      }
     585      return NULL;
     586  }
     587  
     588  
     589  static PyStatus
     590  preconfig_init_utf8_mode(PyPreConfig *config, const _PyPreCmdline *cmdline)
     591  {
     592  #ifdef MS_WINDOWS
     593      if (config->legacy_windows_fs_encoding) {
     594          config->utf8_mode = 0;
     595      }
     596  #endif
     597  
     598      if (config->utf8_mode >= 0) {
     599          return _PyStatus_OK();
     600      }
     601  
     602      const wchar_t *xopt;
     603      xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8");
     604      if (xopt) {
     605          wchar_t *sep = wcschr(xopt, L'=');
     606          if (sep) {
     607              xopt = sep + 1;
     608              if (wcscmp(xopt, L"1") == 0) {
     609                  config->utf8_mode = 1;
     610              }
     611              else if (wcscmp(xopt, L"0") == 0) {
     612                  config->utf8_mode = 0;
     613              }
     614              else {
     615                  return _PyStatus_ERR("invalid -X utf8 option value");
     616              }
     617          }
     618          else {
     619              config->utf8_mode = 1;
     620          }
     621          return _PyStatus_OK();
     622      }
     623  
     624      const char *opt = _Py_GetEnv(config->use_environment, "PYTHONUTF8");
     625      if (opt) {
     626          if (strcmp(opt, "1") == 0) {
     627              config->utf8_mode = 1;
     628          }
     629          else if (strcmp(opt, "0") == 0) {
     630              config->utf8_mode = 0;
     631          }
     632          else {
     633              return _PyStatus_ERR("invalid PYTHONUTF8 environment "
     634                                  "variable value");
     635          }
     636          return _PyStatus_OK();
     637      }
     638  
     639  
     640  #ifndef MS_WINDOWS
     641      if (config->utf8_mode < 0) {
     642          /* The C locale and the POSIX locale enable the UTF-8 Mode (PEP 540) */
     643          const char *ctype_loc = setlocale(LC_CTYPE, NULL);
     644          if (ctype_loc != NULL
     645             && (strcmp(ctype_loc, "C") == 0
     646                 || strcmp(ctype_loc, "POSIX") == 0))
     647          {
     648              config->utf8_mode = 1;
     649          }
     650      }
     651  #endif
     652  
     653      if (config->utf8_mode < 0) {
     654          config->utf8_mode = 0;
     655      }
     656      return _PyStatus_OK();
     657  }
     658  
     659  
     660  static void
     661  preconfig_init_coerce_c_locale(PyPreConfig *config)
     662  {
     663      if (!config->configure_locale) {
     664          config->coerce_c_locale = 0;
     665          config->coerce_c_locale_warn = 0;
     666          return;
     667      }
     668  
     669      const char *env = _Py_GetEnv(config->use_environment, "PYTHONCOERCECLOCALE");
     670      if (env) {
     671          if (strcmp(env, "0") == 0) {
     672              if (config->coerce_c_locale < 0) {
     673                  config->coerce_c_locale = 0;
     674              }
     675          }
     676          else if (strcmp(env, "warn") == 0) {
     677              if (config->coerce_c_locale_warn < 0) {
     678                  config->coerce_c_locale_warn = 1;
     679              }
     680          }
     681          else {
     682              if (config->coerce_c_locale < 0) {
     683                  config->coerce_c_locale = 1;
     684              }
     685          }
     686      }
     687  
     688      /* Test if coerce_c_locale equals to -1 or equals to 1:
     689         PYTHONCOERCECLOCALE=1 doesn't imply that the C locale is always coerced.
     690         It is only coerced if if the LC_CTYPE locale is "C". */
     691      if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
     692          /* The C locale enables the C locale coercion (PEP 538) */
     693          if (_Py_LegacyLocaleDetected(0)) {
     694              config->coerce_c_locale = 2;
     695          }
     696          else {
     697              config->coerce_c_locale = 0;
     698          }
     699      }
     700  
     701      if (config->coerce_c_locale_warn < 0) {
     702          config->coerce_c_locale_warn = 0;
     703      }
     704  }
     705  
     706  
     707  static PyStatus
     708  preconfig_init_allocator(PyPreConfig *config)
     709  {
     710      if (config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
     711          /* bpo-34247. The PYTHONMALLOC environment variable has the priority
     712             over PYTHONDEV env var and "-X dev" command line option.
     713             For example, PYTHONMALLOC=malloc PYTHONDEVMODE=1 sets the memory
     714             allocators to "malloc" (and not to "debug"). */
     715          const char *envvar = _Py_GetEnv(config->use_environment, "PYTHONMALLOC");
     716          if (envvar) {
     717              PyMemAllocatorName name;
     718              if (_PyMem_GetAllocatorName(envvar, &name) < 0) {
     719                  return _PyStatus_ERR("PYTHONMALLOC: unknown allocator");
     720              }
     721              config->allocator = (int)name;
     722          }
     723      }
     724  
     725      if (config->dev_mode && config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
     726          config->allocator = PYMEM_ALLOCATOR_DEBUG;
     727      }
     728      return _PyStatus_OK();
     729  }
     730  
     731  
     732  static PyStatus
     733  preconfig_read(PyPreConfig *config, _PyPreCmdline *cmdline)
     734  {
     735      PyStatus status;
     736  
     737      status = _PyPreCmdline_Read(cmdline, config);
     738      if (_PyStatus_EXCEPTION(status)) {
     739          return status;
     740      }
     741  
     742      precmdline_set_preconfig(cmdline, config);
     743  
     744      /* legacy_windows_fs_encoding, coerce_c_locale, utf8_mode */
     745  #ifdef MS_WINDOWS
     746      _Py_get_env_flag(config->use_environment,
     747                       &config->legacy_windows_fs_encoding,
     748                       "PYTHONLEGACYWINDOWSFSENCODING");
     749  #endif
     750  
     751      preconfig_init_coerce_c_locale(config);
     752  
     753      status = preconfig_init_utf8_mode(config, cmdline);
     754      if (_PyStatus_EXCEPTION(status)) {
     755          return status;
     756      }
     757  
     758      /* allocator */
     759      status = preconfig_init_allocator(config);
     760      if (_PyStatus_EXCEPTION(status)) {
     761          return status;
     762      }
     763  
     764      assert(config->coerce_c_locale >= 0);
     765      assert(config->coerce_c_locale_warn >= 0);
     766  #ifdef MS_WINDOWS
     767      assert(config->legacy_windows_fs_encoding >= 0);
     768  #endif
     769      assert(config->utf8_mode >= 0);
     770      assert(config->isolated >= 0);
     771      assert(config->use_environment >= 0);
     772      assert(config->dev_mode >= 0);
     773  
     774      return _PyStatus_OK();
     775  }
     776  
     777  
     778  /* Read the configuration from:
     779  
     780     - command line arguments
     781     - environment variables
     782     - Py_xxx global configuration variables
     783     - the LC_CTYPE locale */
     784  PyStatus
     785  _PyPreConfig_Read(PyPreConfig *config, const _PyArgv *args)
     786  {
     787      PyStatus status;
     788  
     789      status = _PyRuntime_Initialize();
     790      if (_PyStatus_EXCEPTION(status)) {
     791          return status;
     792      }
     793  
     794      preconfig_get_global_vars(config);
     795  
     796      /* Copy LC_CTYPE locale, since it's modified later */
     797      const char *loc = setlocale(LC_CTYPE, NULL);
     798      if (loc == NULL) {
     799          return _PyStatus_ERR("failed to LC_CTYPE locale");
     800      }
     801      char *init_ctype_locale = _PyMem_RawStrdup(loc);
     802      if (init_ctype_locale == NULL) {
     803          return _PyStatus_NO_MEMORY();
     804      }
     805  
     806      /* Save the config to be able to restore it if encodings change */
     807      PyPreConfig save_config;
     808  
     809      status = _PyPreConfig_InitFromPreConfig(&save_config, config);
     810      if (_PyStatus_EXCEPTION(status)) {
     811          return status;
     812      }
     813  
     814      /* Set LC_CTYPE to the user preferred locale */
     815      if (config->configure_locale) {
     816          _Py_SetLocaleFromEnv(LC_CTYPE);
     817      }
     818  
     819      _PyPreCmdline cmdline = _PyPreCmdline_INIT;
     820      int init_utf8_mode = Py_UTF8Mode;
     821  #ifdef MS_WINDOWS
     822      int init_legacy_encoding = Py_LegacyWindowsFSEncodingFlag;
     823  #endif
     824  
     825      int locale_coerced = 0;
     826      int loops = 0;
     827  
     828      while (1) {
     829          int utf8_mode = config->utf8_mode;
     830  
     831          /* Watchdog to prevent an infinite loop */
     832          loops++;
     833          if (loops == 3) {
     834              status = _PyStatus_ERR("Encoding changed twice while "
     835                                     "reading the configuration");
     836              goto done;
     837          }
     838  
     839          /* bpo-34207: Py_DecodeLocale() and Py_EncodeLocale() depend
     840             on Py_UTF8Mode and Py_LegacyWindowsFSEncodingFlag. */
     841          Py_UTF8Mode = config->utf8_mode;
     842  #ifdef MS_WINDOWS
     843          Py_LegacyWindowsFSEncodingFlag = config->legacy_windows_fs_encoding;
     844  #endif
     845  
     846          if (args) {
     847              // Set command line arguments at each iteration. If they are bytes
     848              // strings, they are decoded from the new encoding.
     849              status = _PyPreCmdline_SetArgv(&cmdline, args);
     850              if (_PyStatus_EXCEPTION(status)) {
     851                  goto done;
     852              }
     853          }
     854  
     855          status = preconfig_read(config, &cmdline);
     856          if (_PyStatus_EXCEPTION(status)) {
     857              goto done;
     858          }
     859  
     860          /* The legacy C locale assumes ASCII as the default text encoding, which
     861           * causes problems not only for the CPython runtime, but also other
     862           * components like GNU readline.
     863           *
     864           * Accordingly, when the CLI detects it, it attempts to coerce it to a
     865           * more capable UTF-8 based alternative.
     866           *
     867           * See the documentation of the PYTHONCOERCECLOCALE setting for more
     868           * details.
     869           */
     870          int encoding_changed = 0;
     871          if (config->coerce_c_locale && !locale_coerced) {
     872              locale_coerced = 1;
     873              _Py_CoerceLegacyLocale(0);
     874              encoding_changed = 1;
     875          }
     876  
     877          if (utf8_mode == -1) {
     878              if (config->utf8_mode == 1) {
     879                  /* UTF-8 Mode enabled */
     880                  encoding_changed = 1;
     881              }
     882          }
     883          else {
     884              if (config->utf8_mode != utf8_mode) {
     885                  encoding_changed = 1;
     886              }
     887          }
     888  
     889          if (!encoding_changed) {
     890              break;
     891          }
     892  
     893          /* Reset the configuration before reading again the configuration,
     894             just keep UTF-8 Mode and coerce C locale value. */
     895          int new_utf8_mode = config->utf8_mode;
     896          int new_coerce_c_locale = config->coerce_c_locale;
     897          preconfig_copy(config, &save_config);
     898          config->utf8_mode = new_utf8_mode;
     899          config->coerce_c_locale = new_coerce_c_locale;
     900  
     901          /* The encoding changed: read again the configuration
     902             with the new encoding */
     903      }
     904      status = _PyStatus_OK();
     905  
     906  done:
     907      if (init_ctype_locale != NULL) {
     908          setlocale(LC_CTYPE, init_ctype_locale);
     909          PyMem_RawFree(init_ctype_locale);
     910      }
     911      Py_UTF8Mode = init_utf8_mode ;
     912  #ifdef MS_WINDOWS
     913      Py_LegacyWindowsFSEncodingFlag = init_legacy_encoding;
     914  #endif
     915      _PyPreCmdline_Clear(&cmdline);
     916      return status;
     917  }
     918  
     919  
     920  /* Write the pre-configuration:
     921  
     922     - set the memory allocators
     923     - set Py_xxx global configuration variables
     924     - set the LC_CTYPE locale (coerce C locale, PEP 538) and set the UTF-8 mode
     925       (PEP 540)
     926  
     927     The applied configuration is written into _PyRuntime.preconfig.
     928     If the C locale cannot be coerced, set coerce_c_locale to 0.
     929  
     930     Do nothing if called after Py_Initialize(): ignore the new
     931     pre-configuration. */
     932  PyStatus
     933  _PyPreConfig_Write(const PyPreConfig *src_config)
     934  {
     935      PyPreConfig config;
     936  
     937      PyStatus status = _PyPreConfig_InitFromPreConfig(&config, src_config);
     938      if (_PyStatus_EXCEPTION(status)) {
     939          return status;
     940      }
     941  
     942      if (_PyRuntime.core_initialized) {
     943          /* bpo-34008: Calling this functions after Py_Initialize() ignores
     944             the new configuration. */
     945          return _PyStatus_OK();
     946      }
     947  
     948      PyMemAllocatorName name = (PyMemAllocatorName)config.allocator;
     949      if (name != PYMEM_ALLOCATOR_NOT_SET) {
     950          if (_PyMem_SetupAllocators(name) < 0) {
     951              return _PyStatus_ERR("Unknown PYTHONMALLOC allocator");
     952          }
     953      }
     954  
     955      preconfig_set_global_vars(&config);
     956  
     957      if (config.configure_locale) {
     958          if (config.coerce_c_locale) {
     959              if (!_Py_CoerceLegacyLocale(config.coerce_c_locale_warn)) {
     960                  /* C locale not coerced */
     961                  config.coerce_c_locale = 0;
     962              }
     963          }
     964  
     965          /* Set LC_CTYPE to the user preferred locale */
     966          _Py_SetLocaleFromEnv(LC_CTYPE);
     967      }
     968  
     969      /* Write the new pre-configuration into _PyRuntime */
     970      preconfig_copy(&_PyRuntime.preconfig, &config);
     971  
     972      return _PyStatus_OK();
     973  }