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