Coverage Report

Created: 2025-07-11 06:59

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