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