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