diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-11-23 16:03:20 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-23 16:03:20 (GMT) |
commit | 0327bde9da203bb256b58218d012ca76ad0db4e4 (patch) | |
tree | a54c0feb235156b9c01db641ebbf18f0a695ad56 /Modules | |
parent | bdb8315c21825487b54852ff0511fb4881ea2181 (diff) | |
download | cpython-0327bde9da203bb256b58218d012ca76ad0db4e4.zip cpython-0327bde9da203bb256b58218d012ca76ad0db4e4.tar.gz cpython-0327bde9da203bb256b58218d012ca76ad0db4e4.tar.bz2 |
bpo-32030: Rewrite calculate_path() (#4521)
* calculate_path() rewritten in Modules/getpath.c and PC/getpathp.c
* Move global variables into a new PyPathConfig structure.
* calculate_path():
* Split the huge calculate_path() function into subfunctions.
* Add PyCalculatePath structure to pass data between subfunctions.
* Document PyCalculatePath fields.
* Move cleanup code into a new calculate_free() subfunction
* calculate_init() now handles Py_DecodeLocale() failures properly
* calculate_path() is now atomic: only replace PyPathConfig
(path_config) at once on success.
* _Py_GetPythonHomeWithConfig() now returns an error on failure
* Add _Py_INIT_NO_MEMORY() helper: report a memory allocation failure
* Coding style fixes (PEP 7)
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/getpath.c | 855 | ||||
-rw-r--r-- | Modules/main.c | 56 |
2 files changed, 542 insertions, 369 deletions
diff --git a/Modules/getpath.c b/Modules/getpath.c index 62f5e69..d8125ae 100644 --- a/Modules/getpath.c +++ b/Modules/getpath.c @@ -7,7 +7,7 @@ #include <string.h> #ifdef __APPLE__ -#include <mach-o/dyld.h> +# include <mach-o/dyld.h> #endif /* Search in some common locations for the associated Python libraries. @@ -97,7 +97,7 @@ */ #ifdef __cplusplus - extern "C" { +extern "C" { #endif @@ -109,13 +109,38 @@ #define LANDMARK L"os.py" #endif -static wchar_t prefix[MAXPATHLEN+1]; -static wchar_t exec_prefix[MAXPATHLEN+1]; -static wchar_t progpath[MAXPATHLEN+1]; -static wchar_t *module_search_path = NULL; +typedef struct { + wchar_t prefix[MAXPATHLEN+1]; + wchar_t exec_prefix[MAXPATHLEN+1]; + wchar_t progpath[MAXPATHLEN+1]; + wchar_t *module_search_path; +} PyPathConfig; + +typedef struct { + wchar_t *path_env; /* PATH environment variable */ + wchar_t *home; /* PYTHONHOME environment variable */ + wchar_t *module_search_path_env; /* PYTHONPATH environment variable */ + wchar_t *module_search_path_buffer; + + wchar_t *prog; /* Program name */ + wchar_t *pythonpath; /* PYTHONPATH define */ + wchar_t *prefix; /* PREFIX define */ + wchar_t *exec_prefix; /* EXEC_PREFIX define */ + + wchar_t *lib_python; /* "lib/pythonX.Y" */ + wchar_t argv0_path[MAXPATHLEN+1]; + wchar_t zip_path[MAXPATHLEN+1]; /* ".../lib/pythonXY.zip" */ + + int prefix_found; /* found platform independent libraries? */ + int exec_prefix_found; /* found the platform dependent libraries? */ +} PyCalculatePath; + +static const wchar_t delimiter[2] = {DELIM, '\0'}; +static const wchar_t separator[2] = {SEP, '\0'}; +static PyPathConfig path_config = {.module_search_path = NULL}; -/* Get file status. Encode the path to the locale encoding. */ +/* Get file status. Encode the path to the locale encoding. */ static int _Py_wstat(const wchar_t* path, struct stat *buf) { @@ -131,6 +156,7 @@ _Py_wstat(const wchar_t* path, struct stat *buf) return err; } + static void reduce(wchar_t *dir) { @@ -140,14 +166,17 @@ reduce(wchar_t *dir) dir[i] = '\0'; } + static int isfile(wchar_t *filename) /* Is file, not directory */ { struct stat buf; - if (_Py_wstat(filename, &buf) != 0) + if (_Py_wstat(filename, &buf) != 0) { return 0; - if (!S_ISREG(buf.st_mode)) + } + if (!S_ISREG(buf.st_mode)) { return 0; + } return 1; } @@ -155,41 +184,50 @@ isfile(wchar_t *filename) /* Is file, not directory */ static int ismodule(wchar_t *filename) /* Is module -- check for .pyc too */ { - if (isfile(filename)) + if (isfile(filename)) { return 1; + } /* Check for the compiled version of prefix. */ if (wcslen(filename) < MAXPATHLEN) { wcscat(filename, L"c"); - if (isfile(filename)) + if (isfile(filename)) { return 1; + } } return 0; } +/* Is executable file */ static int -isxfile(wchar_t *filename) /* Is executable file */ +isxfile(wchar_t *filename) { struct stat buf; - if (_Py_wstat(filename, &buf) != 0) + if (_Py_wstat(filename, &buf) != 0) { return 0; - if (!S_ISREG(buf.st_mode)) + } + if (!S_ISREG(buf.st_mode)) { return 0; - if ((buf.st_mode & 0111) == 0) + } + if ((buf.st_mode & 0111) == 0) { return 0; + } return 1; } +/* Is directory */ static int -isdir(wchar_t *filename) /* Is directory */ +isdir(wchar_t *filename) { struct stat buf; - if (_Py_wstat(filename, &buf) != 0) + if (_Py_wstat(filename, &buf) != 0) { return 0; - if (!S_ISDIR(buf.st_mode)) + } + if (!S_ISDIR(buf.st_mode)) { return 0; + } return 1; } @@ -207,58 +245,67 @@ static void joinpath(wchar_t *buffer, wchar_t *stuff) { size_t n, k; - if (stuff[0] == SEP) + if (stuff[0] == SEP) { n = 0; + } else { n = wcslen(buffer); - if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) + if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) { buffer[n++] = SEP; + } } - if (n > MAXPATHLEN) + if (n > MAXPATHLEN) { Py_FatalError("buffer overflow in getpath.c's joinpath()"); + } k = wcslen(stuff); - if (n + k > MAXPATHLEN) + if (n + k > MAXPATHLEN) { k = MAXPATHLEN - n; + } wcsncpy(buffer+n, stuff, k); buffer[n+k] = '\0'; } + /* copy_absolute requires that path be allocated at least MAXPATHLEN + 1 bytes and that p be no more than MAXPATHLEN bytes. */ static void copy_absolute(wchar_t *path, wchar_t *p, size_t pathlen) { - if (p[0] == SEP) + if (p[0] == SEP) { wcscpy(path, p); + } else { if (!_Py_wgetcwd(path, pathlen)) { /* unable to get the current directory */ wcscpy(path, p); return; } - if (p[0] == '.' && p[1] == SEP) + if (p[0] == '.' && p[1] == SEP) { p += 2; + } joinpath(path, p); } } + /* absolutize() requires that path be allocated at least MAXPATHLEN+1 bytes. */ static void absolutize(wchar_t *path) { wchar_t buffer[MAXPATHLEN+1]; - if (path[0] == SEP) + if (path[0] == SEP) { return; + } copy_absolute(buffer, path, MAXPATHLEN+1); wcscpy(path, buffer); } + /* search for a prefix value in an environment file. If found, copy it to the provided buffer, which is expected to be no more than MAXPATHLEN bytes long. */ - static int find_env_config_value(FILE * env_file, const wchar_t * key, wchar_t * value) { @@ -272,15 +319,18 @@ find_env_config_value(FILE * env_file, const wchar_t * key, wchar_t * value) PyObject * decoded; int n; - if (p == NULL) + if (p == NULL) { break; + } n = strlen(p); if (p[n - 1] != '\n') { /* line has overflowed - bail */ break; } - if (p[0] == '#') /* Comment - skip */ + if (p[0] == '#') { + /* Comment - skip */ continue; + } decoded = PyUnicode_DecodeUTF8(buffer, n, "surrogateescape"); if (decoded != NULL) { Py_ssize_t k; @@ -307,106 +357,151 @@ find_env_config_value(FILE * env_file, const wchar_t * key, wchar_t * value) return result; } + /* search_for_prefix requires that argv0_path be no more than MAXPATHLEN bytes long. */ static int -search_for_prefix(wchar_t *argv0_path, wchar_t *home, wchar_t *_prefix, - wchar_t *lib_python) +search_for_prefix(PyCalculatePath *calculate, PyPathConfig *config) { size_t n; wchar_t *vpath; /* If PYTHONHOME is set, we believe it unconditionally */ - if (home) { - wchar_t *delim; - wcsncpy(prefix, home, MAXPATHLEN); - prefix[MAXPATHLEN] = L'\0'; - delim = wcschr(prefix, DELIM); - if (delim) + if (calculate->home) { + wcsncpy(config->prefix, calculate->home, MAXPATHLEN); + config->prefix[MAXPATHLEN] = L'\0'; + wchar_t *delim = wcschr(config->prefix, DELIM); + if (delim) { *delim = L'\0'; - joinpath(prefix, lib_python); - joinpath(prefix, LANDMARK); + } + joinpath(config->prefix, calculate->lib_python); + joinpath(config->prefix, LANDMARK); return 1; } /* Check to see if argv[0] is in the build directory */ - wcsncpy(prefix, argv0_path, MAXPATHLEN); - prefix[MAXPATHLEN] = L'\0'; - joinpath(prefix, L"Modules/Setup"); - if (isfile(prefix)) { + wcsncpy(config->prefix, calculate->argv0_path, MAXPATHLEN); + config->prefix[MAXPATHLEN] = L'\0'; + joinpath(config->prefix, L"Modules/Setup"); + if (isfile(config->prefix)) { /* Check VPATH to see if argv0_path is in the build directory. */ vpath = Py_DecodeLocale(VPATH, NULL); if (vpath != NULL) { - wcsncpy(prefix, argv0_path, MAXPATHLEN); - prefix[MAXPATHLEN] = L'\0'; - joinpath(prefix, vpath); + wcsncpy(config->prefix, calculate->argv0_path, MAXPATHLEN); + config->prefix[MAXPATHLEN] = L'\0'; + joinpath(config->prefix, vpath); PyMem_RawFree(vpath); - joinpath(prefix, L"Lib"); - joinpath(prefix, LANDMARK); - if (ismodule(prefix)) + joinpath(config->prefix, L"Lib"); + joinpath(config->prefix, LANDMARK); + if (ismodule(config->prefix)) { return -1; + } } } /* Search from argv0_path, until root is found */ - copy_absolute(prefix, argv0_path, MAXPATHLEN+1); + copy_absolute(config->prefix, calculate->argv0_path, MAXPATHLEN+1); do { - n = wcslen(prefix); - joinpath(prefix, lib_python); - joinpath(prefix, LANDMARK); - if (ismodule(prefix)) + n = wcslen(config->prefix); + joinpath(config->prefix, calculate->lib_python); + joinpath(config->prefix, LANDMARK); + if (ismodule(config->prefix)) { return 1; - prefix[n] = L'\0'; - reduce(prefix); - } while (prefix[0]); + } + config->prefix[n] = L'\0'; + reduce(config->prefix); + } while (config->prefix[0]); /* Look at configure's PREFIX */ - wcsncpy(prefix, _prefix, MAXPATHLEN); - prefix[MAXPATHLEN] = L'\0'; - joinpath(prefix, lib_python); - joinpath(prefix, LANDMARK); - if (ismodule(prefix)) + wcsncpy(config->prefix, calculate->prefix, MAXPATHLEN); + config->prefix[MAXPATHLEN] = L'\0'; + joinpath(config->prefix, calculate->lib_python); + joinpath(config->prefix, LANDMARK); + if (ismodule(config->prefix)) { return 1; + } /* Fail */ return 0; } +static void +calculate_prefix(PyCalculatePath *calculate, PyPathConfig *config) +{ + calculate->prefix_found = search_for_prefix(calculate, config); + if (!calculate->prefix_found) { + if (!Py_FrozenFlag) { + fprintf(stderr, + "Could not find platform independent libraries <prefix>\n"); + } + wcsncpy(config->prefix, calculate->prefix, MAXPATHLEN); + joinpath(config->prefix, calculate->lib_python); + } + else { + reduce(config->prefix); + } +} + + +static void +calculate_reduce_prefix(PyCalculatePath *calculate, PyPathConfig *config) +{ + /* Reduce prefix and exec_prefix to their essence, + * e.g. /usr/local/lib/python1.5 is reduced to /usr/local. + * If we're loading relative to the build directory, + * return the compiled-in defaults instead. + */ + if (calculate->prefix_found > 0) { + reduce(config->prefix); + reduce(config->prefix); + /* The prefix is the root directory, but reduce() chopped + * off the "/". */ + if (!config->prefix[0]) { + wcscpy(config->prefix, separator); + } + } + else { + wcsncpy(config->prefix, calculate->prefix, MAXPATHLEN); + } +} + + /* search_for_exec_prefix requires that argv0_path be no more than MAXPATHLEN bytes long. */ static int -search_for_exec_prefix(wchar_t *argv0_path, wchar_t *home, - wchar_t *_exec_prefix, wchar_t *lib_python) +search_for_exec_prefix(PyCalculatePath *calculate, PyPathConfig *config) { size_t n; /* If PYTHONHOME is set, we believe it unconditionally */ - if (home) { - wchar_t *delim; - delim = wcschr(home, DELIM); - if (delim) - wcsncpy(exec_prefix, delim+1, MAXPATHLEN); - else - wcsncpy(exec_prefix, home, MAXPATHLEN); - exec_prefix[MAXPATHLEN] = L'\0'; - joinpath(exec_prefix, lib_python); - joinpath(exec_prefix, L"lib-dynload"); + if (calculate->home) { + wchar_t *delim = wcschr(calculate->home, DELIM); + if (delim) { + wcsncpy(config->exec_prefix, delim+1, MAXPATHLEN); + } + else { + wcsncpy(config->exec_prefix, calculate->home, MAXPATHLEN); + } + config->exec_prefix[MAXPATHLEN] = L'\0'; + joinpath(config->exec_prefix, calculate->lib_python); + joinpath(config->exec_prefix, L"lib-dynload"); return 1; } /* Check to see if argv[0] is in the build directory. "pybuilddir.txt" is written by setup.py and contains the relative path to the location of shared library modules. */ - wcsncpy(exec_prefix, argv0_path, MAXPATHLEN); - exec_prefix[MAXPATHLEN] = L'\0'; - joinpath(exec_prefix, L"pybuilddir.txt"); - if (isfile(exec_prefix)) { - FILE *f = _Py_wfopen(exec_prefix, L"rb"); - if (f == NULL) + wcsncpy(config->exec_prefix, calculate->argv0_path, MAXPATHLEN); + config->exec_prefix[MAXPATHLEN] = L'\0'; + joinpath(config->exec_prefix, L"pybuilddir.txt"); + if (isfile(config->exec_prefix)) { + FILE *f = _Py_wfopen(config->exec_prefix, L"rb"); + if (f == NULL) { errno = 0; + } else { char buf[MAXPATHLEN+1]; PyObject *decoded; @@ -422,9 +517,9 @@ search_for_exec_prefix(wchar_t *argv0_path, wchar_t *home, Py_DECREF(decoded); if (k >= 0) { rel_builddir_path[k] = L'\0'; - wcsncpy(exec_prefix, argv0_path, MAXPATHLEN); - exec_prefix[MAXPATHLEN] = L'\0'; - joinpath(exec_prefix, rel_builddir_path); + wcsncpy(config->exec_prefix, calculate->argv0_path, MAXPATHLEN); + config->exec_prefix[MAXPATHLEN] = L'\0'; + joinpath(config->exec_prefix, rel_builddir_path); return -1; } } @@ -432,87 +527,85 @@ search_for_exec_prefix(wchar_t *argv0_path, wchar_t *home, } /* Search from argv0_path, until root is found */ - copy_absolute(exec_prefix, argv0_path, MAXPATHLEN+1); + copy_absolute(config->exec_prefix, calculate->argv0_path, MAXPATHLEN+1); do { - n = wcslen(exec_prefix); - joinpath(exec_prefix, lib_python); - joinpath(exec_prefix, L"lib-dynload"); - if (isdir(exec_prefix)) + n = wcslen(config->exec_prefix); + joinpath(config->exec_prefix, calculate->lib_python); + joinpath(config->exec_prefix, L"lib-dynload"); + if (isdir(config->exec_prefix)) { return 1; - exec_prefix[n] = L'\0'; - reduce(exec_prefix); - } while (exec_prefix[0]); + } + config->exec_prefix[n] = L'\0'; + reduce(config->exec_prefix); + } while (config->exec_prefix[0]); /* Look at configure's EXEC_PREFIX */ - wcsncpy(exec_prefix, _exec_prefix, MAXPATHLEN); - exec_prefix[MAXPATHLEN] = L'\0'; - joinpath(exec_prefix, lib_python); - joinpath(exec_prefix, L"lib-dynload"); - if (isdir(exec_prefix)) + wcsncpy(config->exec_prefix, calculate->exec_prefix, MAXPATHLEN); + config->exec_prefix[MAXPATHLEN] = L'\0'; + joinpath(config->exec_prefix, calculate->lib_python); + joinpath(config->exec_prefix, L"lib-dynload"); + if (isdir(config->exec_prefix)) { return 1; + } /* Fail */ return 0; } + static void -calculate_path(const _PyMainInterpreterConfig *config) +calculate_exec_prefix(PyCalculatePath *calculate, PyPathConfig *config) { - extern wchar_t *Py_GetProgramName(void); - - static const wchar_t delimiter[2] = {DELIM, '\0'}; - static const wchar_t separator[2] = {SEP, '\0'}; - wchar_t *home = _Py_GetPythonHomeWithConfig(config); - char *_path = getenv("PATH"); - wchar_t *path_buffer = NULL; - wchar_t *path = NULL; - wchar_t *prog = Py_GetProgramName(); - wchar_t argv0_path[MAXPATHLEN+1]; - wchar_t zip_path[MAXPATHLEN+1]; - int pfound, efound; /* 1 if found; -1 if found build directory */ - wchar_t *buf; - size_t bufsz; - size_t prefixsz; - wchar_t *defpath; -#ifdef WITH_NEXT_FRAMEWORK - NSModule pythonModule; - const char* modPath; -#endif -#ifdef __APPLE__ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 - uint32_t nsexeclength = MAXPATHLEN; -#else - unsigned long nsexeclength = MAXPATHLEN; -#endif - char execpath[MAXPATHLEN+1]; -#endif - wchar_t *_pythonpath, *_prefix, *_exec_prefix; - wchar_t *lib_python; + calculate->exec_prefix_found = search_for_exec_prefix(calculate, config); + if (!calculate->exec_prefix_found) { + if (!Py_FrozenFlag) { + fprintf(stderr, + "Could not find platform dependent libraries <exec_prefix>\n"); + } + wcsncpy(config->exec_prefix, calculate->exec_prefix, MAXPATHLEN); + joinpath(config->exec_prefix, L"lib/lib-dynload"); + } + /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */ +} - _pythonpath = Py_DecodeLocale(PYTHONPATH, NULL); - _prefix = Py_DecodeLocale(PREFIX, NULL); - _exec_prefix = Py_DecodeLocale(EXEC_PREFIX, NULL); - lib_python = Py_DecodeLocale("lib/python" VERSION, NULL); - if (!_pythonpath || !_prefix || !_exec_prefix || !lib_python) { - Py_FatalError( - "Unable to decode path variables in getpath.c: " - "memory error"); +static void +calculate_reduce_exec_prefix(PyCalculatePath *calculate, PyPathConfig *config) +{ + if (calculate->exec_prefix_found > 0) { + reduce(config->exec_prefix); + reduce(config->exec_prefix); + reduce(config->exec_prefix); + if (!config->exec_prefix[0]) { + wcscpy(config->exec_prefix, separator); + } } - - if (_path) { - path_buffer = Py_DecodeLocale(_path, NULL); - path = path_buffer; + else { + wcsncpy(config->exec_prefix, calculate->exec_prefix, MAXPATHLEN); } +} + +static void +calculate_progpath(PyCalculatePath *calculate, PyPathConfig *config) +{ /* If there is no slash in the argv0 path, then we have to * assume python is on the user's $PATH, since there's no * other way to find a directory to start the search from. If * $PATH isn't exported, you lose. */ - if (wcschr(prog, SEP)) - wcsncpy(progpath, prog, MAXPATHLEN); + if (wcschr(calculate->prog, SEP)) { + wcsncpy(config->progpath, calculate->prog, MAXPATHLEN); + } + #ifdef __APPLE__ +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + uint32_t nsexeclength = MAXPATHLEN; +#else + unsigned long nsexeclength = MAXPATHLEN; +#endif + char execpath[MAXPATHLEN+1]; + /* On Mac OS X, if a script uses an interpreter of the form * "#!/opt/python2.3/bin/python", the kernel only passes "python" * as argv[0], which falls through to the $PATH search below. @@ -524,47 +617,60 @@ calculate_path(const _PyMainInterpreterConfig *config) * absolutize() should help us out below */ else if(0 == _NSGetExecutablePath(execpath, &nsexeclength) && execpath[0] == SEP) { - size_t r = mbstowcs(progpath, execpath, MAXPATHLEN+1); + size_t r = mbstowcs(config->progpath, execpath, MAXPATHLEN+1); if (r == (size_t)-1 || r > MAXPATHLEN) { /* Could not convert execpath, or it's too long. */ - progpath[0] = '\0'; + config->progpath[0] = '\0'; } } #endif /* __APPLE__ */ - else if (path) { + else if (calculate->path_env) { + wchar_t *path = calculate->path_env; while (1) { wchar_t *delim = wcschr(path, DELIM); if (delim) { size_t len = delim - path; - if (len > MAXPATHLEN) + if (len > MAXPATHLEN) { len = MAXPATHLEN; - wcsncpy(progpath, path, len); - *(progpath + len) = '\0'; + } + wcsncpy(config->progpath, path, len); + *(config->progpath + len) = '\0'; + } + else { + wcsncpy(config->progpath, path, MAXPATHLEN); } - else - wcsncpy(progpath, path, MAXPATHLEN); - joinpath(progpath, prog); - if (isxfile(progpath)) + joinpath(config->progpath, calculate->prog); + if (isxfile(config->progpath)) { break; + } if (!delim) { - progpath[0] = L'\0'; + config->progpath[0] = L'\0'; break; } path = delim + 1; } } - else - progpath[0] = '\0'; - PyMem_RawFree(path_buffer); - if (progpath[0] != SEP && progpath[0] != '\0') - absolutize(progpath); - wcsncpy(argv0_path, progpath, MAXPATHLEN); - argv0_path[MAXPATHLEN] = '\0'; + else { + config->progpath[0] = '\0'; + } + if (config->progpath[0] != SEP && config->progpath[0] != '\0') { + absolutize(config->progpath); + } +} + + +static void +calculate_argv0_path(PyCalculatePath *calculate, PyPathConfig *config) +{ + wcsncpy(calculate->argv0_path, config->progpath, MAXPATHLEN); + calculate->argv0_path[MAXPATHLEN] = '\0'; #ifdef WITH_NEXT_FRAMEWORK + NSModule pythonModule; + /* On Mac OS X we have a special case if we're running from a framework. ** This is because the python home should be set relative to the library, ** which is in the framework, not relative to the executable, which may @@ -572,7 +678,7 @@ calculate_path(const _PyMainInterpreterConfig *config) */ pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize")); /* Use dylib functions to find out where the framework was loaded from */ - modPath = NSLibraryNameForModule(pythonModule); + const char* modPath = NSLibraryNameForModule(pythonModule); if (modPath != NULL) { /* We're in a framework. */ /* See if we might be in the build directory. The framework in the @@ -587,153 +693,132 @@ calculate_path(const _PyMainInterpreterConfig *config) Py_FatalError("Cannot decode framework location"); } - wcsncpy(argv0_path, wbuf, MAXPATHLEN); - reduce(argv0_path); - joinpath(argv0_path, lib_python); - joinpath(argv0_path, LANDMARK); - if (!ismodule(argv0_path)) { + wcsncpy(calculate->argv0_path, wbuf, MAXPATHLEN); + reduce(calculate->argv0_path); + joinpath(calculate->argv0_path, calculate->lib_python); + joinpath(calculate->argv0_path, LANDMARK); + if (!ismodule(calculate->argv0_path)) { /* We are in the build directory so use the name of the executable - we know that the absolute path is passed */ - wcsncpy(argv0_path, progpath, MAXPATHLEN); + wcsncpy(calculate->argv0_path, config->progpath, MAXPATHLEN); } else { /* Use the location of the library as the progpath */ - wcsncpy(argv0_path, wbuf, MAXPATHLEN); + wcsncpy(calculate->argv0_path, wbuf, MAXPATHLEN); } PyMem_RawFree(wbuf); } #endif #if HAVE_READLINK - { - wchar_t tmpbuffer[MAXPATHLEN+1]; - int linklen = _Py_wreadlink(progpath, tmpbuffer, MAXPATHLEN); - while (linklen != -1) { - if (tmpbuffer[0] == SEP) - /* tmpbuffer should never be longer than MAXPATHLEN, - but extra check does not hurt */ - wcsncpy(argv0_path, tmpbuffer, MAXPATHLEN); - else { - /* Interpret relative to progpath */ - reduce(argv0_path); - joinpath(argv0_path, tmpbuffer); - } - linklen = _Py_wreadlink(argv0_path, tmpbuffer, MAXPATHLEN); + wchar_t tmpbuffer[MAXPATHLEN+1]; + int linklen = _Py_wreadlink(config->progpath, tmpbuffer, MAXPATHLEN); + while (linklen != -1) { + if (tmpbuffer[0] == SEP) { + /* tmpbuffer should never be longer than MAXPATHLEN, + but extra check does not hurt */ + wcsncpy(calculate->argv0_path, tmpbuffer, MAXPATHLEN); + } + else { + /* Interpret relative to progpath */ + reduce(calculate->argv0_path); + joinpath(calculate->argv0_path, tmpbuffer); } + linklen = _Py_wreadlink(calculate->argv0_path, tmpbuffer, MAXPATHLEN); } #endif /* HAVE_READLINK */ - reduce(argv0_path); + reduce(calculate->argv0_path); /* At this point, argv0_path is guaranteed to be less than - MAXPATHLEN bytes long. - */ + MAXPATHLEN bytes long. */ +} - /* Search for an environment configuration file, first in the - executable's directory and then in the parent directory. - If found, open it for use when searching for prefixes. - */ - { - wchar_t tmpbuffer[MAXPATHLEN+1]; - wchar_t *env_cfg = L"pyvenv.cfg"; - FILE * env_file = NULL; +/* Search for an "pyvenv.cfg" environment configuration file, first in the + executable's directory and then in the parent directory. + If found, open it for use when searching for prefixes. +*/ +static void +calculate_read_pyenv(PyCalculatePath *calculate) +{ + wchar_t tmpbuffer[MAXPATHLEN+1]; + wchar_t *env_cfg = L"pyvenv.cfg"; + FILE *env_file; + + wcscpy(tmpbuffer, calculate->argv0_path); - wcscpy(tmpbuffer, argv0_path); + joinpath(tmpbuffer, env_cfg); + env_file = _Py_wfopen(tmpbuffer, L"r"); + if (env_file == NULL) { + errno = 0; + reduce(tmpbuffer); + reduce(tmpbuffer); joinpath(tmpbuffer, env_cfg); + env_file = _Py_wfopen(tmpbuffer, L"r"); if (env_file == NULL) { errno = 0; - reduce(tmpbuffer); - reduce(tmpbuffer); - joinpath(tmpbuffer, env_cfg); - env_file = _Py_wfopen(tmpbuffer, L"r"); - if (env_file == NULL) { - errno = 0; - } - } - if (env_file != NULL) { - /* Look for a 'home' variable and set argv0_path to it, if found */ - if (find_env_config_value(env_file, L"home", tmpbuffer)) { - wcscpy(argv0_path, tmpbuffer); - } - fclose(env_file); - env_file = NULL; } } - pfound = search_for_prefix(argv0_path, home, _prefix, lib_python); - if (!pfound) { - if (!Py_FrozenFlag) - fprintf(stderr, - "Could not find platform independent libraries <prefix>\n"); - wcsncpy(prefix, _prefix, MAXPATHLEN); - joinpath(prefix, lib_python); - } - else - reduce(prefix); - - wcsncpy(zip_path, prefix, MAXPATHLEN); - zip_path[MAXPATHLEN] = L'\0'; - if (pfound > 0) { /* Use the reduced prefix returned by Py_GetPrefix() */ - reduce(zip_path); - reduce(zip_path); - } - else - wcsncpy(zip_path, _prefix, MAXPATHLEN); - joinpath(zip_path, L"lib/python00.zip"); - bufsz = wcslen(zip_path); /* Replace "00" with version */ - zip_path[bufsz - 6] = VERSION[0]; - zip_path[bufsz - 5] = VERSION[2]; - - efound = search_for_exec_prefix(argv0_path, home, - _exec_prefix, lib_python); - if (!efound) { - if (!Py_FrozenFlag) - fprintf(stderr, - "Could not find platform dependent libraries <exec_prefix>\n"); - wcsncpy(exec_prefix, _exec_prefix, MAXPATHLEN); - joinpath(exec_prefix, L"lib/lib-dynload"); + if (env_file == NULL) { + return; } - /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */ - if ((!pfound || !efound) && !Py_FrozenFlag) - fprintf(stderr, - "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n"); + /* Look for a 'home' variable and set argv0_path to it, if found */ + if (find_env_config_value(env_file, L"home", tmpbuffer)) { + wcscpy(calculate->argv0_path, tmpbuffer); + } + fclose(env_file); +} - /* Calculate size of return buffer. - */ - bufsz = 0; - wchar_t *env_path = NULL; - if (config) { - if (config->module_search_path_env) { - bufsz += wcslen(config->module_search_path_env) + 1; - } +static void +calculate_zip_path(PyCalculatePath *calculate, PyPathConfig *config) +{ + wcsncpy(calculate->zip_path, config->prefix, MAXPATHLEN); + calculate->zip_path[MAXPATHLEN] = L'\0'; + + if (calculate->prefix_found > 0) { + /* Use the reduced prefix returned by Py_GetPrefix() */ + reduce(calculate->zip_path); + reduce(calculate->zip_path); } else { - char *env_pathb = Py_GETENV("PYTHONPATH"); - if (env_pathb && env_pathb[0] != '\0') { - size_t env_path_len; - env_path = Py_DecodeLocale(env_pathb, &env_path_len); - /* FIXME: handle decoding and memory error */ - if (env_path != NULL) { - bufsz += env_path_len + 1; - } - } + wcsncpy(calculate->zip_path, calculate->prefix, MAXPATHLEN); } + joinpath(calculate->zip_path, L"lib/python00.zip"); + + /* Replace "00" with version */ + size_t bufsz = wcslen(calculate->zip_path); + calculate->zip_path[bufsz - 6] = VERSION[0]; + calculate->zip_path[bufsz - 5] = VERSION[2]; +} - defpath = _pythonpath; - prefixsz = wcslen(prefix) + 1; + +static wchar_t * +calculate_module_search_path(PyCalculatePath *calculate, PyPathConfig *config) +{ + /* Calculate size of return buffer */ + size_t bufsz = 0; + if (calculate->module_search_path_env != NULL) { + bufsz += wcslen(calculate->module_search_path_env) + 1; + } + + wchar_t *defpath = calculate->pythonpath; + size_t prefixsz = wcslen(config->prefix) + 1; while (1) { wchar_t *delim = wcschr(defpath, DELIM); - if (defpath[0] != SEP) + if (defpath[0] != SEP) { /* Paths are relative to prefix */ bufsz += prefixsz; + } - if (delim) + if (delim) { bufsz += delim - defpath + 1; + } else { bufsz += wcslen(defpath) + 1; break; @@ -741,46 +826,40 @@ calculate_path(const _PyMainInterpreterConfig *config) defpath = delim + 1; } - bufsz += wcslen(zip_path) + 1; - bufsz += wcslen(exec_prefix) + 1; + bufsz += wcslen(calculate->zip_path) + 1; + bufsz += wcslen(config->exec_prefix) + 1; - buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t)); + /* Allocate the buffer */ + wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t)); if (buf == NULL) { Py_FatalError( "Not enough memory for dynamic PYTHONPATH"); } + buf[0] = '\0'; /* Run-time value of $PYTHONPATH goes first */ - buf[0] = '\0'; - if (config) { - if (config->module_search_path_env) { - wcscpy(buf, config->module_search_path_env); - wcscat(buf, delimiter); - } - } - else { - if (env_path) { - wcscpy(buf, env_path); - wcscat(buf, delimiter); - } + if (calculate->module_search_path_env) { + wcscpy(buf, calculate->module_search_path_env); + wcscat(buf, delimiter); } - PyMem_RawFree(env_path); /* Next is the default zip path */ - wcscat(buf, zip_path); + wcscat(buf, calculate->zip_path); wcscat(buf, delimiter); /* Next goes merge of compile-time $PYTHONPATH with * dynamically located prefix. */ - defpath = _pythonpath; + defpath = calculate->pythonpath; while (1) { wchar_t *delim = wcschr(defpath, DELIM); if (defpath[0] != SEP) { - wcscat(buf, prefix); - if (prefixsz >= 2 && prefix[prefixsz - 2] != SEP && - defpath[0] != (delim ? DELIM : L'\0')) { /* not empty */ + wcscat(buf, config->prefix); + if (prefixsz >= 2 && config->prefix[prefixsz - 2] != SEP && + defpath[0] != (delim ? DELIM : L'\0')) + { + /* not empty */ wcscat(buf, separator); } } @@ -800,41 +879,130 @@ calculate_path(const _PyMainInterpreterConfig *config) wcscat(buf, delimiter); /* Finally, on goes the directory for dynamic-load modules */ - wcscat(buf, exec_prefix); + wcscat(buf, config->exec_prefix); - /* And publish the results */ - module_search_path = buf; + return buf; +} - /* Reduce prefix and exec_prefix to their essence, - * e.g. /usr/local/lib/python1.5 is reduced to /usr/local. - * If we're loading relative to the build directory, - * return the compiled-in defaults instead. - */ - if (pfound > 0) { - reduce(prefix); - reduce(prefix); - /* The prefix is the root directory, but reduce() chopped - * off the "/". */ - if (!prefix[0]) - wcscpy(prefix, separator); + +#define DECODE_FAILED(NAME, LEN) \ + ((LEN) == (size_t)-2) \ + ? _Py_INIT_ERR("failed to decode " #NAME) \ + : _Py_INIT_NO_MEMORY() + + +static _PyInitError +calculate_init(PyCalculatePath *calculate, + const _PyMainInterpreterConfig *main_config) +{ + _PyInitError err; + + err = _Py_GetPythonHomeWithConfig(main_config, &calculate->home); + if (_Py_INIT_FAILED(err)) { + return err; + } + + size_t len; + char *path = getenv("PATH"); + if (path) { + calculate->path_env = Py_DecodeLocale(path, &len); + if (!calculate->path_env) { + return DECODE_FAILED("PATH environment variable", len); + } + } + + calculate->prog = Py_GetProgramName(); + + calculate->pythonpath = Py_DecodeLocale(PYTHONPATH, &len); + if (!calculate->pythonpath) { + return DECODE_FAILED("PYTHONPATH define", len); + } + calculate->prefix = Py_DecodeLocale(PREFIX, &len); + if (!calculate->prefix) { + return DECODE_FAILED("PREFIX define", len); + } + calculate->exec_prefix = Py_DecodeLocale(EXEC_PREFIX, &len); + if (!calculate->prefix) { + return DECODE_FAILED("EXEC_PREFIX define", len); + } + calculate->lib_python = Py_DecodeLocale("lib/python" VERSION, &len); + if (!calculate->lib_python) { + return DECODE_FAILED("EXEC_PREFIX define", len); } - else - wcsncpy(prefix, _prefix, MAXPATHLEN); - if (efound > 0) { - reduce(exec_prefix); - reduce(exec_prefix); - reduce(exec_prefix); - if (!exec_prefix[0]) - wcscpy(exec_prefix, separator); + calculate->module_search_path_env = NULL; + if (main_config) { + if (main_config->module_search_path_env) { + calculate->module_search_path_env = main_config->module_search_path_env; + } + + } + else { + char *pythonpath = Py_GETENV("PYTHONPATH"); + if (pythonpath && pythonpath[0] != '\0') { + calculate->module_search_path_buffer = Py_DecodeLocale(pythonpath, &len); + if (!calculate->module_search_path_buffer) { + return DECODE_FAILED("PYTHONPATH environment variable", len); + } + calculate->module_search_path_env = calculate->module_search_path_buffer; + } } - else - wcsncpy(exec_prefix, _exec_prefix, MAXPATHLEN); + return _Py_INIT_OK(); +} + - PyMem_RawFree(_pythonpath); - PyMem_RawFree(_prefix); - PyMem_RawFree(_exec_prefix); - PyMem_RawFree(lib_python); +static void +calculate_free(PyCalculatePath *calculate) +{ + PyMem_RawFree(calculate->pythonpath); + PyMem_RawFree(calculate->prefix); + PyMem_RawFree(calculate->exec_prefix); + PyMem_RawFree(calculate->lib_python); + PyMem_RawFree(calculate->path_env); + PyMem_RawFree(calculate->module_search_path_buffer); +} + + +static void +calculate_path_impl(PyCalculatePath *calculate, PyPathConfig *config) +{ + calculate_progpath(calculate, config); + calculate_argv0_path(calculate, config); + calculate_read_pyenv(calculate); + calculate_prefix(calculate, config); + calculate_zip_path(calculate, config); + calculate_exec_prefix(calculate, config); + + if ((!calculate->prefix_found || !calculate->exec_prefix_found) && !Py_FrozenFlag) { + fprintf(stderr, + "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n"); + } + + config->module_search_path = calculate_module_search_path(calculate, config); + calculate_reduce_prefix(calculate, config); + calculate_reduce_exec_prefix(calculate, config); +} + + +static void +calculate_path(const _PyMainInterpreterConfig *main_config) +{ + PyCalculatePath calculate; + memset(&calculate, 0, sizeof(calculate)); + + _PyInitError err = calculate_init(&calculate, main_config); + if (_Py_INIT_FAILED(err)) { + calculate_free(&calculate); + _Py_FatalInitError(err); + } + + PyPathConfig new_path_config; + memset(&new_path_config, 0, sizeof(new_path_config)); + + calculate_path_impl(&calculate, &new_path_config); + path_config = new_path_config; + + calculate_free(&calculate); } @@ -842,63 +1010,74 @@ calculate_path(const _PyMainInterpreterConfig *config) void Py_SetPath(const wchar_t *path) { - if (module_search_path != NULL) { - PyMem_RawFree(module_search_path); - module_search_path = NULL; + if (path_config.module_search_path != NULL) { + PyMem_RawFree(path_config.module_search_path); + path_config.module_search_path = NULL; } - if (path != NULL) { - extern wchar_t *Py_GetProgramName(void); - wchar_t *prog = Py_GetProgramName(); - wcsncpy(progpath, prog, MAXPATHLEN); - exec_prefix[0] = prefix[0] = L'\0'; - module_search_path = PyMem_RawMalloc((wcslen(path) + 1) * sizeof(wchar_t)); - if (module_search_path != NULL) - wcscpy(module_search_path, path); + + if (path == NULL) { + return; + } + + wchar_t *prog = Py_GetProgramName(); + wcsncpy(path_config.progpath, prog, MAXPATHLEN); + path_config.exec_prefix[0] = path_config.prefix[0] = L'\0'; + path_config.module_search_path = PyMem_RawMalloc((wcslen(path) + 1) * sizeof(wchar_t)); + if (path_config.module_search_path != NULL) { + wcscpy(path_config.module_search_path, path); } } + wchar_t * -_Py_GetPathWithConfig(const _PyMainInterpreterConfig *config) +_Py_GetPathWithConfig(const _PyMainInterpreterConfig *main_config) { - if (!module_search_path) { - calculate_path(config); + if (!path_config.module_search_path) { + calculate_path(main_config); } - return module_search_path; + return path_config.module_search_path; } + wchar_t * Py_GetPath(void) { - if (!module_search_path) + if (!path_config.module_search_path) { calculate_path(NULL); - return module_search_path; + } + return path_config.module_search_path; } + wchar_t * Py_GetPrefix(void) { - if (!module_search_path) + if (!path_config.module_search_path) { calculate_path(NULL); - return prefix; + } + return path_config.prefix; } + wchar_t * Py_GetExecPrefix(void) { - if (!module_search_path) + if (!path_config.module_search_path) { calculate_path(NULL); - return exec_prefix; + } + return path_config.exec_prefix; } + wchar_t * Py_GetProgramFullPath(void) { - if (!module_search_path) + if (!path_config.module_search_path) { calculate_path(NULL); - return progpath; + } + return path_config.progpath; } - #ifdef __cplusplus } #endif diff --git a/Modules/main.c b/Modules/main.c index 07e0d2a..349d8c3 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -36,6 +36,16 @@ extern "C" { #endif +#define SET_DECODE_ERROR(NAME, LEN) \ + do { \ + if ((LEN) == (size_t)-2) { \ + pymain->err = _Py_INIT_ERR("failed to decode " #NAME); \ + } \ + else { \ + pymain->err = _Py_INIT_NO_MEMORY(); \ + } \ + } while (0) + /* For Py_GetArgcArgv(); set by main() */ static wchar_t **orig_argv; static int orig_argc; @@ -417,9 +427,6 @@ typedef struct { .env_warning_options = {0, NULL}} -#define INIT_NO_MEMORY() _Py_INIT_ERR("memory allocation failed") - - static void pymain_optlist_clear(_Py_OptList *list) { @@ -510,14 +517,14 @@ pymain_wstrdup(_PyMain *pymain, wchar_t *str) { size_t len = wcslen(str) + 1; /* +1 for NUL character */ if (len > (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return NULL; } size_t size = len * sizeof(wchar_t); wchar_t *str2 = PyMem_RawMalloc(size); if (str2 == NULL) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return NULL; } @@ -538,7 +545,7 @@ pymain_optlist_append(_PyMain *pymain, _Py_OptList *list, wchar_t *str) wchar_t **options2 = (wchar_t **)PyMem_RawRealloc(list->options, size); if (options2 == NULL) { PyMem_RawFree(str2); - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } options2[list->len] = str2; @@ -571,7 +578,7 @@ pymain_parse_cmdline_impl(_PyMain *pymain) size_t len = wcslen(_PyOS_optarg) + 1 + 1; wchar_t *command = PyMem_RawMalloc(sizeof(wchar_t) * len); if (command == NULL) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } memcpy(command, _PyOS_optarg, len * sizeof(wchar_t)); @@ -717,7 +724,7 @@ pymain_add_xoptions(_PyMain *pymain) for (size_t i=0; i < options->len; i++) { wchar_t *option = options->options[i]; if (_PySys_AddXOptionWithError(option) < 0) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } } @@ -748,11 +755,11 @@ pymain_add_warnings_options(_PyMain *pymain) PySys_ResetWarnOptions(); if (pymain_add_warnings_optlist(&pymain->env_warning_options) < 0) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } if (pymain_add_warnings_optlist(&pymain->cmdline.warning_options) < 0) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } return 0; @@ -801,7 +808,7 @@ pymain_warnings_envvar(_PyMain *pymain) C89 wcstok */ buf = (char *)PyMem_RawMalloc(strlen(p) + 1); if (buf == NULL) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } strcpy(buf, p); @@ -811,13 +818,7 @@ pymain_warnings_envvar(_PyMain *pymain) size_t len; wchar_t *warning = Py_DecodeLocale(p, &len); if (warning == NULL) { - if (len == (size_t)-2) { - pymain->err = _Py_INIT_ERR("failed to decode " - "PYTHONWARNINGS"); - } - else { - pymain->err = INIT_NO_MEMORY(); - } + SET_DECODE_ERROR("PYTHONWARNINGS environment variable", len); return -1; } if (pymain_optlist_append(pymain, &pymain->env_warning_options, @@ -902,7 +903,7 @@ pymain_get_program_name(_PyMain *pymain) buffer = PyMem_RawMalloc(len * sizeof(wchar_t)); if (buffer == NULL) { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } @@ -919,15 +920,8 @@ pymain_get_program_name(_PyMain *pymain) size_t len; wchar_t* wbuf = Py_DecodeLocale(pyvenv_launcher, &len); if (wbuf == NULL) { - if (len == (size_t)-2) { - pymain->err = _Py_INIT_ERR("failed to decode " - "__PYVENV_LAUNCHER__"); - return -1; - } - else { - pymain->err = INIT_NO_MEMORY(); - return -1; - } + SET_DECODE_ERROR("__PYVENV_LAUNCHER__", len); + return -1; } pymain->program_name = wbuf; } @@ -1403,7 +1397,7 @@ pymain_get_env_var_dup(_PyMain *pymain, wchar_t **dest, return -2; } else { - pymain->err = INIT_NO_MEMORY(); + pymain->err = _Py_INIT_NO_MEMORY(); return -1; } } @@ -1421,7 +1415,7 @@ pymain_init_pythonpath(_PyMain *pymain) L"PYTHONPATH", "PYTHONPATH"); if (res < 0) { if (res == -2) { - pymain->err = _Py_INIT_ERR("failed to decode PYTHONPATH"); + SET_DECODE_ERROR("PYTHONPATH", (size_t)-2); } return -1; } @@ -1450,7 +1444,7 @@ pymain_init_pythonhome(_PyMain *pymain) L"PYTHONHOME", "PYTHONHOME"); if (res < 0) { if (res == -2) { - pymain->err = _Py_INIT_ERR("failed to decode PYTHONHOME"); + SET_DECODE_ERROR("PYTHONHOME", (size_t)-2); } return -1; } |