xref: /third_party/python/Python/pathconfig.c (revision 7db96d56)
1/* Path configuration like module_search_path (sys.path) */
2
3#include "Python.h"
4#include "marshal.h"              // PyMarshal_ReadObjectFromString
5#include "osdefs.h"               // DELIM
6#include "pycore_initconfig.h"
7#include "pycore_fileutils.h"
8#include "pycore_pathconfig.h"
9#include "pycore_pymem.h"         // _PyMem_SetDefaultAllocator()
10#include <wchar.h>
11#ifdef MS_WINDOWS
12#  include <windows.h>            // GetFullPathNameW(), MAX_PATH
13#  include <pathcch.h>
14#  include <shlwapi.h>
15#endif
16
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21
22/* External interface */
23
24/* Stored values set by C API functions */
25typedef struct _PyPathConfig {
26    /* Full path to the Python program */
27    wchar_t *program_full_path;
28    wchar_t *prefix;
29    wchar_t *exec_prefix;
30    wchar_t *stdlib_dir;
31    /* Set by Py_SetPath */
32    wchar_t *module_search_path;
33    /* Set by _PyPathConfig_UpdateGlobal */
34    wchar_t *calculated_module_search_path;
35    /* Python program name */
36    wchar_t *program_name;
37    /* Set by Py_SetPythonHome() or PYTHONHOME environment variable */
38    wchar_t *home;
39    int _is_python_build;
40} _PyPathConfig;
41
42#  define _PyPathConfig_INIT \
43      {.module_search_path = NULL, ._is_python_build = 0}
44
45
46_PyPathConfig _Py_path_config = _PyPathConfig_INIT;
47
48
49const wchar_t *
50_PyPathConfig_GetGlobalModuleSearchPath(void)
51{
52    return _Py_path_config.module_search_path;
53}
54
55
56void
57_PyPathConfig_ClearGlobal(void)
58{
59    PyMemAllocatorEx old_alloc;
60    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
61
62#define CLEAR(ATTR) \
63    do { \
64        PyMem_RawFree(_Py_path_config.ATTR); \
65        _Py_path_config.ATTR = NULL; \
66    } while (0)
67
68    CLEAR(program_full_path);
69    CLEAR(prefix);
70    CLEAR(exec_prefix);
71    CLEAR(stdlib_dir);
72    CLEAR(module_search_path);
73    CLEAR(calculated_module_search_path);
74    CLEAR(program_name);
75    CLEAR(home);
76    _Py_path_config._is_python_build = 0;
77
78#undef CLEAR
79
80    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
81}
82
83PyStatus
84_PyPathConfig_ReadGlobal(PyConfig *config)
85{
86    PyStatus status = _PyStatus_OK();
87
88#define COPY(ATTR) \
89    do { \
90        if (_Py_path_config.ATTR && !config->ATTR) { \
91            status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.ATTR); \
92            if (_PyStatus_EXCEPTION(status)) goto done; \
93        } \
94    } while (0)
95
96#define COPY2(ATTR, SRCATTR) \
97    do { \
98        if (_Py_path_config.SRCATTR && !config->ATTR) { \
99            status = PyConfig_SetString(config, &config->ATTR, _Py_path_config.SRCATTR); \
100            if (_PyStatus_EXCEPTION(status)) goto done; \
101        } \
102    } while (0)
103
104#define COPY_INT(ATTR) \
105    do { \
106        assert(_Py_path_config.ATTR >= 0); \
107        if ((_Py_path_config.ATTR >= 0) && (config->ATTR <= 0)) { \
108            config->ATTR = _Py_path_config.ATTR; \
109        } \
110    } while (0)
111
112    COPY(prefix);
113    COPY(exec_prefix);
114    COPY(stdlib_dir);
115    COPY(program_name);
116    COPY(home);
117    COPY2(executable, program_full_path);
118    COPY_INT(_is_python_build);
119    // module_search_path must be initialised - not read
120#undef COPY
121#undef COPY2
122#undef COPY_INT
123
124done:
125    return status;
126}
127
128PyStatus
129_PyPathConfig_UpdateGlobal(const PyConfig *config)
130{
131    PyMemAllocatorEx old_alloc;
132    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
133
134#define COPY(ATTR) \
135    do { \
136        if (config->ATTR) { \
137            PyMem_RawFree(_Py_path_config.ATTR); \
138            _Py_path_config.ATTR = _PyMem_RawWcsdup(config->ATTR); \
139            if (!_Py_path_config.ATTR) goto error; \
140        } \
141    } while (0)
142
143#define COPY2(ATTR, SRCATTR) \
144    do { \
145        if (config->SRCATTR) { \
146            PyMem_RawFree(_Py_path_config.ATTR); \
147            _Py_path_config.ATTR = _PyMem_RawWcsdup(config->SRCATTR); \
148            if (!_Py_path_config.ATTR) goto error; \
149        } \
150    } while (0)
151
152#define COPY_INT(ATTR) \
153    do { \
154        if (config->ATTR > 0) { \
155            _Py_path_config.ATTR = config->ATTR; \
156        } \
157    } while (0)
158
159    COPY(prefix);
160    COPY(exec_prefix);
161    COPY(stdlib_dir);
162    COPY(program_name);
163    COPY(home);
164    COPY2(program_full_path, executable);
165    COPY_INT(_is_python_build);
166#undef COPY
167#undef COPY2
168#undef COPY_INT
169
170    PyMem_RawFree(_Py_path_config.module_search_path);
171    _Py_path_config.module_search_path = NULL;
172    PyMem_RawFree(_Py_path_config.calculated_module_search_path);
173    _Py_path_config.calculated_module_search_path = NULL;
174
175    do {
176        size_t cch = 1;
177        for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) {
178            cch += 1 + wcslen(config->module_search_paths.items[i]);
179        }
180
181        wchar_t *path = (wchar_t*)PyMem_RawMalloc(sizeof(wchar_t) * cch);
182        if (!path) {
183            goto error;
184        }
185        wchar_t *p = path;
186        for (Py_ssize_t i = 0; i < config->module_search_paths.length; ++i) {
187            wcscpy(p, config->module_search_paths.items[i]);
188            p = wcschr(p, L'\0');
189            *p++ = DELIM;
190            *p = L'\0';
191        }
192
193        do {
194            *p = L'\0';
195        } while (p != path && *--p == DELIM);
196        _Py_path_config.calculated_module_search_path = path;
197    } while (0);
198
199    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
200    return _PyStatus_OK();
201
202error:
203    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
204    return _PyStatus_NO_MEMORY();
205}
206
207
208static void _Py_NO_RETURN
209path_out_of_memory(const char *func)
210{
211    _Py_FatalErrorFunc(func, "out of memory");
212}
213
214void
215Py_SetPath(const wchar_t *path)
216{
217    if (path == NULL) {
218        _PyPathConfig_ClearGlobal();
219        return;
220    }
221
222    PyMemAllocatorEx old_alloc;
223    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
224
225    PyMem_RawFree(_Py_path_config.prefix);
226    PyMem_RawFree(_Py_path_config.exec_prefix);
227    PyMem_RawFree(_Py_path_config.stdlib_dir);
228    PyMem_RawFree(_Py_path_config.module_search_path);
229    PyMem_RawFree(_Py_path_config.calculated_module_search_path);
230
231    _Py_path_config.prefix = _PyMem_RawWcsdup(L"");
232    _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
233    // XXX Copy this from the new module_search_path?
234    if (_Py_path_config.home != NULL) {
235        _Py_path_config.stdlib_dir = _PyMem_RawWcsdup(_Py_path_config.home);
236    }
237    else {
238        _Py_path_config.stdlib_dir = _PyMem_RawWcsdup(L"");
239    }
240    _Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
241    _Py_path_config.calculated_module_search_path = NULL;
242
243    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
244
245    if (_Py_path_config.prefix == NULL
246        || _Py_path_config.exec_prefix == NULL
247        || _Py_path_config.stdlib_dir == NULL
248        || _Py_path_config.module_search_path == NULL)
249    {
250        path_out_of_memory(__func__);
251    }
252}
253
254
255void
256Py_SetPythonHome(const wchar_t *home)
257{
258    int has_value = home && home[0];
259
260    PyMemAllocatorEx old_alloc;
261    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
262
263    PyMem_RawFree(_Py_path_config.home);
264    _Py_path_config.home = NULL;
265
266    if (has_value) {
267        _Py_path_config.home = _PyMem_RawWcsdup(home);
268    }
269
270    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
271
272    if (has_value && _Py_path_config.home == NULL) {
273        path_out_of_memory(__func__);
274    }
275}
276
277
278void
279Py_SetProgramName(const wchar_t *program_name)
280{
281    int has_value = program_name && program_name[0];
282
283    PyMemAllocatorEx old_alloc;
284    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
285
286    PyMem_RawFree(_Py_path_config.program_name);
287    _Py_path_config.program_name = NULL;
288
289    if (has_value) {
290        _Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
291    }
292
293    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
294
295    if (has_value && _Py_path_config.program_name == NULL) {
296        path_out_of_memory(__func__);
297    }
298}
299
300void
301_Py_SetProgramFullPath(const wchar_t *program_full_path)
302{
303    int has_value = program_full_path && program_full_path[0];
304
305    PyMemAllocatorEx old_alloc;
306    _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
307
308    PyMem_RawFree(_Py_path_config.program_full_path);
309    _Py_path_config.program_full_path = NULL;
310
311    if (has_value) {
312        _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
313    }
314
315    PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
316
317    if (has_value && _Py_path_config.program_full_path == NULL) {
318        path_out_of_memory(__func__);
319    }
320}
321
322
323wchar_t *
324Py_GetPath(void)
325{
326    /* If the user has provided a path, return that */
327    if (_Py_path_config.module_search_path) {
328        return _Py_path_config.module_search_path;
329    }
330    /* If we have already done calculations, return the calculated path */
331    return _Py_path_config.calculated_module_search_path;
332}
333
334
335wchar_t *
336_Py_GetStdlibDir(void)
337{
338    wchar_t *stdlib_dir = _Py_path_config.stdlib_dir;
339    if (stdlib_dir != NULL && stdlib_dir[0] != L'\0') {
340        return stdlib_dir;
341    }
342    return NULL;
343}
344
345
346wchar_t *
347Py_GetPrefix(void)
348{
349    return _Py_path_config.prefix;
350}
351
352
353wchar_t *
354Py_GetExecPrefix(void)
355{
356    return _Py_path_config.exec_prefix;
357}
358
359
360wchar_t *
361Py_GetProgramFullPath(void)
362{
363    return _Py_path_config.program_full_path;
364}
365
366
367wchar_t*
368Py_GetPythonHome(void)
369{
370    return _Py_path_config.home;
371}
372
373
374wchar_t *
375Py_GetProgramName(void)
376{
377    return _Py_path_config.program_name;
378}
379
380
381
382/* Compute module search path from argv[0] or the current working
383   directory ("-m module" case) which will be prepended to sys.argv:
384   sys.path[0].
385
386   Return 1 if the path is correctly resolved and written into *path0_p.
387
388   Return 0 if it fails to resolve the full path. For example, return 0 if the
389   current working directory has been removed (bpo-36236) or if argv is empty.
390
391   Raise an exception and return -1 on error.
392   */
393int
394_PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p)
395{
396    assert(_PyWideStringList_CheckConsistency(argv));
397
398    if (argv->length == 0) {
399        /* Leave sys.path unchanged if sys.argv is empty */
400        return 0;
401    }
402
403    wchar_t *argv0 = argv->items[0];
404    int have_module_arg = (wcscmp(argv0, L"-m") == 0);
405    int have_script_arg = (!have_module_arg && (wcscmp(argv0, L"-c") != 0));
406
407    wchar_t *path0 = argv0;
408    Py_ssize_t n = 0;
409
410#ifdef HAVE_REALPATH
411    wchar_t fullpath[MAXPATHLEN];
412#elif defined(MS_WINDOWS)
413    wchar_t fullpath[MAX_PATH];
414#endif
415
416    if (have_module_arg) {
417#if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
418        if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) {
419            return 0;
420        }
421        path0 = fullpath;
422#else
423        path0 = L".";
424#endif
425        n = wcslen(path0);
426    }
427
428#ifdef HAVE_READLINK
429    wchar_t link[MAXPATHLEN + 1];
430    int nr = 0;
431    wchar_t path0copy[2 * MAXPATHLEN + 1];
432
433    if (have_script_arg) {
434        nr = _Py_wreadlink(path0, link, Py_ARRAY_LENGTH(link));
435    }
436    if (nr > 0) {
437        /* It's a symlink */
438        link[nr] = '\0';
439        if (link[0] == SEP) {
440            path0 = link; /* Link to absolute path */
441        }
442        else if (wcschr(link, SEP) == NULL) {
443            /* Link without path */
444        }
445        else {
446            /* Must join(dirname(path0), link) */
447            wchar_t *q = wcsrchr(path0, SEP);
448            if (q == NULL) {
449                /* path0 without path */
450                path0 = link;
451            }
452            else {
453                /* Must make a copy, path0copy has room for 2 * MAXPATHLEN */
454                wcsncpy(path0copy, path0, MAXPATHLEN);
455                q = wcsrchr(path0copy, SEP);
456                wcsncpy(q+1, link, MAXPATHLEN);
457                q[MAXPATHLEN + 1] = L'\0';
458                path0 = path0copy;
459            }
460        }
461    }
462#endif /* HAVE_READLINK */
463
464    wchar_t *p = NULL;
465
466#if SEP == '\\'
467    /* Special case for Microsoft filename syntax */
468    if (have_script_arg) {
469        wchar_t *q;
470#if defined(MS_WINDOWS)
471        /* Replace the first element in argv with the full path. */
472        wchar_t *ptemp;
473        if (GetFullPathNameW(path0,
474                           Py_ARRAY_LENGTH(fullpath),
475                           fullpath,
476                           &ptemp)) {
477            path0 = fullpath;
478        }
479#endif
480        p = wcsrchr(path0, SEP);
481        /* Test for alternate separator */
482        q = wcsrchr(p ? p : path0, '/');
483        if (q != NULL)
484            p = q;
485        if (p != NULL) {
486            n = p + 1 - path0;
487            if (n > 1 && p[-1] != ':')
488                n--; /* Drop trailing separator */
489        }
490    }
491#else
492    /* All other filename syntaxes */
493    if (have_script_arg) {
494#if defined(HAVE_REALPATH)
495        if (_Py_wrealpath(path0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
496            path0 = fullpath;
497        }
498#endif
499        p = wcsrchr(path0, SEP);
500    }
501    if (p != NULL) {
502        n = p + 1 - path0;
503#if SEP == '/' /* Special case for Unix filename syntax */
504        if (n > 1) {
505            /* Drop trailing separator */
506            n--;
507        }
508#endif /* Unix */
509    }
510#endif /* All others */
511
512    PyObject *path0_obj = PyUnicode_FromWideChar(path0, n);
513    if (path0_obj == NULL) {
514        return -1;
515    }
516
517    *path0_p = path0_obj;
518    return 1;
519}
520
521
522#ifdef __cplusplus
523}
524#endif
525