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 | } |