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