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