| /* Return the initial module search path. */ | |
| #include "Python.h" | |
| #include "pycore_fileutils.h" // _Py_abspath() | |
| #include "pycore_initconfig.h" // _PyStatus_EXCEPTION() | |
| #include "pycore_pathconfig.h" // _PyPathConfig_ReadGlobal() | |
| #include "pycore_pymem.h" // _PyMem_RawWcsdup() | |
| #include "pycore_pystate.h" // _PyThreadState_GET() | |
| #include "marshal.h" // PyMarshal_ReadObjectFromString | |
| #include "osdefs.h" // DELIM | |
| #include <wchar.h> | |
| #ifdef MS_WINDOWS | |
| # include <windows.h> // GetFullPathNameW(), MAX_PATH | |
| # include <pathcch.h> | |
| #endif | |
| #ifdef __APPLE__ | |
| # include <mach-o/dyld.h> | |
| #endif | |
| #ifdef HAVE_DLFCN_H | |
| # include <dlfcn.h> | |
| #endif | |
| /* Reference the precompiled getpath.py */ | |
| #include "Python/frozen_modules/getpath.h" | |
| #if (!defined(PREFIX) || !defined(EXEC_PREFIX) \ | |
| || !defined(VERSION) || !defined(VPATH) \ | |
| || !defined(PLATLIBDIR)) | |
| #error "PREFIX, EXEC_PREFIX, VERSION, VPATH and PLATLIBDIR macros must be defined" | |
| #endif | |
| #if !defined(PYTHONPATH) | |
| #define PYTHONPATH NULL | |
| #endif | |
| #if !defined(PYDEBUGEXT) | |
| #define PYDEBUGEXT NULL | |
| #endif | |
| #if !defined(PYWINVER) | |
| #ifdef MS_DLL_ID | |
| #define PYWINVER MS_DLL_ID | |
| #else | |
| #define PYWINVER NULL | |
| #endif | |
| #endif | |
| #if !defined(EXE_SUFFIX) | |
| #if defined(MS_WINDOWS) || defined(__CYGWIN__) || defined(__MINGW32__) | |
| #define EXE_SUFFIX L".exe" | |
| #else | |
| #define EXE_SUFFIX NULL | |
| #endif | |
| #endif | |
| /*[clinic input] | |
| module getpath | |
| [clinic start generated code]*/ | |
| /*[clinic end generated code: output=da39a3ee5e6b4b0d input=b425aa3ea7292100]*/ | |
| #include "clinic/getpath.c.h" | |
| /* HELPER FUNCTIONS for getpath.py */ | |
| /*[clinic input] | |
| getpath.abspath | |
| path: Py_UNICODE | |
| / | |
| Return the absolute path. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| getpath_abspath_impl(PyObject *module, const wchar_t *path) | |
| /*[clinic end generated code: output=5009336e32e75b9b input=0fd554ce535744f2]*/ | |
| { | |
| wchar_t *abs; | |
| if (_Py_abspath(_Py_normpath((wchar_t *)path, -1), &abs) < 0 || abs == NULL) { | |
| PyErr_SetString(PyExc_OSError, "failed to make path absolute"); | |
| return NULL; | |
| } | |
| PyObject *r = PyUnicode_FromWideChar(abs, -1); | |
| PyMem_RawFree((void *)abs); | |
| return r; | |
| } | |
| /*[clinic input] | |
| getpath.basename | |
| path: unicode | |
| / | |
| Return the final component of the path. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| getpath_basename_impl(PyObject *module, PyObject *path) | |
| /*[clinic end generated code: output=d383d3b34842236e input=0de5b71fc78bbad8]*/ | |
| { | |
| Py_ssize_t end = PyUnicode_GET_LENGTH(path); | |
| Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); | |
| if (pos < 0) { | |
| return Py_NewRef(path); | |
| } | |
| return PyUnicode_Substring(path, pos + 1, end); | |
| } | |
| /*[clinic input] | |
| getpath.dirname | |
| path: unicode | |
| / | |
| Return the directory component of the path. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| getpath_dirname_impl(PyObject *module, PyObject *path) | |
| /*[clinic end generated code: output=6887a025f5f6784c input=0c33269fe28957fb]*/ | |
| { | |
| Py_ssize_t end = PyUnicode_GET_LENGTH(path); | |
| Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1); | |
| if (pos < 0) { | |
| return Py_GetConstant(Py_CONSTANT_EMPTY_STR); | |
| } | |
| return PyUnicode_Substring(path, 0, pos); | |
| } | |
| /*[clinic input] | |
| getpath.isabs -> bool | |
| path: Py_UNICODE | |
| / | |
| Return True if the path is absolute. | |
| [clinic start generated code]*/ | |
| static int | |
| getpath_isabs_impl(PyObject *module, const wchar_t *path) | |
| /*[clinic end generated code: output=4659b9e6f088c2ca input=04931a206cdd77f6]*/ | |
| { | |
| return _Py_isabs(path); | |
| } | |
| /*[clinic input] | |
| getpath.hassuffix -> bool | |
| path as pathobj: unicode | |
| suffix as suffixobj: unicode | |
| / | |
| Return True if the path ends with the suffix, ignoring the case. | |
| [clinic start generated code]*/ | |
| static int | |
| getpath_hassuffix_impl(PyObject *module, PyObject *pathobj, | |
| PyObject *suffixobj) | |
| /*[clinic end generated code: output=c4cbd6f2f567f83c input=4b477740575b94d8]*/ | |
| { | |
| int r = -1; | |
| Py_ssize_t len, suffixLen; | |
| const wchar_t *path = PyUnicode_AsWideCharString(pathobj, &len); | |
| if (path) { | |
| const wchar_t *suffix = PyUnicode_AsWideCharString(suffixobj, | |
| &suffixLen); | |
| if (suffix) { | |
| r = suffixLen <= len && | |
| #ifdef MS_WINDOWS | |
| wcsicmp(&path[len - suffixLen], suffix) == 0; | |
| #else | |
| wcscmp(&path[len - suffixLen], suffix) == 0; | |
| #endif | |
| PyMem_Free((void *)suffix); | |
| } | |
| PyMem_Free((void *)path); | |
| } | |
| return r; | |
| } | |
| /*[clinic input] | |
| getpath.isdir -> bool | |
| path: Py_UNICODE | |
| / | |
| Return True if the path is a directory. | |
| [clinic start generated code]*/ | |
| static int | |
| getpath_isdir_impl(PyObject *module, const wchar_t *path) | |
| /*[clinic end generated code: output=467820dd83daa7a6 input=0ddd588cd5357a9a]*/ | |
| { | |
| #ifdef MS_WINDOWS | |
| DWORD attr = GetFileAttributesW(path); | |
| return (attr != INVALID_FILE_ATTRIBUTES) | |
| && (attr & FILE_ATTRIBUTE_DIRECTORY); | |
| #else | |
| struct stat st; | |
| return (_Py_wstat(path, &st) == 0) && S_ISDIR(st.st_mode); | |
| #endif | |
| } | |
| /*[clinic input] | |
| getpath.isfile -> bool | |
| path as pathobj: unicode | |
| / | |
| Return True if the path is a regular file. | |
| [clinic start generated code]*/ | |
| static int | |
| getpath_isfile_impl(PyObject *module, PyObject *pathobj) | |
| /*[clinic end generated code: output=197acb7a5434b9d1 input=ba1c2746ec8f2f47]*/ | |
| { | |
| #ifdef MS_WINDOWS | |
| wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL); | |
| if (path == NULL) { | |
| return -1; | |
| } | |
| DWORD attr = GetFileAttributesW(path); | |
| PyMem_Free(path); | |
| return (attr != INVALID_FILE_ATTRIBUTES) | |
| && !(attr & FILE_ATTRIBUTE_DIRECTORY); | |
| #else | |
| struct stat st; | |
| int res = _Py_stat(pathobj, &st); | |
| if (res == -2) { | |
| return -1; | |
| } | |
| return (res == 0) && S_ISREG(st.st_mode); | |
| #endif | |
| } | |
| /*[clinic input] | |
| getpath.isxfile -> bool | |
| path as pathobj: unicode | |
| / | |
| Return True if the path is an executable file. | |
| [clinic start generated code]*/ | |
| static int | |
| getpath_isxfile_impl(PyObject *module, PyObject *pathobj) | |
| /*[clinic end generated code: output=2f0bc6708d945bc8 input=6d99faa90e262511]*/ | |
| { | |
| #ifdef MS_WINDOWS | |
| Py_ssize_t cchPath; | |
| wchar_t *path = PyUnicode_AsWideCharString(pathobj, &cchPath); | |
| if (path == NULL) { | |
| return -1; | |
| } | |
| DWORD attr = GetFileAttributesW(path); | |
| int isxfile = (attr != INVALID_FILE_ATTRIBUTES) && | |
| !(attr & FILE_ATTRIBUTE_DIRECTORY) && | |
| (cchPath >= 4) && | |
| (CompareStringOrdinal(path + cchPath - 4, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL); | |
| PyMem_Free(path); | |
| return isxfile; | |
| #else | |
| struct stat st; | |
| int res = _Py_stat(pathobj, &st); | |
| if (res == -2) { | |
| return -1; | |
| } | |
| return (res == 0) && S_ISREG(st.st_mode) && (st.st_mode & 0111); | |
| #endif | |
| } | |
| /*[clinic input] | |
| getpath.joinpath | |
| *args: tuple | |
| Join the path components. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| getpath_joinpath_impl(PyObject *module, PyObject *args) | |
| /*[clinic end generated code: output=0044d429b32e5f5a input=5c67eeca4a6d1413]*/ | |
| { | |
| Py_ssize_t n = PyTuple_GET_SIZE(args); | |
| if (n == 0) { | |
| return Py_GetConstant(Py_CONSTANT_EMPTY_STR); | |
| } | |
| /* Convert all parts to wchar and accumulate max final length */ | |
| wchar_t **parts = (wchar_t **)PyMem_Malloc(n * sizeof(wchar_t *)); | |
| if (parts == NULL) { | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| memset(parts, 0, n * sizeof(wchar_t *)); | |
| Py_ssize_t cchFinal = 0; | |
| Py_ssize_t first = 0; | |
| for (Py_ssize_t i = 0; i < n; ++i) { | |
| PyObject *s = PyTuple_GET_ITEM(args, i); | |
| Py_ssize_t cch; | |
| if (s == Py_None) { | |
| cch = 0; | |
| } else if (PyUnicode_Check(s)) { | |
| parts[i] = PyUnicode_AsWideCharString(s, &cch); | |
| if (!parts[i]) { | |
| cchFinal = -1; | |
| break; | |
| } | |
| if (_Py_isabs(parts[i])) { | |
| first = i; | |
| } | |
| } else { | |
| PyErr_SetString(PyExc_TypeError, "all arguments to joinpath() must be str or None"); | |
| cchFinal = -1; | |
| break; | |
| } | |
| cchFinal += cch + 1; | |
| } | |
| wchar_t *final = cchFinal > 0 ? (wchar_t *)PyMem_Malloc(cchFinal * sizeof(wchar_t)) : NULL; | |
| if (!final) { | |
| for (Py_ssize_t i = 0; i < n; ++i) { | |
| PyMem_Free(parts[i]); | |
| } | |
| PyMem_Free(parts); | |
| if (cchFinal) { | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| return Py_GetConstant(Py_CONSTANT_EMPTY_STR); | |
| } | |
| final[0] = '\0'; | |
| /* Now join all the paths. The final result should be shorter than the buffer */ | |
| for (Py_ssize_t i = 0; i < n; ++i) { | |
| if (!parts[i]) { | |
| continue; | |
| } | |
| if (i >= first && final) { | |
| if (!final[0]) { | |
| /* final is definitely long enough to fit any individual part */ | |
| wcscpy(final, parts[i]); | |
| } else if (_Py_add_relfile(final, parts[i], cchFinal) < 0) { | |
| /* if we fail, keep iterating to free memory, but stop adding parts */ | |
| PyMem_Free(final); | |
| final = NULL; | |
| } | |
| } | |
| PyMem_Free(parts[i]); | |
| } | |
| PyMem_Free(parts); | |
| if (!final) { | |
| PyErr_SetString(PyExc_SystemError, "failed to join paths"); | |
| return NULL; | |
| } | |
| PyObject *r = PyUnicode_FromWideChar(_Py_normpath(final, -1), -1); | |
| PyMem_Free(final); | |
| return r; | |
| } | |
| /*[clinic input] | |
| getpath.readlines | |
| path as pathobj: unicode | |
| / | |
| Return the lines of the file. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| getpath_readlines_impl(PyObject *module, PyObject *pathobj) | |
| /*[clinic end generated code: output=6a7c1a1d2bb23046 input=6f54150f72789b78]*/ | |
| { | |
| FILE *fp = Py_fopen(pathobj, "rb"); | |
| if (!fp) { | |
| return NULL; | |
| } | |
| PyObject *r = PyList_New(0); | |
| if (!r) { | |
| fclose(fp); | |
| return NULL; | |
| } | |
| const size_t MAX_FILE = 32 * 1024; | |
| char *buffer = (char *)PyMem_Malloc(MAX_FILE); | |
| if (!buffer) { | |
| Py_DECREF(r); | |
| fclose(fp); | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| size_t cb = fread(buffer, 1, MAX_FILE, fp); | |
| fclose(fp); | |
| if (!cb) { | |
| return r; | |
| } | |
| if (cb >= MAX_FILE) { | |
| Py_DECREF(r); | |
| PyErr_SetString(PyExc_MemoryError, | |
| "cannot read file larger than 32KB during initialization"); | |
| return NULL; | |
| } | |
| buffer[cb] = '\0'; | |
| size_t len; | |
| wchar_t *wbuffer = _Py_DecodeUTF8_surrogateescape(buffer, cb, &len); | |
| PyMem_Free((void *)buffer); | |
| if (!wbuffer) { | |
| Py_DECREF(r); | |
| PyErr_NoMemory(); | |
| return NULL; | |
| } | |
| wchar_t *p1 = wbuffer; | |
| wchar_t *p2 = p1; | |
| while ((p2 = wcschr(p1, L'\n')) != NULL) { | |
| Py_ssize_t cb = p2 - p1; | |
| while (cb >= 0 && (p1[cb] == L'\n' || p1[cb] == L'\r')) { | |
| --cb; | |
| } | |
| PyObject *u = PyUnicode_FromWideChar(p1, cb >= 0 ? cb + 1 : 0); | |
| if (!u || PyList_Append(r, u) < 0) { | |
| Py_XDECREF(u); | |
| Py_CLEAR(r); | |
| break; | |
| } | |
| Py_DECREF(u); | |
| p1 = p2 + 1; | |
| } | |
| if (r && p1 && *p1) { | |
| PyObject *u = PyUnicode_FromWideChar(p1, -1); | |
| if (!u || PyList_Append(r, u) < 0) { | |
| Py_CLEAR(r); | |
| } | |
| Py_XDECREF(u); | |
| } | |
| PyMem_RawFree(wbuffer); | |
| return r; | |
| } | |
| /*[clinic input] | |
| getpath.realpath | |
| path as pathobj: unicode | |
| / | |
| Resolve a symlinked file. | |
| [clinic start generated code]*/ | |
| static PyObject * | |
| getpath_realpath_impl(PyObject *module, PyObject *pathobj) | |
| /*[clinic end generated code: output=8559260e3786874e input=d3c5eee0ddd2c3e2]*/ | |
| { | |
| #if defined(HAVE_READLINK) | |
| /* This readlink calculation only resolves a symlinked file, and | |
| does not resolve any path segments. This is consistent with | |
| prior releases, however, the realpath implementation below is | |
| potentially correct in more cases. */ | |
| PyObject *r = NULL; | |
| int nlink = 0; | |
| wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL); | |
| if (!path) { | |
| goto done; | |
| } | |
| wchar_t *path2 = _PyMem_RawWcsdup(path); | |
| PyMem_Free((void *)path); | |
| path = path2; | |
| while (path) { | |
| wchar_t resolved[MAXPATHLEN + 1]; | |
| int linklen = _Py_wreadlink(path, resolved, Py_ARRAY_LENGTH(resolved)); | |
| if (linklen == -1) { | |
| r = PyUnicode_FromWideChar(path, -1); | |
| break; | |
| } | |
| if (_Py_isabs(resolved)) { | |
| PyMem_RawFree((void *)path); | |
| path = _PyMem_RawWcsdup(resolved); | |
| } else { | |
| wchar_t *s = wcsrchr(path, SEP); | |
| if (s) { | |
| *s = L'\0'; | |
| } | |
| path2 = _Py_join_relfile(path, resolved); | |
| if (path2) { | |
| path2 = _Py_normpath(path2, -1); | |
| } | |
| PyMem_RawFree((void *)path); | |
| path = path2; | |
| } | |
| nlink++; | |
| /* 40 is the Linux kernel 4.2 limit */ | |
| if (nlink >= 40) { | |
| PyErr_SetString(PyExc_OSError, "maximum number of symbolic links reached"); | |
| break; | |
| } | |
| } | |
| if (!path) { | |
| PyErr_NoMemory(); | |
| } | |
| done: | |
| PyMem_RawFree((void *)path); | |
| return r; | |
| #elif defined(HAVE_REALPATH) | |
| PyObject *r = NULL; | |
| struct stat st; | |
| const char *narrow = NULL; | |
| wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL); | |
| if (!path) { | |
| goto done; | |
| } | |
| narrow = Py_EncodeLocale(path, NULL); | |
| if (!narrow) { | |
| PyErr_NoMemory(); | |
| goto done; | |
| } | |
| if (lstat(narrow, &st)) { | |
| PyErr_SetFromErrno(PyExc_OSError); | |
| goto done; | |
| } | |
| if (!S_ISLNK(st.st_mode)) { | |
| r = Py_NewRef(pathobj); | |
| goto done; | |
| } | |
| wchar_t resolved[MAXPATHLEN+1]; | |
| if (_Py_wrealpath(path, resolved, MAXPATHLEN) == NULL) { | |
| PyErr_SetFromErrno(PyExc_OSError); | |
| } else { | |
| r = PyUnicode_FromWideChar(resolved, -1); | |
| } | |
| done: | |
| PyMem_Free((void *)path); | |
| PyMem_Free((void *)narrow); | |
| return r; | |
| #elif defined(MS_WINDOWS) | |
| HANDLE hFile; | |
| wchar_t resolved[MAXPATHLEN+1]; | |
| int len = 0, err; | |
| Py_ssize_t pathlen; | |
| PyObject *result; | |
| wchar_t *path = PyUnicode_AsWideCharString(pathobj, &pathlen); | |
| if (!path) { | |
| return NULL; | |
| } | |
| if (wcslen(path) != pathlen) { | |
| PyErr_SetString(PyExc_ValueError, "path contains embedded nulls"); | |
| return NULL; | |
| } | |
| Py_BEGIN_ALLOW_THREADS | |
| hFile = CreateFileW(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); | |
| if (hFile != INVALID_HANDLE_VALUE) { | |
| len = GetFinalPathNameByHandleW(hFile, resolved, MAXPATHLEN, VOLUME_NAME_DOS); | |
| err = len ? 0 : GetLastError(); | |
| CloseHandle(hFile); | |
| } else { | |
| err = GetLastError(); | |
| } | |
| Py_END_ALLOW_THREADS | |
| if (err) { | |
| PyErr_SetFromWindowsErr(err); | |
| result = NULL; | |
| } else if (len <= MAXPATHLEN) { | |
| const wchar_t *p = resolved; | |
| if (0 == wcsncmp(p, L"\\\\?\\", 4)) { | |
| if (GetFileAttributesW(&p[4]) != INVALID_FILE_ATTRIBUTES) { | |
| p += 4; | |
| len -= 4; | |
| } | |
| } | |
| if (CompareStringOrdinal(path, (int)pathlen, p, len, TRUE) == CSTR_EQUAL) { | |
| result = Py_NewRef(pathobj); | |
| } else { | |
| result = PyUnicode_FromWideChar(p, len); | |
| } | |
| } else { | |
| result = Py_NewRef(pathobj); | |
| } | |
| PyMem_Free(path); | |
| return result; | |
| #endif | |
| return Py_NewRef(pathobj); | |
| } | |
| static PyMethodDef getpath_methods[] = { | |
| GETPATH_ABSPATH_METHODDEF | |
| GETPATH_BASENAME_METHODDEF | |
| GETPATH_DIRNAME_METHODDEF | |
| GETPATH_HASSUFFIX_METHODDEF | |
| GETPATH_ISABS_METHODDEF | |
| GETPATH_ISDIR_METHODDEF | |
| GETPATH_ISFILE_METHODDEF | |
| GETPATH_ISXFILE_METHODDEF | |
| GETPATH_JOINPATH_METHODDEF | |
| GETPATH_READLINES_METHODDEF | |
| GETPATH_REALPATH_METHODDEF | |
| {NULL, NULL, 0, NULL} | |
| }; | |
| /* Two implementations of warn() to use depending on whether warnings | |
| are enabled or not. */ | |
| static PyObject * | |
| getpath_warn(PyObject *Py_UNUSED(self), PyObject *args) | |
| { | |
| PyObject *msgobj; | |
| if (!PyArg_ParseTuple(args, "U", &msgobj)) { | |
| return NULL; | |
| } | |
| fprintf(stderr, "%s\n", PyUnicode_AsUTF8(msgobj)); | |
| Py_RETURN_NONE; | |
| } | |
| static PyObject * | |
| getpath_nowarn(PyObject *Py_UNUSED(self), PyObject *args) | |
| { | |
| Py_RETURN_NONE; | |
| } | |
| static PyMethodDef getpath_warn_method = {"warn", getpath_warn, METH_VARARGS, NULL}; | |
| static PyMethodDef getpath_nowarn_method = {"warn", getpath_nowarn, METH_VARARGS, NULL}; | |
| /* Add the helper functions to the dict */ | |
| static int | |
| funcs_to_dict(PyObject *dict, int warnings) | |
| { | |
| for (PyMethodDef *m = getpath_methods; m->ml_name; ++m) { | |
| PyObject *f = PyCFunction_NewEx(m, NULL, NULL); | |
| if (!f) { | |
| return 0; | |
| } | |
| if (PyDict_SetItemString(dict, m->ml_name, f) < 0) { | |
| Py_DECREF(f); | |
| return 0; | |
| } | |
| Py_DECREF(f); | |
| } | |
| PyMethodDef *m2 = warnings ? &getpath_warn_method : &getpath_nowarn_method; | |
| PyObject *f = PyCFunction_NewEx(m2, NULL, NULL); | |
| if (!f) { | |
| return 0; | |
| } | |
| if (PyDict_SetItemString(dict, m2->ml_name, f) < 0) { | |
| Py_DECREF(f); | |
| return 0; | |
| } | |
| Py_DECREF(f); | |
| return 1; | |
| } | |
| /* Add a wide-character string constant to the dict */ | |
| static int | |
| wchar_to_dict(PyObject *dict, const char *key, const wchar_t *s) | |
| { | |
| PyObject *u; | |
| int r; | |
| if (s && s[0]) { | |
| u = PyUnicode_FromWideChar(s, -1); | |
| if (!u) { | |
| return 0; | |
| } | |
| } else { | |
| u = Py_NewRef(Py_None); | |
| } | |
| r = PyDict_SetItemString(dict, key, u) == 0; | |
| Py_DECREF(u); | |
| return r; | |
| } | |
| /* Add a narrow string constant to the dict, using default locale decoding */ | |
| static int | |
| decode_to_dict(PyObject *dict, const char *key, const char *s) | |
| { | |
| PyObject *u = NULL; | |
| int r; | |
| if (s && s[0]) { | |
| size_t len; | |
| const wchar_t *w = Py_DecodeLocale(s, &len); | |
| if (w) { | |
| u = PyUnicode_FromWideChar(w, len); | |
| PyMem_RawFree((void *)w); | |
| } | |
| if (!u) { | |
| return 0; | |
| } | |
| } else { | |
| u = Py_NewRef(Py_None); | |
| } | |
| r = PyDict_SetItemString(dict, key, u) == 0; | |
| Py_DECREF(u); | |
| return r; | |
| } | |
| /* Add an environment variable to the dict, optionally clearing it afterwards */ | |
| static int | |
| env_to_dict(PyObject *dict, const char *key, int and_clear) | |
| { | |
| PyObject *u = NULL; | |
| int r = 0; | |
| assert(strncmp(key, "ENV_", 4) == 0); | |
| assert(strlen(key) < 64); | |
| #ifdef MS_WINDOWS | |
| wchar_t wkey[64]; | |
| // Quick convert to wchar_t, since we know key is ASCII | |
| wchar_t *wp = wkey; | |
| for (const char *p = &key[4]; *p; ++p) { | |
| assert(!(*p & 0x80)); | |
| *wp++ = *p; | |
| } | |
| *wp = L'\0'; | |
| const wchar_t *v = _wgetenv(wkey); | |
| if (v) { | |
| u = PyUnicode_FromWideChar(v, -1); | |
| if (!u) { | |
| PyErr_Clear(); | |
| } | |
| } | |
| #else | |
| const char *v = getenv(&key[4]); | |
| if (v) { | |
| size_t len; | |
| const wchar_t *w = Py_DecodeLocale(v, &len); | |
| if (w) { | |
| u = PyUnicode_FromWideChar(w, len); | |
| if (!u) { | |
| PyErr_Clear(); | |
| } | |
| PyMem_RawFree((void *)w); | |
| } | |
| } | |
| #endif | |
| if (u) { | |
| r = PyDict_SetItemString(dict, key, u) == 0; | |
| Py_DECREF(u); | |
| } else { | |
| r = PyDict_SetItemString(dict, key, Py_None) == 0; | |
| } | |
| if (r && and_clear) { | |
| #ifdef MS_WINDOWS | |
| _wputenv_s(wkey, L""); | |
| #else | |
| unsetenv(&key[4]); | |
| #endif | |
| } | |
| return r; | |
| } | |
| /* Add an integer constant to the dict */ | |
| static int | |
| int_to_dict(PyObject *dict, const char *key, int v) | |
| { | |
| PyObject *o; | |
| int r; | |
| o = PyLong_FromLong(v); | |
| if (!o) { | |
| return 0; | |
| } | |
| r = PyDict_SetItemString(dict, key, o) == 0; | |
| Py_DECREF(o); | |
| return r; | |
| } | |
| #ifdef MS_WINDOWS | |
| static int | |
| winmodule_to_dict(PyObject *dict, const char *key, HMODULE mod) | |
| { | |
| wchar_t *buffer = NULL; | |
| for (DWORD cch = 256; buffer == NULL && cch < (1024 * 1024); cch *= 2) { | |
| buffer = (wchar_t*)PyMem_RawMalloc(cch * sizeof(wchar_t)); | |
| if (buffer) { | |
| if (GetModuleFileNameW(mod, buffer, cch) == cch) { | |
| PyMem_RawFree(buffer); | |
| buffer = NULL; | |
| } | |
| } | |
| } | |
| int r = wchar_to_dict(dict, key, buffer); | |
| PyMem_RawFree(buffer); | |
| return r; | |
| } | |
| #endif | |
| /* Add the current executable's path to the dict */ | |
| static int | |
| progname_to_dict(PyObject *dict, const char *key) | |
| { | |
| #ifdef MS_WINDOWS | |
| return winmodule_to_dict(dict, key, NULL); | |
| #elif defined(__APPLE__) | |
| char *path; | |
| uint32_t pathLen = 256; | |
| while (pathLen) { | |
| path = PyMem_RawMalloc((pathLen + 1) * sizeof(char)); | |
| if (!path) { | |
| return 0; | |
| } | |
| if (_NSGetExecutablePath(path, &pathLen) != 0) { | |
| PyMem_RawFree(path); | |
| continue; | |
| } | |
| // Only keep if the path is absolute | |
| if (path[0] == SEP) { | |
| int r = decode_to_dict(dict, key, path); | |
| PyMem_RawFree(path); | |
| return r; | |
| } | |
| // Fall back and store None | |
| PyMem_RawFree(path); | |
| break; | |
| } | |
| #endif | |
| return PyDict_SetItemString(dict, key, Py_None) == 0; | |
| } | |
| /* Add the runtime library's path to the dict */ | |
| static int | |
| library_to_dict(PyObject *dict, const char *key) | |
| { | |
| /* macOS framework builds do not link against a libpython dynamic library, but | |
| instead link against a macOS Framework. */ | |
| #if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK) | |
| #ifdef MS_WINDOWS | |
| extern HMODULE PyWin_DLLhModule; | |
| if (PyWin_DLLhModule) { | |
| return winmodule_to_dict(dict, key, PyWin_DLLhModule); | |
| } | |
| #endif | |
| #if HAVE_DLADDR | |
| Dl_info libpython_info; | |
| if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) { | |
| return decode_to_dict(dict, key, libpython_info.dli_fname); | |
| } | |
| #endif | |
| #endif | |
| return PyDict_SetItemString(dict, key, Py_None) == 0; | |
| } | |
| PyObject * | |
| _Py_Get_Getpath_CodeObject(void) | |
| { | |
| return PyMarshal_ReadObjectFromString( | |
| (const char*)_Py_M__getpath, sizeof(_Py_M__getpath)); | |
| } | |
| /* Perform the actual path calculation. | |
| When compute_path_config is 0, this only reads any initialised path | |
| config values into the PyConfig struct. For example, Py_SetHome() or | |
| Py_SetPath(). The only error should be due to failed memory allocation. | |
| When compute_path_config is 1, full path calculation is performed. | |
| The GIL must be held, and there may be filesystem access, side | |
| effects, and potential unraisable errors that are reported directly | |
| to stderr. | |
| Calling this function multiple times on the same PyConfig is only | |
| safe because already-configured values are not recalculated. To | |
| actually recalculate paths, you need a clean PyConfig. | |
| */ | |
| PyStatus | |
| _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config) | |
| { | |
| PyStatus status = _PyPathConfig_ReadGlobal(config); | |
| if (_PyStatus_EXCEPTION(status) || !compute_path_config) { | |
| return status; | |
| } | |
| if (!_PyThreadState_GET()) { | |
| return PyStatus_Error("cannot calculate path configuration without GIL"); | |
| } | |
| PyObject *configDict = _PyConfig_AsDict(config); | |
| if (!configDict) { | |
| PyErr_Clear(); | |
| return PyStatus_NoMemory(); | |
| } | |
| PyObject *dict = PyDict_New(); | |
| if (!dict) { | |
| PyErr_Clear(); | |
| Py_DECREF(configDict); | |
| return PyStatus_NoMemory(); | |
| } | |
| if (PyDict_SetItemString(dict, "config", configDict) < 0) { | |
| PyErr_Clear(); | |
| Py_DECREF(configDict); | |
| Py_DECREF(dict); | |
| return PyStatus_NoMemory(); | |
| } | |
| /* reference now held by dict */ | |
| Py_DECREF(configDict); | |
| PyObject *co = _Py_Get_Getpath_CodeObject(); | |
| if (!co || !PyCode_Check(co)) { | |
| PyErr_Clear(); | |
| Py_XDECREF(co); | |
| Py_DECREF(dict); | |
| return PyStatus_Error("error reading frozen getpath.py"); | |
| } | |
| #ifdef MS_WINDOWS | |
| PyObject *winreg = PyImport_ImportModule("winreg"); | |
| if (!winreg || PyDict_SetItemString(dict, "winreg", winreg) < 0) { | |
| PyErr_Clear(); | |
| Py_XDECREF(winreg); | |
| if (PyDict_SetItemString(dict, "winreg", Py_None) < 0) { | |
| PyErr_Clear(); | |
| Py_DECREF(co); | |
| Py_DECREF(dict); | |
| return PyStatus_Error("error importing winreg module"); | |
| } | |
| } else { | |
| Py_DECREF(winreg); | |
| } | |
| #endif | |
| if ( | |
| #ifdef MS_WINDOWS | |
| !decode_to_dict(dict, "os_name", "nt") || | |
| #elif defined(__APPLE__) | |
| !decode_to_dict(dict, "os_name", "darwin") || | |
| #else | |
| !decode_to_dict(dict, "os_name", "posix") || | |
| #endif | |
| #ifdef WITH_NEXT_FRAMEWORK | |
| !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 1) || | |
| #else | |
| !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 0) || | |
| #endif | |
| !decode_to_dict(dict, "PREFIX", PREFIX) || | |
| !decode_to_dict(dict, "EXEC_PREFIX", EXEC_PREFIX) || | |
| !decode_to_dict(dict, "PYTHONPATH", PYTHONPATH) || | |
| !decode_to_dict(dict, "VPATH", VPATH) || | |
| !decode_to_dict(dict, "PLATLIBDIR", PLATLIBDIR) || | |
| !decode_to_dict(dict, "PYDEBUGEXT", PYDEBUGEXT) || | |
| !int_to_dict(dict, "VERSION_MAJOR", PY_MAJOR_VERSION) || | |
| !int_to_dict(dict, "VERSION_MINOR", PY_MINOR_VERSION) || | |
| !decode_to_dict(dict, "PYWINVER", PYWINVER) || | |
| !wchar_to_dict(dict, "EXE_SUFFIX", EXE_SUFFIX) || | |
| !env_to_dict(dict, "ENV_PATH", 0) || | |
| !env_to_dict(dict, "ENV_PYTHONHOME", 0) || | |
| !env_to_dict(dict, "ENV_PYTHONEXECUTABLE", 0) || | |
| !env_to_dict(dict, "ENV___PYVENV_LAUNCHER__", 1) || | |
| !progname_to_dict(dict, "real_executable") || | |
| !library_to_dict(dict, "library") || | |
| !wchar_to_dict(dict, "executable_dir", NULL) || | |
| !wchar_to_dict(dict, "py_setpath", _PyPathConfig_GetGlobalModuleSearchPath()) || | |
| !funcs_to_dict(dict, config->pathconfig_warnings) || | |
| #ifdef Py_GIL_DISABLED | |
| !decode_to_dict(dict, "ABI_THREAD", "t") || | |
| #else | |
| !decode_to_dict(dict, "ABI_THREAD", "") || | |
| #endif | |
| #ifndef MS_WINDOWS | |
| PyDict_SetItemString(dict, "winreg", Py_None) < 0 || | |
| #endif | |
| PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0 | |
| ) { | |
| Py_DECREF(co); | |
| Py_DECREF(dict); | |
| PyErr_FormatUnraisable("Exception ignored while preparing getpath"); | |
| return PyStatus_Error("error evaluating initial values"); | |
| } | |
| PyObject *r = PyEval_EvalCode(co, dict, dict); | |
| Py_DECREF(co); | |
| if (!r) { | |
| Py_DECREF(dict); | |
| PyErr_FormatUnraisable("Exception ignored while running getpath"); | |
| return PyStatus_Error("error evaluating path"); | |
| } | |
| Py_DECREF(r); | |
| if (_PyConfig_FromDict(config, configDict) < 0) { | |
| PyErr_FormatUnraisable("Exception ignored while reading getpath results"); | |
| Py_DECREF(dict); | |
| return PyStatus_Error("error getting getpath results"); | |
| } | |
| Py_DECREF(dict); | |
| return _PyStatus_OK(); | |
| } |