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