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