diff --git a/Include/internal/pystate.h b/Include/internal/pystate.h index 70e06666258..83fa7dc33cf 100644 --- a/Include/internal/pystate.h +++ b/Include/internal/pystate.h @@ -37,7 +37,7 @@ struct _gilstate_runtime_state { #define _PyGILState_check_enabled _PyRuntime.gilstate.check_enabled -typedef struct { +typedef struct _PyPathConfig { /* Full path to the Python program */ wchar_t *program_full_path; wchar_t *prefix; @@ -59,11 +59,15 @@ typedef struct { PyAPI_DATA(_PyPathConfig) _Py_path_config; -PyAPI_FUNC(_PyInitError) _PyPathConfig_Calculate( +PyAPI_FUNC(_PyInitError) _PyPathConfig_Calculate_impl( _PyPathConfig *config, const _PyCoreConfig *core_config); -PyAPI_FUNC(void) _PyPathConfig_Clear(_PyPathConfig *config); +PyAPI_FUNC(void) _PyPathConfig_ClearGlobal(void); +PyAPI_FUNC(_PyInitError) _Py_wstrlist_append( + int *len, + wchar_t ***list, + const wchar_t *str); /* interpreter state */ diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 95dd55b0517..9644116ba62 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -59,6 +59,9 @@ PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *); PyAPI_FUNC(int) _PyCoreConfig_Copy( _PyCoreConfig *config, const _PyCoreConfig *config2); +PyAPI_FUNC(_PyInitError) _PyCoreConfig_InitPathConfig(_PyCoreConfig *config); +PyAPI_FUNC(_PyInitError) _PyCoreConfig_SetPathConfig( + const _PyCoreConfig *config); PyAPI_FUNC(_PyInitError) _PyMainInterpreterConfig_Read( _PyMainInterpreterConfig *config, @@ -116,7 +119,11 @@ PyAPI_FUNC(wchar_t *) Py_GetPrefix(void); PyAPI_FUNC(wchar_t *) Py_GetExecPrefix(void); PyAPI_FUNC(wchar_t *) Py_GetPath(void); #ifdef Py_BUILD_CORE -PyAPI_FUNC(_PyInitError) _PyPathConfig_Init(const _PyCoreConfig *core_config); +struct _PyPathConfig; +typedef struct _PyPathConfig _PyPathConfig; + +PyAPI_FUNC(_PyInitError) _PyPathConfig_SetGlobal( + const _PyPathConfig *config); PyAPI_FUNC(PyObject*) _PyPathConfig_ComputeArgv0(int argc, wchar_t **argv); PyAPI_FUNC(int) _Py_FindEnvConfigValue( FILE *env_file, diff --git a/Include/pystate.h b/Include/pystate.h index 8158cabc717..ac09dc30927 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -71,6 +71,9 @@ typedef struct { wchar_t *base_prefix; /* sys.base_prefix */ wchar_t *exec_prefix; /* sys.exec_prefix */ wchar_t *base_exec_prefix; /* sys.base_exec_prefix */ +#ifdef MS_WINDOWS + wchar_t *dll_path; /* Windows DLL path */ +#endif /* Private fields */ int _disable_importlib; /* Needed by freeze_importlib */ diff --git a/Modules/getpath.c b/Modules/getpath.c index 755298eb4b7..6d966e1bced 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -946,7 +946,7 @@ calculate_path_impl(const _PyCoreConfig *core_config, _PyInitError -_PyPathConfig_Calculate(_PyPathConfig *config, const _PyCoreConfig *core_config) +_PyPathConfig_Calculate_impl(_PyPathConfig *config, const _PyCoreConfig *core_config) { PyCalculatePath calculate; memset(&calculate, 0, sizeof(calculate)); diff --git a/Modules/main.c b/Modules/main.c index 1ff985517db..5166e742920 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -650,7 +650,7 @@ pymain_free_raw(_PyMain *pymain) configuration options set before Py_Initialize() which should remain valid after Py_Finalize(), since Py_Initialize()-Py_Finalize() can be called multiple times. */ - _PyPathConfig_Clear(&_Py_path_config); + _PyPathConfig_ClearGlobal(); pymain_clear_config(pymain); @@ -701,9 +701,13 @@ error: } -static _PyInitError -wstrlist_append(int *len, wchar_t ***list, const wchar_t *str) +_PyInitError +_Py_wstrlist_append(int *len, wchar_t ***list, const wchar_t *str) { + if (*len == INT_MAX) { + /* len+1 would overflow */ + return _Py_INIT_NO_MEMORY(); + } wchar_t *str2 = _PyMem_RawWcsdup(str); if (str2 == NULL) { return _Py_INIT_NO_MEMORY(); @@ -725,7 +729,7 @@ wstrlist_append(int *len, wchar_t ***list, const wchar_t *str) static int pymain_wstrlist_append(_PyMain *pymain, int *len, wchar_t ***list, const wchar_t *str) { - _PyInitError err = wstrlist_append(len, list, str); + _PyInitError err = _Py_wstrlist_append(len, list, str); if (_Py_INIT_FAILED(err)) { pymain->err = err; return -1; @@ -972,9 +976,9 @@ static _PyInitError config_add_warnings_optlist(_PyCoreConfig *config, int len, wchar_t **options) { for (int i = 0; i < len; i++) { - _PyInitError err = wstrlist_append(&config->nwarnoption, - &config->warnoptions, - options[i]); + _PyInitError err = _Py_wstrlist_append(&config->nwarnoption, + &config->warnoptions, + options[i]); if (_Py_INIT_FAILED(err)) { return err; } @@ -1006,9 +1010,9 @@ config_init_warnoptions(_PyCoreConfig *config, _Py_CommandLineDetails *cmdline) */ if (config->dev_mode) { - err = wstrlist_append(&config->nwarnoption, - &config->warnoptions, - L"default"); + err = _Py_wstrlist_append(&config->nwarnoption, + &config->warnoptions, + L"default"); if (_Py_INIT_FAILED(err)) { return err; } @@ -1040,9 +1044,9 @@ config_init_warnoptions(_PyCoreConfig *config, _Py_CommandLineDetails *cmdline) else { filter = L"default::BytesWarning"; } - err = wstrlist_append(&config->nwarnoption, - &config->warnoptions, - filter); + err = _Py_wstrlist_append(&config->nwarnoption, + &config->warnoptions, + filter); if (_Py_INIT_FAILED(err)) { return err; } @@ -1077,9 +1081,9 @@ cmdline_init_env_warnoptions(_Py_CommandLineDetails *cmdline) warning != NULL; warning = WCSTOK(NULL, L",", &context)) { - _PyInitError err = wstrlist_append(&cmdline->nenv_warnoption, - &cmdline->env_warnoptions, - warning); + _PyInitError err = _Py_wstrlist_append(&cmdline->nenv_warnoption, + &cmdline->env_warnoptions, + warning); if (_Py_INIT_FAILED(err)) { PyMem_RawFree(env); return err; @@ -2099,101 +2103,6 @@ config_init_locale(_PyCoreConfig *config) } -static _PyInitError -config_init_module_search_paths(_PyCoreConfig *config) -{ - assert(config->module_search_paths == NULL); - assert(config->nmodule_search_path < 0); - - config->nmodule_search_path = 0; - - const wchar_t *sys_path = Py_GetPath(); - const wchar_t delim = DELIM; - const wchar_t *p = sys_path; - while (1) { - p = wcschr(sys_path, delim); - if (p == NULL) { - p = sys_path + wcslen(sys_path); /* End of string */ - } - - size_t path_len = (p - sys_path); - wchar_t *path = PyMem_RawMalloc((path_len + 1) * sizeof(wchar_t)); - if (path == NULL) { - return _Py_INIT_NO_MEMORY(); - } - memcpy(path, sys_path, path_len * sizeof(wchar_t)); - path[path_len] = L'\0'; - - _PyInitError err = wstrlist_append(&config->nmodule_search_path, - &config->module_search_paths, - path); - PyMem_RawFree(path); - if (_Py_INIT_FAILED(err)) { - return err; - } - - if (*p == '\0') { - break; - } - sys_path = p + 1; - } - return _Py_INIT_OK(); -} - - -static _PyInitError -config_init_path_config(_PyCoreConfig *config) -{ - _PyInitError err = _PyPathConfig_Init(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - - if (config->nmodule_search_path < 0) { - err = config_init_module_search_paths(config); - if (_Py_INIT_FAILED(err)) { - return err; - } - } - - if (config->executable == NULL) { - config->executable = _PyMem_RawWcsdup(Py_GetProgramFullPath()); - if (config->executable == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } - - if (config->prefix == NULL) { - config->prefix = _PyMem_RawWcsdup(Py_GetPrefix()); - if (config->prefix == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } - - if (config->exec_prefix == NULL) { - config->exec_prefix = _PyMem_RawWcsdup(Py_GetExecPrefix()); - if (config->exec_prefix == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } - - if (config->base_prefix == NULL) { - config->base_prefix = _PyMem_RawWcsdup(config->prefix); - if (config->base_prefix == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } - - if (config->base_exec_prefix == NULL) { - config->base_exec_prefix = _PyMem_RawWcsdup(config->exec_prefix); - if (config->base_exec_prefix == NULL) { - return _Py_INIT_NO_MEMORY(); - } - } - - return _Py_INIT_OK(); -} - /* Read configuration settings from standard locations * * This function doesn't make any changes to the interpreter state - it @@ -2252,7 +2161,7 @@ _PyCoreConfig_Read(_PyCoreConfig *config) } if (!config->_disable_importlib) { - err = config_init_path_config(config); + err = _PyCoreConfig_InitPathConfig(config); if (_Py_INIT_FAILED(err)) { return err; } @@ -2294,6 +2203,9 @@ _PyCoreConfig_Clear(_PyCoreConfig *config) CLEAR(config->prefix); CLEAR(config->base_prefix); CLEAR(config->exec_prefix); +#ifdef MS_WINDOWS + CLEAR(config->dll_path); +#endif CLEAR(config->base_exec_prefix); #undef CLEAR #undef CLEAR_WSTRLIST @@ -2356,6 +2268,9 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2) COPY_STR_ATTR(prefix); COPY_STR_ATTR(base_prefix); COPY_STR_ATTR(exec_prefix); +#ifdef MS_WINDOWS + COPY_STR_ATTR(dll_path); +#endif COPY_STR_ATTR(base_exec_prefix); #undef COPY_ATTR diff --git a/PC/getpathp.c b/PC/getpathp.c index 93828432ae3..fa687b7056b 100644 --- a/PC/getpathp.c +++ b/PC/getpathp.c @@ -1000,7 +1000,7 @@ calculate_free(PyCalculatePath *calculate) _PyInitError -_PyPathConfig_Calculate(_PyPathConfig *config, const _PyCoreConfig *core_config) +_PyPathConfig_Calculate_impl(_PyPathConfig *config, const _PyCoreConfig *core_config) { PyCalculatePath calculate; memset(&calculate, 0, sizeof(calculate)); diff --git a/Python/pathconfig.c b/Python/pathconfig.c index 07aa01c0304..e987df1b1fd 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -13,7 +13,23 @@ extern "C" { _PyPathConfig _Py_path_config = _PyPathConfig_INIT; -void +static int +copy_wstr(wchar_t **dst, const wchar_t *src) +{ + if (src != NULL) { + *dst = _PyMem_RawWcsdup(src); + if (*dst == NULL) { + return -1; + } + } + else { + *dst = NULL; + } + return 0; +} + + +static void _PyPathConfig_Clear(_PyPathConfig *config) { /* _PyMem_SetDefaultAllocator() is needed to get a known memory allocator, @@ -44,16 +60,11 @@ _PyPathConfig_Clear(_PyPathConfig *config) } -/* Initialize paths for Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix() - and Py_GetProgramFullPath() */ -_PyInitError -_PyPathConfig_Init(const _PyCoreConfig *core_config) +/* Calculate the path configuration: initialize path_config from core_config */ +static _PyInitError +_PyPathConfig_Calculate(_PyPathConfig *path_config, + const _PyCoreConfig *core_config) { - if (_Py_path_config.module_search_path) { - /* Already initialized */ - return _Py_INIT_OK(); - } - _PyInitError err; _PyPathConfig new_config = _PyPathConfig_INIT; @@ -62,31 +73,67 @@ _PyPathConfig_Init(const _PyCoreConfig *core_config) /* Calculate program_full_path, prefix, exec_prefix (Unix) or dll_path (Windows), and module_search_path */ - err = _PyPathConfig_Calculate(&new_config, core_config); + err = _PyPathConfig_Calculate_impl(&new_config, core_config); if (_Py_INIT_FAILED(err)) { - _PyPathConfig_Clear(&new_config); - goto done; + goto err; } /* Copy home and program_name from core_config */ - if (core_config->home != NULL) { - new_config.home = _PyMem_RawWcsdup(core_config->home); - if (new_config.home == NULL) { - err = _Py_INIT_NO_MEMORY(); - goto done; - } + if (copy_wstr(&new_config.home, core_config->home) < 0) { + err = _Py_INIT_NO_MEMORY(); + goto err; } - else { - new_config.home = NULL; + if (copy_wstr(&new_config.program_name, core_config->program_name) < 0) { + err = _Py_INIT_NO_MEMORY(); + goto err; } - new_config.program_name = _PyMem_RawWcsdup(core_config->program_name); - if (new_config.program_name == NULL) { - err = _Py_INIT_NO_MEMORY(); - goto done; - } + _PyPathConfig_Clear(path_config); + *path_config = new_config; + + err = _Py_INIT_OK(); + goto done; + +err: + _PyPathConfig_Clear(&new_config); + +done: + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return err; +} + + +_PyInitError +_PyPathConfig_SetGlobal(const _PyPathConfig *config) +{ + _PyInitError err; + _PyPathConfig new_config = _PyPathConfig_INIT; + + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + +#define COPY_ATTR(ATTR) \ + do { \ + if (copy_wstr(&new_config.ATTR, config->ATTR) < 0) { \ + _PyPathConfig_Clear(&new_config); \ + err = _Py_INIT_NO_MEMORY(); \ + goto done; \ + } \ + } while (0) + + COPY_ATTR(program_full_path); + COPY_ATTR(prefix); +#ifdef MS_WINDOWS + COPY_ATTR(dll_path); +#else + COPY_ATTR(exec_prefix); +#endif + COPY_ATTR(module_search_path); + COPY_ATTR(program_name); + COPY_ATTR(home); _PyPathConfig_Clear(&_Py_path_config); + /* Steal new_config strings; don't clear new_config */ _Py_path_config = new_config; err = _Py_INIT_OK(); @@ -97,15 +144,228 @@ done: } +void +_PyPathConfig_ClearGlobal(void) +{ + _PyPathConfig_Clear(&_Py_path_config); +} + + +static wchar_t* +wstrlist_join(wchar_t sep, int count, wchar_t **list) +{ + size_t len = 1; /* NUL terminator */ + for (int i=0; i < count; i++) { + if (i != 0) { + len++; + } + len += wcslen(list[i]); + } + + wchar_t *text = PyMem_RawMalloc(len * sizeof(wchar_t)); + if (text == NULL) { + return NULL; + } + wchar_t *str = text; + for (int i=0; i < count; i++) { + wchar_t *path = list[i]; + if (i != 0) { + *str++ = SEP; + } + len = wcslen(path); + memcpy(str, path, len * sizeof(wchar_t)); + str += len; + } + *str = L'\0'; + + return text; +} + + +/* Set the global path configuration from core_config. */ +_PyInitError +_PyCoreConfig_SetPathConfig(const _PyCoreConfig *core_config) +{ + PyMemAllocatorEx old_alloc; + _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + _PyInitError err; + _PyPathConfig path_config = _PyPathConfig_INIT; + + path_config.module_search_path = wstrlist_join(DELIM, + core_config->nmodule_search_path, + core_config->module_search_paths); + if (path_config.module_search_path == NULL) { + goto no_memory; + } + + if (copy_wstr(&path_config.program_full_path, core_config->executable) < 0) { + goto no_memory; + } + if (copy_wstr(&path_config.prefix, core_config->prefix) < 0) { + goto no_memory; + } +#ifdef MS_WINDOWS + if (copy_wstr(&path_config.dll_path, core_config->dll_path) < 0) { + goto no_memory; + } +#else + if (copy_wstr(&path_config.exec_prefix, core_config->exec_prefix) < 0) { + goto no_memory; + } +#endif + if (copy_wstr(&path_config.program_name, core_config->program_name) < 0) { + goto no_memory; + } + if (copy_wstr(&path_config.home, core_config->home) < 0) { + goto no_memory; + } + + err = _PyPathConfig_SetGlobal(&path_config); + if (_Py_INIT_FAILED(err)) { + goto done; + } + + err = _Py_INIT_OK(); + goto done; + +no_memory: + err = _Py_INIT_NO_MEMORY(); + +done: + _PyPathConfig_Clear(&path_config); + PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + return err; +} + + +static _PyInitError +core_config_init_module_search_paths(_PyCoreConfig *config, + _PyPathConfig *path_config) +{ + assert(config->module_search_paths == NULL); + assert(config->nmodule_search_path < 0); + + config->nmodule_search_path = 0; + + const wchar_t *sys_path = path_config->module_search_path; + const wchar_t delim = DELIM; + const wchar_t *p = sys_path; + while (1) { + p = wcschr(sys_path, delim); + if (p == NULL) { + p = sys_path + wcslen(sys_path); /* End of string */ + } + + size_t path_len = (p - sys_path); + wchar_t *path = PyMem_RawMalloc((path_len + 1) * sizeof(wchar_t)); + if (path == NULL) { + return _Py_INIT_NO_MEMORY(); + } + memcpy(path, sys_path, path_len * sizeof(wchar_t)); + path[path_len] = L'\0'; + + _PyInitError err = _Py_wstrlist_append(&config->nmodule_search_path, + &config->module_search_paths, + path); + PyMem_RawFree(path); + if (_Py_INIT_FAILED(err)) { + return err; + } + + if (*p == '\0') { + break; + } + sys_path = p + 1; + } + return _Py_INIT_OK(); +} + + +_PyInitError +_PyCoreConfig_InitPathConfig(_PyCoreConfig *config) +{ + _PyPathConfig path_config = _PyPathConfig_INIT; + _PyInitError err; + + err = _PyPathConfig_Calculate(&path_config, config); + if (_Py_INIT_FAILED(err)) { + goto error; + } + + if (config->nmodule_search_path < 0) { + err = core_config_init_module_search_paths(config, &path_config); + if (_Py_INIT_FAILED(err)) { + goto error; + } + } + + if (config->executable == NULL) { + if (copy_wstr(&config->executable, + path_config.program_full_path) < 0) { + goto no_memory; + } + } + + if (config->prefix == NULL) { + if (copy_wstr(&config->prefix, path_config.prefix) < 0) { + goto no_memory; + } + } + + if (config->exec_prefix == NULL) { +#ifdef MS_WINDOWS + wchar_t *exec_prefix = path_config.prefix; +#else + wchar_t *exec_prefix = path_config.exec_prefix; +#endif + if (copy_wstr(&config->exec_prefix, exec_prefix) < 0) { + goto no_memory; + } + } + +#ifdef MS_WINDOWS + if (config->dll_path == NULL) { + if (copy_wstr(&config->dll_path, path_config.dll_path) < 0) { + goto no_memory; + } + } +#endif + + if (config->base_prefix == NULL) { + if (copy_wstr(&config->base_prefix, config->prefix) < 0) { + goto no_memory; + } + } + + if (config->base_exec_prefix == NULL) { + if (copy_wstr(&config->base_exec_prefix, config->exec_prefix) < 0) { + goto no_memory; + } + } + + _PyPathConfig_Clear(&path_config); + return _Py_INIT_OK(); + +no_memory: + err = _Py_INIT_NO_MEMORY(); + +error: + _PyPathConfig_Clear(&path_config); + return err; +} + + static void pathconfig_global_init(void) { - if (_Py_path_config.module_search_path) { + if (_Py_path_config.module_search_path != NULL) { /* Already initialized */ return; } _PyInitError err; + _PyPathConfig path_config = _PyPathConfig_INIT; _PyCoreConfig config = _PyCoreConfig_INIT; err = _PyCoreConfig_Read(&config); @@ -113,15 +373,22 @@ pathconfig_global_init(void) goto error; } - err = _PyPathConfig_Init(&config); + err = _PyPathConfig_Calculate(&path_config, &config); if (_Py_INIT_FAILED(err)) { goto error; } + err = _PyPathConfig_SetGlobal(&path_config); + if (_Py_INIT_FAILED(err)) { + goto error; + } + + _PyPathConfig_Clear(&path_config); _PyCoreConfig_Clear(&config); return; error: + _PyPathConfig_Clear(&path_config); _PyCoreConfig_Clear(&config); _Py_FatalInitError(err); } @@ -142,13 +409,18 @@ Py_SetPath(const wchar_t *path) _PyPathConfig new_config; new_config.program_full_path = _PyMem_RawWcsdup(Py_GetProgramName()); + int alloc_error = (new_config.program_full_path == NULL); new_config.prefix = _PyMem_RawWcsdup(L""); + alloc_error |= (new_config.prefix == NULL); #ifdef MS_WINDOWS new_config.dll_path = _PyMem_RawWcsdup(L""); + alloc_error |= (new_config.dll_path == NULL); #else new_config.exec_prefix = _PyMem_RawWcsdup(L""); + alloc_error |= (new_config.exec_prefix == NULL); #endif new_config.module_search_path = _PyMem_RawWcsdup(path); + alloc_error |= (new_config.module_search_path == NULL); /* steal the home and program_name values (to leave them unchanged) */ new_config.home = _Py_path_config.home; @@ -160,6 +432,10 @@ Py_SetPath(const wchar_t *path) _Py_path_config = new_config; PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); + + if (alloc_error) { + Py_FatalError("Py_SetPath() failed: out of memory"); + } } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 64fa1e58f7d..325f42305f3 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -759,6 +759,13 @@ _Py_InitializeCore(const _PyCoreConfig *core_config) if (!_PyContext_Init()) return _Py_INIT_ERR("can't init context"); + if (!core_config->_disable_importlib) { + err = _PyCoreConfig_SetPathConfig(core_config); + if (_Py_INIT_FAILED(err)) { + return err; + } + } + /* This call sets up builtin and frozen import support */ if (!interp->core_config._disable_importlib) { err = initimport(interp, sysmod);