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