Branch data Line data Source code
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 : 6090 : _Py_ClearFileSystemEncoding(void)
26 : : {
27 : : _Py_COMP_DIAG_PUSH
28 : : _Py_COMP_DIAG_IGNORE_DEPR_DECLS
29 [ + - + + ]: 6090 : if (!Py_HasFileSystemDefaultEncoding && Py_FileSystemDefaultEncoding) {
30 : 3127 : PyMem_RawFree((char*)Py_FileSystemDefaultEncoding);
31 : 3127 : Py_FileSystemDefaultEncoding = NULL;
32 : : }
33 [ + - + + ]: 6090 : if (!_Py_HasFileSystemDefaultEncodeErrors && Py_FileSystemDefaultEncodeErrors) {
34 : 3127 : PyMem_RawFree((char*)Py_FileSystemDefaultEncodeErrors);
35 : 3127 : Py_FileSystemDefaultEncodeErrors = NULL;
36 : : }
37 : : _Py_COMP_DIAG_POP
38 : 6090 : }
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 : 3134 : _Py_SetFileSystemEncoding(const char *encoding, const char *errors)
48 : : {
49 : 3134 : char *encoding2 = _PyMem_RawStrdup(encoding);
50 [ - + ]: 3134 : if (encoding2 == NULL) {
51 : 0 : return -1;
52 : : }
53 : :
54 : 3134 : char *errors2 = _PyMem_RawStrdup(errors);
55 [ - + ]: 3134 : if (errors2 == NULL) {
56 : 0 : PyMem_RawFree(encoding2);
57 : 0 : return -1;
58 : : }
59 : :
60 : 3134 : _Py_ClearFileSystemEncoding();
61 : :
62 : : _Py_COMP_DIAG_PUSH
63 : : _Py_COMP_DIAG_IGNORE_DEPR_DECLS
64 : 3134 : Py_FileSystemDefaultEncoding = encoding2;
65 : 3134 : Py_HasFileSystemDefaultEncoding = 0;
66 : :
67 : 3134 : Py_FileSystemDefaultEncodeErrors = errors2;
68 : 3134 : _Py_HasFileSystemDefaultEncodeErrors = 0;
69 : : _Py_COMP_DIAG_POP
70 : 3134 : return 0;
71 : : }
72 : :
73 : :
74 : : /* --- _PyArgv ---------------------------------------------------- */
75 : :
76 : : /* Decode bytes_argv using Py_DecodeLocale() */
77 : : PyStatus
78 : 5933 : _PyArgv_AsWstrList(const _PyArgv *args, PyWideStringList *list)
79 : : {
80 : 5933 : PyWideStringList wargv = _PyWideStringList_INIT;
81 [ + + ]: 5933 : if (args->use_bytes_argv) {
82 : 5873 : size_t size = sizeof(wchar_t*) * args->argc;
83 : 5873 : wargv.items = (wchar_t **)PyMem_RawMalloc(size);
84 [ - + ]: 5873 : if (wargv.items == NULL) {
85 : 0 : return _PyStatus_NO_MEMORY();
86 : : }
87 : :
88 [ + + ]: 42670 : for (Py_ssize_t i = 0; i < args->argc; i++) {
89 : : size_t len;
90 : 36797 : wchar_t *arg = Py_DecodeLocale(args->bytes_argv[i], &len);
91 [ - + ]: 36797 : if (arg == NULL) {
92 : 0 : _PyWideStringList_Clear(&wargv);
93 [ # # ]: 0 : return DECODE_LOCALE_ERR("command line arguments", len);
94 : : }
95 : 36797 : wargv.items[i] = arg;
96 : 36797 : wargv.length++;
97 : : }
98 : :
99 : 5873 : _PyWideStringList_Clear(list);
100 : 5873 : *list = wargv;
101 : : }
102 : : else {
103 : 60 : wargv.length = args->argc;
104 : 60 : wargv.items = (wchar_t **)args->wchar_argv;
105 [ - + ]: 60 : if (_PyWideStringList_Copy(list, &wargv) < 0) {
106 : 0 : return _PyStatus_NO_MEMORY();
107 : : }
108 : : }
109 : 5933 : return _PyStatus_OK();
110 : : }
111 : :
112 : :
113 : : /* --- _PyPreCmdline ------------------------------------------------- */
114 : :
115 : : void
116 : 6041 : _PyPreCmdline_Clear(_PyPreCmdline *cmdline)
117 : : {
118 : 6041 : _PyWideStringList_Clear(&cmdline->argv);
119 : 6041 : _PyWideStringList_Clear(&cmdline->xoptions);
120 : 6041 : }
121 : :
122 : :
123 : : PyStatus
124 : 3020 : _PyPreCmdline_SetArgv(_PyPreCmdline *cmdline, const _PyArgv *args)
125 : : {
126 : 3020 : return _PyArgv_AsWstrList(args, &cmdline->argv);
127 : : }
128 : :
129 : :
130 : : static void
131 : 6145 : 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 [ + - ]: 6145 : COPY_ATTR(isolated);
139 [ + - ]: 6145 : COPY_ATTR(use_environment);
140 [ + + ]: 6145 : COPY_ATTR(dev_mode);
141 : :
142 : : #undef COPY_ATTR
143 : 6145 : }
144 : :
145 : :
146 : : static void
147 : 3092 : precmdline_set_preconfig(const _PyPreCmdline *cmdline, PyPreConfig *config)
148 : : {
149 : : #define COPY_ATTR(ATTR) \
150 : : config->ATTR = cmdline->ATTR
151 : :
152 : 3092 : COPY_ATTR(isolated);
153 : 3092 : COPY_ATTR(use_environment);
154 : 3092 : COPY_ATTR(dev_mode);
155 : :
156 : : #undef COPY_ATTR
157 : 3092 : }
158 : :
159 : :
160 : : PyStatus
161 : 3053 : _PyPreCmdline_SetConfig(const _PyPreCmdline *cmdline, PyConfig *config)
162 : : {
163 : : #define COPY_ATTR(ATTR) \
164 : : config->ATTR = cmdline->ATTR
165 : :
166 : 3053 : PyStatus status = _PyWideStringList_Extend(&config->xoptions, &cmdline->xoptions);
167 [ - + ]: 3053 : if (_PyStatus_EXCEPTION(status)) {
168 : 0 : return status;
169 : : }
170 : :
171 : 3053 : COPY_ATTR(isolated);
172 : 3053 : COPY_ATTR(use_environment);
173 : 3053 : COPY_ATTR(dev_mode);
174 : 3053 : COPY_ATTR(warn_default_encoding);
175 : 3053 : return _PyStatus_OK();
176 : :
177 : : #undef COPY_ATTR
178 : : }
179 : :
180 : :
181 : : /* Parse the command line arguments */
182 : : static PyStatus
183 : 5997 : precmdline_parse_cmdline(_PyPreCmdline *cmdline)
184 : : {
185 : 5997 : const PyWideStringList *argv = &cmdline->argv;
186 : :
187 : 5997 : _PyOS_ResetGetOpt();
188 : : /* Don't log parsing errors into stderr here: PyConfig_Read()
189 : : is responsible for that */
190 : 5997 : _PyOS_opterr = 0;
191 : 13610 : do {
192 : 19607 : int longindex = -1;
193 : 19607 : int c = _PyOS_GetOpt(argv->length, argv->items, &longindex);
194 : :
195 [ + + + + : 19607 : if (c == EOF || c == 'c' || c == 'm') {
+ + ]
196 : : break;
197 : : }
198 : :
199 [ + + + + ]: 13610 : switch (c) {
200 : 2375 : case 'E':
201 : 2375 : cmdline->use_environment = 0;
202 : 2375 : break;
203 : :
204 : 1367 : case 'I':
205 : 1367 : cmdline->isolated = 1;
206 : 1367 : break;
207 : :
208 : 2721 : case 'X':
209 : : {
210 : 2721 : PyStatus status = PyWideStringList_Append(&cmdline->xoptions,
211 : : _PyOS_optarg);
212 [ - + ]: 2721 : if (_PyStatus_EXCEPTION(status)) {
213 : 0 : return status;
214 : : }
215 : 2721 : break;
216 : : }
217 : :
218 : 7147 : default:
219 : : /* ignore other argument:
220 : : handled by PyConfig_Read() */
221 : 7147 : break;
222 : : }
223 : : } while (1);
224 : :
225 : 5997 : return _PyStatus_OK();
226 : : }
227 : :
228 : :
229 : : PyStatus
230 : 6145 : _PyPreCmdline_Read(_PyPreCmdline *cmdline, const PyPreConfig *preconfig)
231 : : {
232 : 6145 : precmdline_get_preconfig(cmdline, preconfig);
233 : :
234 [ + + ]: 6145 : if (preconfig->parse_argv) {
235 : 5997 : PyStatus status = precmdline_parse_cmdline(cmdline);
236 [ - + ]: 5997 : if (_PyStatus_EXCEPTION(status)) {
237 : 0 : return status;
238 : : }
239 : : }
240 : :
241 : : /* isolated, use_environment */
242 [ - + ]: 6145 : if (cmdline->isolated < 0) {
243 : 0 : cmdline->isolated = 0;
244 : : }
245 [ + + ]: 6145 : if (cmdline->isolated > 0) {
246 : 1428 : cmdline->use_environment = 0;
247 : : }
248 [ - + ]: 6145 : if (cmdline->use_environment < 0) {
249 : 0 : cmdline->use_environment = 0;
250 : : }
251 : :
252 : : /* dev_mode */
253 [ + + ]: 6145 : if ((cmdline->dev_mode < 0)
254 [ + + ]: 2957 : && (_Py_get_xoption(&cmdline->xoptions, L"dev")
255 [ + + ]: 2928 : || _Py_GetEnv(cmdline->use_environment, "PYTHONDEVMODE")))
256 : : {
257 : 32 : cmdline->dev_mode = 1;
258 : : }
259 [ + + ]: 6145 : if (cmdline->dev_mode < 0) {
260 : 2925 : cmdline->dev_mode = 0;
261 : : }
262 : :
263 : : // warn_default_encoding
264 [ + + ]: 6145 : if (_Py_get_xoption(&cmdline->xoptions, L"warn_default_encoding")
265 [ - + ]: 6139 : || _Py_GetEnv(cmdline->use_environment, "PYTHONWARNDEFAULTENCODING"))
266 : : {
267 : 6 : 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 : 6145 : return _PyStatus_OK();
276 : : }
277 : :
278 : :
279 : : /* --- PyPreConfig ----------------------------------------------- */
280 : :
281 : :
282 : : void
283 : 17993 : _PyPreConfig_InitCompatConfig(PyPreConfig *config)
284 : : {
285 : 17993 : memset(config, 0, sizeof(*config));
286 : :
287 : 17993 : config->_config_init = (int)_PyConfig_INIT_COMPAT;
288 : 17993 : config->parse_argv = 0;
289 : 17993 : config->isolated = -1;
290 : 17993 : config->use_environment = -1;
291 : 17993 : 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 : 17993 : config->utf8_mode = 0;
299 : 17993 : config->coerce_c_locale = 0;
300 : 17993 : config->coerce_c_locale_warn = 0;
301 : :
302 : 17993 : config->dev_mode = -1;
303 : 17993 : config->allocator = PYMEM_ALLOCATOR_NOT_SET;
304 : : #ifdef MS_WINDOWS
305 : : config->legacy_windows_fs_encoding = -1;
306 : : #endif
307 : 17993 : }
308 : :
309 : :
310 : : void
311 : 17905 : PyPreConfig_InitPythonConfig(PyPreConfig *config)
312 : : {
313 : 17905 : _PyPreConfig_InitCompatConfig(config);
314 : :
315 : 17905 : config->_config_init = (int)_PyConfig_INIT_PYTHON;
316 : 17905 : config->isolated = 0;
317 : 17905 : config->parse_argv = 1;
318 : 17905 : 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 : 17905 : config->coerce_c_locale = -1;
323 : 17905 : config->coerce_c_locale_warn = -1;
324 : 17905 : config->utf8_mode = -1;
325 : : #ifdef MS_WINDOWS
326 : : config->legacy_windows_fs_encoding = 0;
327 : : #endif
328 : 17905 : }
329 : :
330 : :
331 : : void
332 : 29 : PyPreConfig_InitIsolatedConfig(PyPreConfig *config)
333 : : {
334 : 29 : _PyPreConfig_InitCompatConfig(config);
335 : :
336 : 29 : config->_config_init = (int)_PyConfig_INIT_ISOLATED;
337 : 29 : config->configure_locale = 0;
338 : 29 : config->isolated = 1;
339 : 29 : config->use_environment = 0;
340 : 29 : config->utf8_mode = 0;
341 : 29 : config->dev_mode = 0;
342 : : #ifdef MS_WINDOWS
343 : : config->legacy_windows_fs_encoding = 0;
344 : : #endif
345 : 29 : }
346 : :
347 : :
348 : : PyStatus
349 : 12016 : _PyPreConfig_InitFromPreConfig(PyPreConfig *config,
350 : : const PyPreConfig *config2)
351 : : {
352 : 12016 : PyPreConfig_InitPythonConfig(config);
353 : 12016 : preconfig_copy(config, config2);
354 : 12016 : return _PyStatus_OK();
355 : : }
356 : :
357 : :
358 : : void
359 : 112 : _PyPreConfig_InitFromConfig(PyPreConfig *preconfig, const PyConfig *config)
360 : : {
361 : 112 : _PyConfigInitEnum config_init = (_PyConfigInitEnum)config->_config_init;
362 [ + + + ]: 112 : switch (config_init) {
363 : 30 : case _PyConfig_INIT_PYTHON:
364 : 30 : PyPreConfig_InitPythonConfig(preconfig);
365 : 30 : break;
366 : 27 : case _PyConfig_INIT_ISOLATED:
367 : 27 : PyPreConfig_InitIsolatedConfig(preconfig);
368 : 27 : break;
369 : 55 : case _PyConfig_INIT_COMPAT:
370 : : default:
371 : 55 : _PyPreConfig_InitCompatConfig(preconfig);
372 : : }
373 : :
374 : 112 : _PyPreConfig_GetConfig(preconfig, config);
375 : 112 : }
376 : :
377 : :
378 : : static void
379 : 24175 : preconfig_copy(PyPreConfig *config, const PyPreConfig *config2)
380 : : {
381 : : #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
382 : :
383 : 24175 : COPY_ATTR(_config_init);
384 : 24175 : COPY_ATTR(parse_argv);
385 : 24175 : COPY_ATTR(isolated);
386 : 24175 : COPY_ATTR(use_environment);
387 : 24175 : COPY_ATTR(configure_locale);
388 : 24175 : COPY_ATTR(dev_mode);
389 : 24175 : COPY_ATTR(coerce_c_locale);
390 : 24175 : COPY_ATTR(coerce_c_locale_warn);
391 : 24175 : COPY_ATTR(utf8_mode);
392 : 24175 : COPY_ATTR(allocator);
393 : : #ifdef MS_WINDOWS
394 : : COPY_ATTR(legacy_windows_fs_encoding);
395 : : #endif
396 : :
397 : : #undef COPY_ATTR
398 : 24175 : }
399 : :
400 : :
401 : : PyObject*
402 : 41 : _PyPreConfig_AsDict(const PyPreConfig *config)
403 : : {
404 : : PyObject *dict;
405 : :
406 : 41 : dict = PyDict_New();
407 [ - + ]: 41 : if (dict == NULL) {
408 : 0 : 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 [ - + - + ]: 41 : SET_ITEM_INT(_config_init);
425 [ - + - + ]: 41 : SET_ITEM_INT(parse_argv);
426 [ - + - + ]: 41 : SET_ITEM_INT(isolated);
427 [ - + - + ]: 41 : SET_ITEM_INT(use_environment);
428 [ - + - + ]: 41 : SET_ITEM_INT(configure_locale);
429 [ - + - + ]: 41 : SET_ITEM_INT(coerce_c_locale);
430 [ - + - + ]: 41 : SET_ITEM_INT(coerce_c_locale_warn);
431 [ - + - + ]: 41 : SET_ITEM_INT(utf8_mode);
432 : : #ifdef MS_WINDOWS
433 : : SET_ITEM_INT(legacy_windows_fs_encoding);
434 : : #endif
435 [ - + - + ]: 41 : SET_ITEM_INT(dev_mode);
436 [ - + - + ]: 41 : SET_ITEM_INT(allocator);
437 : 41 : return dict;
438 : :
439 : 0 : fail:
440 : 0 : Py_DECREF(dict);
441 : 0 : return NULL;
442 : :
443 : : #undef SET_ITEM_INT
444 : : }
445 : :
446 : :
447 : : void
448 : 3165 : _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 [ + - ]: 3165 : COPY_ATTR(parse_argv);
456 [ + + ]: 3165 : COPY_ATTR(isolated);
457 [ + + ]: 3165 : COPY_ATTR(use_environment);
458 [ + + ]: 3165 : COPY_ATTR(dev_mode);
459 : :
460 : : #undef COPY_ATTR
461 : 3165 : }
462 : :
463 : :
464 : : static void
465 : 2988 : preconfig_get_global_vars(PyPreConfig *config)
466 : : {
467 [ + + ]: 2988 : if (config->_config_init != _PyConfig_INIT_COMPAT) {
468 : : /* Python and Isolated configuration ignore global variables */
469 : 2929 : 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 [ + + ]: 59 : COPY_FLAG(isolated, Py_IsolatedFlag);
484 [ + - ]: 59 : COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
485 [ + + ]: 59 : if (Py_UTF8Mode > 0) {
486 : 1 : 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 : 2987 : 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 [ + - ]: 2987 : COPY_FLAG(isolated, Py_IsolatedFlag);
513 [ + - ]: 2987 : COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
514 : : #ifdef MS_WINDOWS
515 : : COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
516 : : #endif
517 [ + - ]: 2987 : COPY_FLAG(utf8_mode, Py_UTF8Mode);
518 : : _Py_COMP_DIAG_POP
519 : :
520 : : #undef COPY_FLAG
521 : : #undef COPY_NOT_FLAG
522 : 2987 : }
523 : :
524 : :
525 : : const char*
526 : 47222 : _Py_GetEnv(int use_environment, const char *name)
527 : : {
528 : : assert(use_environment >= 0);
529 : :
530 [ + + ]: 47222 : if (!use_environment) {
531 : 21859 : return NULL;
532 : : }
533 : :
534 : 25363 : const char *var = getenv(name);
535 [ + + + + ]: 25363 : if (var && var[0] != '\0') {
536 : 457 : return var;
537 : : }
538 : : else {
539 : 24906 : return NULL;
540 : : }
541 : : }
542 : :
543 : :
544 : : int
545 : 38 : _Py_str_to_int(const char *str, int *result)
546 : : {
547 : 38 : const char *endptr = str;
548 : 38 : errno = 0;
549 : 38 : long value = strtol(str, (char **)&endptr, 10);
550 [ + + - + ]: 38 : if (*endptr != '\0' || errno == ERANGE) {
551 : 4 : return -1;
552 : : }
553 [ + - - + ]: 34 : if (value < INT_MIN || value > INT_MAX) {
554 : 0 : return -1;
555 : : }
556 : :
557 : 34 : *result = (int)value;
558 : 34 : return 0;
559 : : }
560 : :
561 : :
562 : : void
563 : 7756 : _Py_get_env_flag(int use_environment, int *flag, const char *name)
564 : : {
565 : 7756 : const char *var = _Py_GetEnv(use_environment, name);
566 [ + + ]: 7756 : if (!var) {
567 : 7725 : return;
568 : : }
569 : : int value;
570 [ + + - + ]: 31 : if (_Py_str_to_int(var, &value) < 0 || value < 0) {
571 : : /* PYTHONDEBUG=text and PYTHONDEBUG=-2 behave as PYTHONDEBUG=1 */
572 : 4 : value = 1;
573 : : }
574 [ + + ]: 31 : if (*flag < value) {
575 : 26 : *flag = value;
576 : : }
577 : : }
578 : :
579 : :
580 : : const wchar_t*
581 : 36170 : _Py_get_xoption(const PyWideStringList *xoptions, const wchar_t *name)
582 : : {
583 [ + + ]: 50555 : for (Py_ssize_t i=0; i < xoptions->length; i++) {
584 : 15662 : const wchar_t *option = xoptions->items[i];
585 : : size_t len;
586 : 15662 : wchar_t *sep = wcschr(option, L'=');
587 [ + + ]: 15662 : if (sep != NULL) {
588 : 1348 : len = (sep - option);
589 : : }
590 : : else {
591 : 14314 : len = wcslen(option);
592 : : }
593 [ + + + - ]: 15662 : if (wcsncmp(option, name, len) == 0 && name[len] == L'\0') {
594 : 1277 : return option;
595 : : }
596 : : }
597 : 34893 : return NULL;
598 : : }
599 : :
600 : :
601 : : static PyStatus
602 : 3092 : 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 [ + + ]: 3092 : if (config->utf8_mode >= 0) {
611 : 192 : return _PyStatus_OK();
612 : : }
613 : :
614 : : const wchar_t *xopt;
615 : 2900 : xopt = _Py_get_xoption(&cmdline->xoptions, L"utf8");
616 [ + + ]: 2900 : if (xopt) {
617 : 135 : wchar_t *sep = wcschr(xopt, L'=');
618 [ + + ]: 135 : if (sep) {
619 : 95 : xopt = sep + 1;
620 [ + + ]: 95 : if (wcscmp(xopt, L"1") == 0) {
621 : 3 : config->utf8_mode = 1;
622 : : }
623 [ + - ]: 92 : else if (wcscmp(xopt, L"0") == 0) {
624 : 92 : config->utf8_mode = 0;
625 : : }
626 : : else {
627 : 0 : return _PyStatus_ERR("invalid -X utf8 option value");
628 : : }
629 : : }
630 : : else {
631 : 40 : config->utf8_mode = 1;
632 : : }
633 : 135 : return _PyStatus_OK();
634 : : }
635 : :
636 : 2765 : const char *opt = _Py_GetEnv(config->use_environment, "PYTHONUTF8");
637 [ + + ]: 2765 : if (opt) {
638 [ + + ]: 11 : if (strcmp(opt, "1") == 0) {
639 : 9 : config->utf8_mode = 1;
640 : : }
641 [ + + ]: 2 : else if (strcmp(opt, "0") == 0) {
642 : 1 : config->utf8_mode = 0;
643 : : }
644 : : else {
645 : 1 : return _PyStatus_ERR("invalid PYTHONUTF8 environment "
646 : : "variable value");
647 : : }
648 : 10 : return _PyStatus_OK();
649 : : }
650 : :
651 : :
652 : : #ifndef MS_WINDOWS
653 [ + - ]: 2754 : if (config->utf8_mode < 0) {
654 : : /* The C locale and the POSIX locale enable the UTF-8 Mode (PEP 540) */
655 : 2754 : const char *ctype_loc = setlocale(LC_CTYPE, NULL);
656 [ + - ]: 2754 : if (ctype_loc != NULL
657 [ + + ]: 2754 : && (strcmp(ctype_loc, "C") == 0
658 [ - + ]: 2744 : || strcmp(ctype_loc, "POSIX") == 0))
659 : : {
660 : 10 : config->utf8_mode = 1;
661 : : }
662 : : }
663 : : #endif
664 : :
665 [ + + ]: 2754 : if (config->utf8_mode < 0) {
666 : 2744 : config->utf8_mode = 0;
667 : : }
668 : 2754 : return _PyStatus_OK();
669 : : }
670 : :
671 : :
672 : : static void
673 : 3092 : preconfig_init_coerce_c_locale(PyPreConfig *config)
674 : : {
675 [ + + ]: 3092 : if (!config->configure_locale) {
676 : 31 : config->coerce_c_locale = 0;
677 : 31 : config->coerce_c_locale_warn = 0;
678 : 31 : return;
679 : : }
680 : :
681 : 3061 : const char *env = _Py_GetEnv(config->use_environment, "PYTHONCOERCECLOCALE");
682 [ + + ]: 3061 : if (env) {
683 [ + + ]: 146 : if (strcmp(env, "0") == 0) {
684 [ + + ]: 82 : if (config->coerce_c_locale < 0) {
685 : 57 : config->coerce_c_locale = 0;
686 : : }
687 : : }
688 [ + + ]: 64 : else if (strcmp(env, "warn") == 0) {
689 [ + - ]: 21 : if (config->coerce_c_locale_warn < 0) {
690 : 21 : config->coerce_c_locale_warn = 1;
691 : : }
692 : : }
693 : : else {
694 [ + + ]: 43 : if (config->coerce_c_locale < 0) {
695 : 22 : 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 [ + + + + ]: 3061 : if (config->coerce_c_locale < 0 || config->coerce_c_locale == 1) {
704 : : /* The C locale enables the C locale coercion (PEP 538) */
705 [ + + ]: 2842 : if (_Py_LegacyLocaleDetected(0)) {
706 : 44 : config->coerce_c_locale = 2;
707 : : }
708 : : else {
709 : 2798 : config->coerce_c_locale = 0;
710 : : }
711 : : }
712 : :
713 [ + + ]: 3061 : if (config->coerce_c_locale_warn < 0) {
714 : 2981 : config->coerce_c_locale_warn = 0;
715 : : }
716 : : }
717 : :
718 : :
719 : : static PyStatus
720 : 3091 : preconfig_init_allocator(PyPreConfig *config)
721 : : {
722 [ + + ]: 3091 : 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 : 3090 : const char *envvar = _Py_GetEnv(config->use_environment, "PYTHONMALLOC");
728 [ + + ]: 3090 : if (envvar) {
729 : : PyMemAllocatorName name;
730 [ - + ]: 11 : if (_PyMem_GetAllocatorName(envvar, &name) < 0) {
731 : 0 : return _PyStatus_ERR("PYTHONMALLOC: unknown allocator");
732 : : }
733 : 11 : config->allocator = (int)name;
734 : : }
735 : : }
736 : :
737 [ + + + + ]: 3091 : if (config->dev_mode && config->allocator == PYMEM_ALLOCATOR_NOT_SET) {
738 : 33 : config->allocator = PYMEM_ALLOCATOR_DEBUG;
739 : : }
740 : 3091 : return _PyStatus_OK();
741 : : }
742 : :
743 : :
744 : : static PyStatus
745 : 3092 : preconfig_read(PyPreConfig *config, _PyPreCmdline *cmdline)
746 : : {
747 : : PyStatus status;
748 : :
749 : 3092 : status = _PyPreCmdline_Read(cmdline, config);
750 [ - + ]: 3092 : if (_PyStatus_EXCEPTION(status)) {
751 : 0 : return status;
752 : : }
753 : :
754 : 3092 : 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 : 3092 : preconfig_init_coerce_c_locale(config);
764 : :
765 : 3092 : status = preconfig_init_utf8_mode(config, cmdline);
766 [ + + ]: 3092 : if (_PyStatus_EXCEPTION(status)) {
767 : 1 : return status;
768 : : }
769 : :
770 : : /* allocator */
771 : 3091 : status = preconfig_init_allocator(config);
772 [ - + ]: 3091 : if (_PyStatus_EXCEPTION(status)) {
773 : 0 : 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 : 3091 : 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 : 2988 : _PyPreConfig_Read(PyPreConfig *config, const _PyArgv *args)
798 : : {
799 : : PyStatus status;
800 : :
801 : 2988 : status = _PyRuntime_Initialize();
802 [ - + ]: 2988 : if (_PyStatus_EXCEPTION(status)) {
803 : 0 : return status;
804 : : }
805 : :
806 : 2988 : preconfig_get_global_vars(config);
807 : :
808 : : /* Copy LC_CTYPE locale, since it's modified later */
809 : 2988 : const char *loc = setlocale(LC_CTYPE, NULL);
810 [ - + ]: 2988 : if (loc == NULL) {
811 : 0 : return _PyStatus_ERR("failed to LC_CTYPE locale");
812 : : }
813 : 2988 : char *init_ctype_locale = _PyMem_RawStrdup(loc);
814 [ - + ]: 2988 : if (init_ctype_locale == NULL) {
815 : 0 : 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 : 2988 : status = _PyPreConfig_InitFromPreConfig(&save_config, config);
822 [ - + ]: 2988 : if (_PyStatus_EXCEPTION(status)) {
823 : 0 : return status;
824 : : }
825 : :
826 : : /* Set LC_CTYPE to the user preferred locale */
827 [ + + ]: 2988 : if (config->configure_locale) {
828 : 2958 : _Py_SetLocaleFromEnv(LC_CTYPE);
829 : : }
830 : :
831 : : PyPreConfig save_runtime_config;
832 : 2988 : preconfig_copy(&save_runtime_config, &_PyRuntime.preconfig);
833 : :
834 : 2988 : _PyPreCmdline cmdline = _PyPreCmdline_INIT;
835 : 2988 : int locale_coerced = 0;
836 : 2988 : int loops = 0;
837 : :
838 : 104 : while (1) {
839 : 3092 : int utf8_mode = config->utf8_mode;
840 : :
841 : : /* Watchdog to prevent an infinite loop */
842 : 3092 : loops++;
843 [ - + ]: 3092 : if (loops == 3) {
844 : 0 : status = _PyStatus_ERR("Encoding changed twice while "
845 : : "reading the configuration");
846 : 0 : 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 : 3092 : preconfig_copy(&_PyRuntime.preconfig, config);
853 : :
854 [ + + ]: 3092 : if (args) {
855 : : // Set command line arguments at each iteration. If they are bytes
856 : : // strings, they are decoded from the new encoding.
857 : 3020 : status = _PyPreCmdline_SetArgv(&cmdline, args);
858 [ - + ]: 3020 : if (_PyStatus_EXCEPTION(status)) {
859 : 0 : goto done;
860 : : }
861 : : }
862 : :
863 : 3092 : status = preconfig_read(config, &cmdline);
864 [ + + ]: 3092 : if (_PyStatus_EXCEPTION(status)) {
865 : 1 : 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 : 3091 : int encoding_changed = 0;
879 [ + + + + ]: 3091 : if (config->coerce_c_locale && !locale_coerced) {
880 : 44 : locale_coerced = 1;
881 : 44 : _Py_CoerceLegacyLocale(0);
882 : 44 : encoding_changed = 1;
883 : : }
884 : :
885 [ + + ]: 3091 : if (utf8_mode == -1) {
886 [ + + ]: 2899 : if (config->utf8_mode == 1) {
887 : : /* UTF-8 Mode enabled */
888 : 62 : encoding_changed = 1;
889 : : }
890 : : }
891 : : else {
892 [ - + ]: 192 : if (config->utf8_mode != utf8_mode) {
893 : 0 : encoding_changed = 1;
894 : : }
895 : : }
896 : :
897 [ + + ]: 3091 : if (!encoding_changed) {
898 : 2987 : break;
899 : : }
900 : :
901 : : /* Reset the configuration before reading again the configuration,
902 : : just keep UTF-8 Mode and coerce C locale value. */
903 : 104 : int new_utf8_mode = config->utf8_mode;
904 : 104 : int new_coerce_c_locale = config->coerce_c_locale;
905 : 104 : preconfig_copy(config, &save_config);
906 : 104 : config->utf8_mode = new_utf8_mode;
907 : 104 : config->coerce_c_locale = new_coerce_c_locale;
908 : :
909 : : /* The encoding changed: read again the configuration
910 : : with the new encoding */
911 : : }
912 : 2987 : status = _PyStatus_OK();
913 : :
914 : 2988 : done:
915 : : // Revert side effects
916 : 2988 : setlocale(LC_CTYPE, init_ctype_locale);
917 : 2988 : PyMem_RawFree(init_ctype_locale);
918 : 2988 : preconfig_copy(&_PyRuntime.preconfig, &save_runtime_config);
919 : 2988 : _PyPreCmdline_Clear(&cmdline);
920 : 2988 : 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 : 2987 : _PyPreConfig_Write(const PyPreConfig *src_config)
938 : : {
939 : : PyPreConfig config;
940 : :
941 : 2987 : PyStatus status = _PyPreConfig_InitFromPreConfig(&config, src_config);
942 [ - + ]: 2987 : if (_PyStatus_EXCEPTION(status)) {
943 : 0 : return status;
944 : : }
945 : :
946 [ - + ]: 2987 : if (_PyRuntime.core_initialized) {
947 : : /* bpo-34008: Calling this functions after Py_Initialize() ignores
948 : : the new configuration. */
949 : 0 : return _PyStatus_OK();
950 : : }
951 : :
952 : 2987 : PyMemAllocatorName name = (PyMemAllocatorName)config.allocator;
953 [ + + ]: 2987 : if (name != PYMEM_ALLOCATOR_NOT_SET) {
954 [ - + ]: 44 : if (_PyMem_SetupAllocators(name) < 0) {
955 : 0 : return _PyStatus_ERR("Unknown PYTHONMALLOC allocator");
956 : : }
957 : : }
958 : :
959 : 2987 : preconfig_set_global_vars(&config);
960 : :
961 [ + + ]: 2987 : if (config.configure_locale) {
962 [ + + ]: 2957 : if (config.coerce_c_locale) {
963 [ - + ]: 44 : if (!_Py_CoerceLegacyLocale(config.coerce_c_locale_warn)) {
964 : : /* C locale not coerced */
965 : 0 : config.coerce_c_locale = 0;
966 : : }
967 : : }
968 : :
969 : : /* Set LC_CTYPE to the user preferred locale */
970 : 2957 : _Py_SetLocaleFromEnv(LC_CTYPE);
971 : : }
972 : :
973 : : /* Write the new pre-configuration into _PyRuntime */
974 : 2987 : preconfig_copy(&_PyRuntime.preconfig, &config);
975 : :
976 : 2987 : return _PyStatus_OK();
977 : : }
|