Coverage Report

Created: 2022-07-08 09:39

/home/mdboom/Work/builds/cpython/Python/preconfig.c
Line
Count
Source (jump to first uncovered line)
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) {
  Branch (29:9): [True: 381, False: 0]
  Branch (29:45): [True: 274, False: 107]
30
        PyMem_RawFree((char*)Py_FileSystemDefaultEncoding);
31
        Py_FileSystemDefaultEncoding = NULL;
32
    }
33
    if (!_Py_HasFileSystemDefaultEncodeErrors && Py_FileSystemDefaultEncodeErrors) {
  Branch (33:9): [True: 381, False: 0]
  Branch (33:50): [True: 274, False: 107]
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) {
  Branch (50:9): [True: 0, False: 278]
51
        return -1;
52
    }
53
54
    char *errors2 = _PyMem_RawStrdup(errors);
55
    if (errors2 == NULL) {
  Branch (55:9): [True: 0, False: 278]
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) {
  Branch (81:9): [True: 23, False: 60]
82
        size_t size = sizeof(wchar_t*) * args->argc;
83
        wargv.items = (wchar_t **)PyMem_RawMalloc(size);
84
        if (wargv.items == NULL) {
  Branch (84:13): [True: 0, False: 23]
85
            return _PyStatus_NO_MEMORY();
86
        }
87
88
        
for (Py_ssize_t i = 0; 23
i < args->argc;
i++72
) {
  Branch (88:32): [True: 72, False: 23]
89
            size_t len;
90
            wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
91
            if (arg == NULL) {
  Branch (91:17): [True: 0, False: 72]
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) {
  Branch (105:13): [True: 0, False: 60]
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'88
||
c == 'm'28
) {
  Branch (195:13): [True: 33, False: 88]
  Branch (195:25): [True: 60, False: 28]
  Branch (195:37): [True: 2, False: 26]
196
            break;
197
        }
198
199
        switch (c) {
200
        case 'E':
  Branch (200:9): [True: 2, False: 24]
201
            cmdline->use_environment = 0;
202
            break;
203
204
        case 'I':
  Branch (204:9): [True: 0, False: 26]
205
            cmdline->isolated = 1;
206
            break;
207
208
        case 'X':
  Branch (208:9): [True: 14, False: 12]
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:
  Branch (218:9): [True: 10, False: 16]
219
            /* ignore other argument:
220
               handled by PyConfig_Read() */
221
            break;
222
        }
223
    } while (1);
  Branch (223:14): [Folded - Ignored]
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) {
  Branch (234:9): [True: 95, False: 126]
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) {
  Branch (242:9): [True: 0, False: 221]
243
        cmdline->isolated = 0;
244
    }
245
    if (cmdline->isolated > 0) {
  Branch (245:9): [True: 20, False: 201]
246
        cmdline->use_environment = 0;
247
    }
248
    if (cmdline->use_environment < 0) {
  Branch (248:9): [True: 0, False: 221]
249
        cmdline->use_environment = 0;
250
    }
251
252
    /* dev_mode */
253
    if ((cmdline->dev_mode < 0)
  Branch (253:9): [True: 101, False: 120]
254
        && 
(101
_Py_get_xoption(&cmdline->xoptions, L"dev")101
  Branch (254:13): [True: 1, False: 100]
255
            || 
_Py_GetEnv(cmdline->use_environment, "PYTHONDEVMODE")100
))
  Branch (255:16): [True: 2, False: 98]
256
    {
257
        cmdline->dev_mode = 1;
258
    }
259
    if (cmdline->dev_mode < 0) {
  Branch (259:9): [True: 98, False: 123]
260
        cmdline->dev_mode = 0;
261
    }
262
263
    // warn_default_encoding
264
    if (_Py_get_xoption(&cmdline->xoptions, L"warn_default_encoding")
  Branch (264:9): [True: 0, False: 221]
265
            || _Py_GetEnv(cmdline->use_environment, "PYTHONWARNDEFAULTENCODING"))
  Branch (265:16): [True: 0, False: 221]
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:
  Branch (363:5): [True: 30, False: 55]
364
        PyPreConfig_InitPythonConfig(preconfig);
365
        break;
366
    case _PyConfig_INIT_ISOLATED:
  Branch (366:5): [True: 2, False: 83]
367
        PyPreConfig_InitIsolatedConfig(preconfig);
368
        break;
369
    case _PyConfig_INIT_COMPAT:
  Branch (369:5): [True: 53, False: 32]
370
    default:
  Branch (370:5): [True: 0, False: 85]
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) {
  Branch (407:9): [True: 0, False: 39]
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) {
  Branch (467:9): [True: 50, False: 57]
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) {
  Branch (485:9): [True: 1, False: 56]
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) {
  Branch (530:9): [True: 113, False: 2.09k]
531
        return NULL;
532
    }
533
534
    const char *var = getenv(name);
535
    if (var && 
var[0] != '\0'64
) {
  Branch (535:9): [True: 64, False: 2.03k]
  Branch (535:16): [True: 50, False: 14]
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) {
  Branch (550:9): [True: 0, False: 20]
  Branch (550:28): [True: 0, False: 20]
551
        return -1;
552
    }
553
    if (value < INT_MIN || value > INT_MAX) {
  Branch (553:9): [True: 0, False: 20]
  Branch (553:28): [True: 0, False: 20]
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) {
  Branch (566:9): [True: 668, False: 18]
567
        return;
568
    }
569
    int value;
570
    if (_Py_str_to_int(var, &value) < 0 || value < 0) {
  Branch (570:9): [True: 0, False: 18]
  Branch (570:44): [True: 0, False: 18]
571
        /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */
572
        value = 1;
573
    }
574
    if (*flag < value) {
  Branch (574:9): [True: 13, False: 5]
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++105
) {
  Branch (583:26): [True: 116, False: 1.38k]
584
        const wchar_t *option = xoptions->items[i];
585
        size_t len;
586
        wchar_t *sep = wcschr(option, L'=');
587
        if (sep != NULL) {
  Branch (587:13): [True: 55, False: 61]
588
            len = (sep - option);
589
        }
590
        else {
591
            len = wcslen(option);
592
        }
593
        if (wcsncmp(option, name, len) == 0 && 
name[len] == L'\0'11
) {
  Branch (593:13): [True: 11, False: 105]
  Branch (593:48): [True: 11, False: 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) {
  Branch (610:9): [True: 64, False: 46]
611
        return _PyStatus_OK();
612
    }
613
614
    const wchar_t *xopt;
615
    xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8");
616
    if (xopt) {
  Branch (616:9): [True: 1, False: 45]
617
        wchar_t *sep = wcschr(xopt, L'=');
618
        if (sep) {
  Branch (618:13): [True: 0, False: 1]
619
            xopt = sep + 1;
620
            if (wcscmp(xopt, L"1") == 0) {
  Branch (620:17): [True: 0, False: 0]
621
                config->utf8_mode = 1;
622
            }
623
            else if (wcscmp(xopt, L"0") == 0) {
  Branch (623:22): [True: 0, False: 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) {
  Branch (637:9): [True: 1, False: 44]
638
        if (strcmp(opt, "1") == 0) {
  Branch (638:13): [True: 1, False: 0]
639
            config->utf8_mode = 1;
640
        }
641
        else if (strcmp(opt, "0") == 0) {
  Branch (641:18): [True: 0, False: 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) {
  Branch (653:9): [True: 44, False: 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
  Branch (656:13): [True: 44, False: 0]
657
           && (strcmp(ctype_loc, "C") == 0
  Branch (657:16): [True: 1, False: 43]
658
               || 
strcmp(ctype_loc, "POSIX") == 043
))
  Branch (658:19): [True: 0, False: 43]
659
        {
660
            config->utf8_mode = 1;
661
        }
662
    }
663
#endif
664
665
    if (config->utf8_mode < 0) {
  Branch (665:9): [True: 43, False: 1]
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) {
  Branch (675:9): [True: 6, False: 104]
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) {
  Branch (682:9): [True: 0, False: 104]
683
        if (strcmp(env, "0") == 0) {
  Branch (683:13): [True: 0, False: 0]
684
            if (config->coerce_c_locale < 0) {
  Branch (684:17): [True: 0, False: 0]
685
                config->coerce_c_locale = 0;
686
            }
687
        }
688
        else if (strcmp(env, "warn") == 0) {
  Branch (688:18): [True: 0, False: 0]
689
            if (config->coerce_c_locale_warn < 0) {
  Branch (689:17): [True: 0, False: 0]
690
                config->coerce_c_locale_warn = 1;
691
            }
692
        }
693
        else {
694
            if (config->coerce_c_locale < 0) {
  Branch (694:17): [True: 0, False: 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 == 159
) {
  Branch (703:9): [True: 45, False: 59]
  Branch (703:40): [True: 0, False: 59]
704
        /* The C locale enables the C locale coercion (PEP 538) */
705
        if (_Py_LegacyLocaleDetected(0)) {
  Branch (705:13): [True: 0, False: 45]
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) {
  Branch (713:9): [True: 47, False: 57]
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) {
  Branch (722:9): [True: 109, False: 1]
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) {
  Branch (728:13): [True: 4, False: 105]
729
            PyMemAllocatorName name;
730
            if (_PyMem_GetAllocatorName(envvar, &name) < 0) {
  Branch (730:17): [True: 0, False: 4]
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_SET5
) {
  Branch (737:9): [True: 5, False: 105]
  Branch (737:29): [True: 4, False: 1]
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) {
  Branch (810:9): [True: 0, False: 107]
811
        return _PyStatus_ERR("failed to LC_CTYPE locale");
812
    }
813
    char *init_ctype_locale = _PyMem_RawStrdup(loc);
814
    if (init_ctype_locale == NULL) {
  Branch (814:9): [True: 0, False: 107]
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) {
  Branch (827:9): [True: 102, False: 5]
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) {
  Branch (838:12): [Folded - Ignored]
839
        int utf8_mode = config->utf8_mode;
840
841
        /* Watchdog to prevent an infinite loop */
842
        loops++;
843
        if (loops == 3) {
  Branch (843:13): [True: 0, False: 110]
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) {
  Branch (854:13): [True: 44, False: 66]
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_coerced0
) {
  Branch (879:13): [True: 0, False: 110]
  Branch (879:40): [True: 0, False: 0]
880
            locale_coerced = 1;
881
            _Py_CoerceLegacyLocale(0);
882
            encoding_changed = 1;
883
        }
884
885
        if (utf8_mode == -1) {
  Branch (885:13): [True: 46, False: 64]
886
            if (config->utf8_mode == 1) {
  Branch (886:17): [True: 3, False: 43]
887
                /* UTF-8 Mode enabled */
888
                encoding_changed = 1;
889
            }
890
        }
891
        else {
892
            if (config->utf8_mode != utf8_mode) {
  Branch (892:17): [True: 0, False: 64]
893
                encoding_changed = 1;
894
            }
895
        }
896
897
        if (!encoding_changed) {
  Branch (897:13): [True: 107, False: 3]
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) {
  Branch (946:9): [True: 0, False: 107]
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) {
  Branch (953:9): [True: 8, False: 99]
954
        if (_PyMem_SetupAllocators(name) < 0) {
  Branch (954:13): [True: 0, False: 8]
955
            return _PyStatus_ERR("Unknown PYTHONMALLOC allocator");
956
        }
957
    }
958
959
    preconfig_set_global_vars(&config);
960
961
    if (config.configure_locale) {
  Branch (961:9): [True: 102, False: 5]
962
        if (config.coerce_c_locale) {
  Branch (962:13): [True: 0, False: 102]
963
            if (!_Py_CoerceLegacyLocale(config.coerce_c_locale_warn)) {
  Branch (963:17): [True: 0, False: 0]
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
}