/src/Python-3.8.3/Modules/getpath.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Return the initial module search path. */ |
2 | | |
3 | | #include "Python.h" |
4 | | #include "pycore_initconfig.h" |
5 | | #include "osdefs.h" |
6 | | #include "pycore_fileutils.h" |
7 | | #include "pycore_pathconfig.h" |
8 | | #include "pycore_pystate.h" |
9 | | |
10 | | #include <sys/types.h> |
11 | | #include <string.h> |
12 | | |
13 | | #ifdef __APPLE__ |
14 | | # include <mach-o/dyld.h> |
15 | | #endif |
16 | | |
17 | | /* Search in some common locations for the associated Python libraries. |
18 | | * |
19 | | * Two directories must be found, the platform independent directory |
20 | | * (prefix), containing the common .py and .pyc files, and the platform |
21 | | * dependent directory (exec_prefix), containing the shared library |
22 | | * modules. Note that prefix and exec_prefix can be the same directory, |
23 | | * but for some installations, they are different. |
24 | | * |
25 | | * Py_GetPath() carries out separate searches for prefix and exec_prefix. |
26 | | * Each search tries a number of different locations until a ``landmark'' |
27 | | * file or directory is found. If no prefix or exec_prefix is found, a |
28 | | * warning message is issued and the preprocessor defined PREFIX and |
29 | | * EXEC_PREFIX are used (even though they will not work); python carries on |
30 | | * as best as is possible, but most imports will fail. |
31 | | * |
32 | | * Before any searches are done, the location of the executable is |
33 | | * determined. If argv[0] has one or more slashes in it, it is used |
34 | | * unchanged. Otherwise, it must have been invoked from the shell's path, |
35 | | * so we search $PATH for the named executable and use that. If the |
36 | | * executable was not found on $PATH (or there was no $PATH environment |
37 | | * variable), the original argv[0] string is used. |
38 | | * |
39 | | * Next, the executable location is examined to see if it is a symbolic |
40 | | * link. If so, the link is chased (correctly interpreting a relative |
41 | | * pathname if one is found) and the directory of the link target is used. |
42 | | * |
43 | | * Finally, argv0_path is set to the directory containing the executable |
44 | | * (i.e. the last component is stripped). |
45 | | * |
46 | | * With argv0_path in hand, we perform a number of steps. The same steps |
47 | | * are performed for prefix and for exec_prefix, but with a different |
48 | | * landmark. |
49 | | * |
50 | | * Step 1. Are we running python out of the build directory? This is |
51 | | * checked by looking for a different kind of landmark relative to |
52 | | * argv0_path. For prefix, the landmark's path is derived from the VPATH |
53 | | * preprocessor variable (taking into account that its value is almost, but |
54 | | * not quite, what we need). For exec_prefix, the landmark is |
55 | | * pybuilddir.txt. If the landmark is found, we're done. |
56 | | * |
57 | | * For the remaining steps, the prefix landmark will always be |
58 | | * lib/python$VERSION/os.py and the exec_prefix will always be |
59 | | * lib/python$VERSION/lib-dynload, where $VERSION is Python's version |
60 | | * number as supplied by the Makefile. Note that this means that no more |
61 | | * build directory checking is performed; if the first step did not find |
62 | | * the landmarks, the assumption is that python is running from an |
63 | | * installed setup. |
64 | | * |
65 | | * Step 2. See if the $PYTHONHOME environment variable points to the |
66 | | * installed location of the Python libraries. If $PYTHONHOME is set, then |
67 | | * it points to prefix and exec_prefix. $PYTHONHOME can be a single |
68 | | * directory, which is used for both, or the prefix and exec_prefix |
69 | | * directories separated by a colon. |
70 | | * |
71 | | * Step 3. Try to find prefix and exec_prefix relative to argv0_path, |
72 | | * backtracking up the path until it is exhausted. This is the most common |
73 | | * step to succeed. Note that if prefix and exec_prefix are different, |
74 | | * exec_prefix is more likely to be found; however if exec_prefix is a |
75 | | * subdirectory of prefix, both will be found. |
76 | | * |
77 | | * Step 4. Search the directories pointed to by the preprocessor variables |
78 | | * PREFIX and EXEC_PREFIX. These are supplied by the Makefile but can be |
79 | | * passed in as options to the configure script. |
80 | | * |
81 | | * That's it! |
82 | | * |
83 | | * Well, almost. Once we have determined prefix and exec_prefix, the |
84 | | * preprocessor variable PYTHONPATH is used to construct a path. Each |
85 | | * relative path on PYTHONPATH is prefixed with prefix. Then the directory |
86 | | * containing the shared library modules is appended. The environment |
87 | | * variable $PYTHONPATH is inserted in front of it all. Finally, the |
88 | | * prefix and exec_prefix globals are tweaked so they reflect the values |
89 | | * expected by other code, by stripping the "lib/python$VERSION/..." stuff |
90 | | * off. If either points to the build directory, the globals are reset to |
91 | | * the corresponding preprocessor variables (so sys.prefix will reflect the |
92 | | * installation location, even though sys.path points into the build |
93 | | * directory). This seems to make more sense given that currently the only |
94 | | * known use of sys.prefix and sys.exec_prefix is for the ILU installation |
95 | | * process to find the installed Python tree. |
96 | | * |
97 | | * An embedding application can use Py_SetPath() to override all of |
98 | | * these authomatic path computations. |
99 | | * |
100 | | * NOTE: Windows MSVC builds use PC/getpathp.c instead! |
101 | | */ |
102 | | |
103 | | #ifdef __cplusplus |
104 | | extern "C" { |
105 | | #endif |
106 | | |
107 | | |
108 | | #if !defined(PREFIX) || !defined(EXEC_PREFIX) || !defined(VERSION) || !defined(VPATH) |
109 | | #error "PREFIX, EXEC_PREFIX, VERSION, and VPATH must be constant defined" |
110 | | #endif |
111 | | |
112 | | #ifndef LANDMARK |
113 | 0 | #define LANDMARK L"os.py" |
114 | | #endif |
115 | | |
116 | | #define DECODE_LOCALE_ERR(NAME, LEN) \ |
117 | 0 | ((LEN) == (size_t)-2) \ |
118 | 0 | ? _PyStatus_ERR("cannot decode " NAME) \ |
119 | 0 | : _PyStatus_NO_MEMORY() |
120 | | |
121 | 0 | #define PATHLEN_ERR() _PyStatus_ERR("path configuration: path too long") |
122 | | |
123 | | typedef struct { |
124 | | wchar_t *path_env; /* PATH environment variable */ |
125 | | |
126 | | wchar_t *pythonpath; /* PYTHONPATH macro */ |
127 | | wchar_t *prefix; /* PREFIX macro */ |
128 | | wchar_t *exec_prefix; /* EXEC_PREFIX macro */ |
129 | | |
130 | | wchar_t *lib_python; /* "lib/pythonX.Y" */ |
131 | | |
132 | | int prefix_found; /* found platform independent libraries? */ |
133 | | int exec_prefix_found; /* found the platform dependent libraries? */ |
134 | | |
135 | | int warnings; |
136 | | const wchar_t *pythonpath_env; |
137 | | } PyCalculatePath; |
138 | | |
139 | | static const wchar_t delimiter[2] = {DELIM, '\0'}; |
140 | | static const wchar_t separator[2] = {SEP, '\0'}; |
141 | | |
142 | | |
143 | | /* Get file status. Encode the path to the locale encoding. */ |
144 | | static int |
145 | | _Py_wstat(const wchar_t* path, struct stat *buf) |
146 | 28 | { |
147 | 28 | int err; |
148 | 28 | char *fname; |
149 | 28 | fname = _Py_EncodeLocaleRaw(path, NULL); |
150 | 28 | if (fname == NULL) { |
151 | 0 | errno = EINVAL; |
152 | 0 | return -1; |
153 | 0 | } |
154 | 28 | err = stat(fname, buf); |
155 | 28 | PyMem_RawFree(fname); |
156 | 28 | return err; |
157 | 28 | } |
158 | | |
159 | | |
160 | | static void |
161 | | reduce(wchar_t *dir) |
162 | 140 | { |
163 | 140 | size_t i = wcslen(dir); |
164 | 1.26k | while (i > 0 && dir[i] != SEP) { |
165 | 1.12k | --i; |
166 | 1.12k | } |
167 | 140 | dir[i] = '\0'; |
168 | 140 | } |
169 | | |
170 | | |
171 | | /* Is file, not directory */ |
172 | | static int |
173 | | isfile(const wchar_t *filename) |
174 | 0 | { |
175 | 0 | struct stat buf; |
176 | 0 | if (_Py_wstat(filename, &buf) != 0) { |
177 | 0 | return 0; |
178 | 0 | } |
179 | 0 | if (!S_ISREG(buf.st_mode)) { |
180 | 0 | return 0; |
181 | 0 | } |
182 | 0 | return 1; |
183 | 0 | } |
184 | | |
185 | | |
186 | | /* Is module -- check for .pyc too */ |
187 | | static int |
188 | | ismodule(wchar_t *filename, size_t filename_len) |
189 | 0 | { |
190 | 0 | if (isfile(filename)) { |
191 | 0 | return 1; |
192 | 0 | } |
193 | | |
194 | | /* Check for the compiled version of prefix. */ |
195 | 0 | if (wcslen(filename) + 2 <= filename_len) { |
196 | 0 | wcscat(filename, L"c"); |
197 | 0 | if (isfile(filename)) { |
198 | 0 | return 1; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | return 0; |
202 | 0 | } |
203 | | |
204 | | |
205 | | /* Is executable file */ |
206 | | static int |
207 | | isxfile(const wchar_t *filename) |
208 | 28 | { |
209 | 28 | struct stat buf; |
210 | 28 | if (_Py_wstat(filename, &buf) != 0) { |
211 | 14 | return 0; |
212 | 14 | } |
213 | 14 | if (!S_ISREG(buf.st_mode)) { |
214 | 0 | return 0; |
215 | 0 | } |
216 | 14 | if ((buf.st_mode & 0111) == 0) { |
217 | 0 | return 0; |
218 | 0 | } |
219 | 14 | return 1; |
220 | 14 | } |
221 | | |
222 | | |
223 | | /* Is directory */ |
224 | | static int |
225 | | isdir(wchar_t *filename) |
226 | 0 | { |
227 | 0 | struct stat buf; |
228 | 0 | if (_Py_wstat(filename, &buf) != 0) { |
229 | 0 | return 0; |
230 | 0 | } |
231 | 0 | if (!S_ISDIR(buf.st_mode)) { |
232 | 0 | return 0; |
233 | 0 | } |
234 | 0 | return 1; |
235 | 0 | } |
236 | | |
237 | | |
238 | | /* Add a path component, by appending stuff to buffer. |
239 | | buflen: 'buffer' length in characters including trailing NUL. */ |
240 | | static PyStatus |
241 | | joinpath(wchar_t *buffer, const wchar_t *stuff, size_t buflen) |
242 | 112 | { |
243 | 112 | size_t n, k; |
244 | 112 | if (stuff[0] != SEP) { |
245 | 112 | n = wcslen(buffer); |
246 | 112 | if (n >= buflen) { |
247 | 0 | return PATHLEN_ERR(); |
248 | 0 | } |
249 | | |
250 | 112 | if (n > 0 && buffer[n-1] != SEP) { |
251 | 112 | buffer[n++] = SEP; |
252 | 112 | } |
253 | 112 | } |
254 | 0 | else { |
255 | 0 | n = 0; |
256 | 0 | } |
257 | | |
258 | 112 | k = wcslen(stuff); |
259 | 112 | if (n + k >= buflen) { |
260 | 0 | return PATHLEN_ERR(); |
261 | 0 | } |
262 | 112 | wcsncpy(buffer+n, stuff, k); |
263 | 112 | buffer[n+k] = '\0'; |
264 | | |
265 | 112 | return _PyStatus_OK(); |
266 | 112 | } |
267 | | |
268 | | |
269 | | static inline int |
270 | | safe_wcscpy(wchar_t *dst, const wchar_t *src, size_t n) |
271 | 84 | { |
272 | 84 | size_t srclen = wcslen(src); |
273 | 84 | if (n <= srclen) { |
274 | 0 | dst[0] = L'\0'; |
275 | 0 | return -1; |
276 | 0 | } |
277 | 84 | memcpy(dst, src, (srclen + 1) * sizeof(wchar_t)); |
278 | 84 | return 0; |
279 | 84 | } |
280 | | |
281 | | |
282 | | /* copy_absolute requires that path be allocated at least |
283 | | 'pathlen' characters (including trailing NUL). */ |
284 | | static PyStatus |
285 | | copy_absolute(wchar_t *path, const wchar_t *p, size_t pathlen) |
286 | 0 | { |
287 | 0 | if (p[0] == SEP) { |
288 | 0 | if (safe_wcscpy(path, p, pathlen) < 0) { |
289 | 0 | return PATHLEN_ERR(); |
290 | 0 | } |
291 | 0 | } |
292 | 0 | else { |
293 | 0 | if (!_Py_wgetcwd(path, pathlen)) { |
294 | | /* unable to get the current directory */ |
295 | 0 | if (safe_wcscpy(path, p, pathlen) < 0) { |
296 | 0 | return PATHLEN_ERR(); |
297 | 0 | } |
298 | 0 | return _PyStatus_OK(); |
299 | 0 | } |
300 | 0 | if (p[0] == '.' && p[1] == SEP) { |
301 | 0 | p += 2; |
302 | 0 | } |
303 | 0 | PyStatus status = joinpath(path, p, pathlen); |
304 | 0 | if (_PyStatus_EXCEPTION(status)) { |
305 | 0 | return status; |
306 | 0 | } |
307 | 0 | } |
308 | 0 | return _PyStatus_OK(); |
309 | 0 | } |
310 | | |
311 | | |
312 | | /* path_len: path length in characters including trailing NUL */ |
313 | | static PyStatus |
314 | | absolutize(wchar_t *path, size_t path_len) |
315 | 0 | { |
316 | 0 | if (path[0] == SEP) { |
317 | 0 | return _PyStatus_OK(); |
318 | 0 | } |
319 | | |
320 | 0 | wchar_t abs_path[MAXPATHLEN+1]; |
321 | 0 | PyStatus status = copy_absolute(abs_path, path, Py_ARRAY_LENGTH(abs_path)); |
322 | 0 | if (_PyStatus_EXCEPTION(status)) { |
323 | 0 | return status; |
324 | 0 | } |
325 | | |
326 | 0 | if (safe_wcscpy(path, abs_path, path_len) < 0) { |
327 | 0 | return PATHLEN_ERR(); |
328 | 0 | } |
329 | 0 | return _PyStatus_OK(); |
330 | 0 | } |
331 | | |
332 | | |
333 | | #if defined(__CYGWIN__) || defined(__MINGW32__) |
334 | | #ifndef EXE_SUFFIX |
335 | | #define EXE_SUFFIX L".exe" |
336 | | #endif |
337 | | |
338 | | /* pathlen: 'path' length in characters including trailing NUL */ |
339 | | static PyStatus |
340 | | add_exe_suffix(wchar_t *progpath, size_t progpathlen) |
341 | | { |
342 | | /* Check for already have an executable suffix */ |
343 | | size_t n = wcslen(progpath); |
344 | | size_t s = wcslen(EXE_SUFFIX); |
345 | | if (wcsncasecmp(EXE_SUFFIX, progpath + n - s, s) == 0) { |
346 | | return _PyStatus_OK(); |
347 | | } |
348 | | |
349 | | if (n + s >= progpathlen) { |
350 | | return PATHLEN_ERR(); |
351 | | } |
352 | | wcsncpy(progpath + n, EXE_SUFFIX, s); |
353 | | progpath[n+s] = '\0'; |
354 | | |
355 | | if (!isxfile(progpath)) { |
356 | | /* Path that added suffix is invalid: truncate (remove suffix) */ |
357 | | progpath[n] = '\0'; |
358 | | } |
359 | | |
360 | | return _PyStatus_OK(); |
361 | | } |
362 | | #endif |
363 | | |
364 | | |
365 | | /* search_for_prefix requires that argv0_path be no more than MAXPATHLEN |
366 | | bytes long. |
367 | | */ |
368 | | static PyStatus |
369 | | search_for_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
370 | | const wchar_t *argv0_path, |
371 | | wchar_t *prefix, size_t prefix_len, int *found) |
372 | 14 | { |
373 | 14 | wchar_t path[MAXPATHLEN+1]; |
374 | 14 | memset(path, 0, sizeof(path)); |
375 | 14 | size_t path_len = Py_ARRAY_LENGTH(path); |
376 | | |
377 | 14 | PyStatus status; |
378 | | |
379 | | /* If PYTHONHOME is set, we believe it unconditionally */ |
380 | 14 | if (pathconfig->home) { |
381 | | /* Path: <home> / <lib_python> */ |
382 | 14 | if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) { |
383 | 0 | return PATHLEN_ERR(); |
384 | 0 | } |
385 | 14 | wchar_t *delim = wcschr(prefix, DELIM); |
386 | 14 | if (delim) { |
387 | 0 | *delim = L'\0'; |
388 | 0 | } |
389 | 14 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
390 | 14 | if (_PyStatus_EXCEPTION(status)) { |
391 | 0 | return status; |
392 | 0 | } |
393 | 14 | *found = 1; |
394 | 14 | return _PyStatus_OK(); |
395 | 14 | } |
396 | | |
397 | | /* Check to see if argv[0] is in the build directory */ |
398 | 0 | if (safe_wcscpy(path, argv0_path, path_len) < 0) { |
399 | 0 | return PATHLEN_ERR(); |
400 | 0 | } |
401 | 0 | status = joinpath(path, L"Modules/Setup.local", path_len); |
402 | 0 | if (_PyStatus_EXCEPTION(status)) { |
403 | 0 | return status; |
404 | 0 | } |
405 | | |
406 | 0 | if (isfile(path)) { |
407 | | /* Check VPATH to see if argv0_path is in the build directory. |
408 | | VPATH can be empty. */ |
409 | 0 | wchar_t *vpath = Py_DecodeLocale(VPATH, NULL); |
410 | 0 | if (vpath != NULL) { |
411 | | /* Path: <argv0_path> / <vpath> / Lib / LANDMARK */ |
412 | 0 | if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) { |
413 | 0 | return PATHLEN_ERR(); |
414 | 0 | } |
415 | 0 | status = joinpath(prefix, vpath, prefix_len); |
416 | 0 | PyMem_RawFree(vpath); |
417 | 0 | if (_PyStatus_EXCEPTION(status)) { |
418 | 0 | return status; |
419 | 0 | } |
420 | | |
421 | 0 | status = joinpath(prefix, L"Lib", prefix_len); |
422 | 0 | if (_PyStatus_EXCEPTION(status)) { |
423 | 0 | return status; |
424 | 0 | } |
425 | 0 | status = joinpath(prefix, LANDMARK, prefix_len); |
426 | 0 | if (_PyStatus_EXCEPTION(status)) { |
427 | 0 | return status; |
428 | 0 | } |
429 | | |
430 | 0 | if (ismodule(prefix, prefix_len)) { |
431 | 0 | *found = -1; |
432 | 0 | reduce(prefix); |
433 | 0 | return _PyStatus_OK(); |
434 | 0 | } |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | /* Search from argv0_path, until root is found */ |
439 | 0 | status = copy_absolute(prefix, argv0_path, prefix_len); |
440 | 0 | if (_PyStatus_EXCEPTION(status)) { |
441 | 0 | return status; |
442 | 0 | } |
443 | | |
444 | 0 | do { |
445 | | /* Path: <argv0_path or substring> / <lib_python> / LANDMARK */ |
446 | 0 | size_t n = wcslen(prefix); |
447 | 0 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
448 | 0 | if (_PyStatus_EXCEPTION(status)) { |
449 | 0 | return status; |
450 | 0 | } |
451 | 0 | status = joinpath(prefix, LANDMARK, prefix_len); |
452 | 0 | if (_PyStatus_EXCEPTION(status)) { |
453 | 0 | return status; |
454 | 0 | } |
455 | | |
456 | 0 | if (ismodule(prefix, prefix_len)) { |
457 | 0 | *found = 1; |
458 | 0 | reduce(prefix); |
459 | 0 | return _PyStatus_OK(); |
460 | 0 | } |
461 | 0 | prefix[n] = L'\0'; |
462 | 0 | reduce(prefix); |
463 | 0 | } while (prefix[0]); |
464 | | |
465 | | /* Look at configure's PREFIX. |
466 | | Path: <PREFIX macro> / <lib_python> / LANDMARK */ |
467 | 0 | if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) { |
468 | 0 | return PATHLEN_ERR(); |
469 | 0 | } |
470 | 0 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
471 | 0 | if (_PyStatus_EXCEPTION(status)) { |
472 | 0 | return status; |
473 | 0 | } |
474 | 0 | status = joinpath(prefix, LANDMARK, prefix_len); |
475 | 0 | if (_PyStatus_EXCEPTION(status)) { |
476 | 0 | return status; |
477 | 0 | } |
478 | | |
479 | 0 | if (ismodule(prefix, prefix_len)) { |
480 | 0 | *found = 1; |
481 | 0 | reduce(prefix); |
482 | 0 | return _PyStatus_OK(); |
483 | 0 | } |
484 | | |
485 | | /* Fail */ |
486 | 0 | *found = 0; |
487 | 0 | return _PyStatus_OK(); |
488 | 0 | } |
489 | | |
490 | | |
491 | | static PyStatus |
492 | | calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
493 | | const wchar_t *argv0_path, |
494 | | wchar_t *prefix, size_t prefix_len) |
495 | 14 | { |
496 | 14 | PyStatus status; |
497 | | |
498 | 14 | status = search_for_prefix(calculate, pathconfig, argv0_path, |
499 | 14 | prefix, prefix_len, |
500 | 14 | &calculate->prefix_found); |
501 | 14 | if (_PyStatus_EXCEPTION(status)) { |
502 | 0 | return status; |
503 | 0 | } |
504 | | |
505 | 14 | if (!calculate->prefix_found) { |
506 | 0 | if (calculate->warnings) { |
507 | 0 | fprintf(stderr, |
508 | 0 | "Could not find platform independent libraries <prefix>\n"); |
509 | 0 | } |
510 | 0 | if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) { |
511 | 0 | return PATHLEN_ERR(); |
512 | 0 | } |
513 | 0 | status = joinpath(prefix, calculate->lib_python, prefix_len); |
514 | 0 | if (_PyStatus_EXCEPTION(status)) { |
515 | 0 | return status; |
516 | 0 | } |
517 | 0 | } |
518 | 14 | return _PyStatus_OK(); |
519 | 14 | } |
520 | | |
521 | | |
522 | | static PyStatus |
523 | | calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
524 | | wchar_t *prefix) |
525 | 14 | { |
526 | | /* Reduce prefix and exec_prefix to their essence, |
527 | | * e.g. /usr/local/lib/python1.5 is reduced to /usr/local. |
528 | | * If we're loading relative to the build directory, |
529 | | * return the compiled-in defaults instead. |
530 | | */ |
531 | 14 | if (calculate->prefix_found > 0) { |
532 | 14 | reduce(prefix); |
533 | 14 | reduce(prefix); |
534 | | /* The prefix is the root directory, but reduce() chopped |
535 | | * off the "/". */ |
536 | 14 | if (!prefix[0]) { |
537 | 0 | wcscpy(prefix, separator); |
538 | 0 | } |
539 | 14 | pathconfig->prefix = _PyMem_RawWcsdup(prefix); |
540 | 14 | } |
541 | 0 | else { |
542 | 0 | pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix); |
543 | 0 | } |
544 | | |
545 | 14 | if (pathconfig->prefix == NULL) { |
546 | 0 | return _PyStatus_NO_MEMORY(); |
547 | 0 | } |
548 | 14 | return _PyStatus_OK(); |
549 | 14 | } |
550 | | |
551 | | |
552 | | static PyStatus |
553 | | calculate_pybuilddir(const wchar_t *argv0_path, |
554 | | wchar_t *exec_prefix, size_t exec_prefix_len, |
555 | | int *found) |
556 | 0 | { |
557 | 0 | PyStatus status; |
558 | |
|
559 | 0 | wchar_t filename[MAXPATHLEN+1]; |
560 | 0 | memset(filename, 0, sizeof(filename)); |
561 | 0 | size_t filename_len = Py_ARRAY_LENGTH(filename); |
562 | | |
563 | | /* Check to see if argv[0] is in the build directory. "pybuilddir.txt" |
564 | | is written by setup.py and contains the relative path to the location |
565 | | of shared library modules. |
566 | | |
567 | | Filename: <argv0_path> / "pybuilddir.txt" */ |
568 | 0 | if (safe_wcscpy(filename, argv0_path, filename_len) < 0) { |
569 | 0 | return PATHLEN_ERR(); |
570 | 0 | } |
571 | 0 | status = joinpath(filename, L"pybuilddir.txt", filename_len); |
572 | 0 | if (_PyStatus_EXCEPTION(status)) { |
573 | 0 | return status; |
574 | 0 | } |
575 | | |
576 | 0 | if (!isfile(filename)) { |
577 | 0 | return _PyStatus_OK(); |
578 | 0 | } |
579 | | |
580 | 0 | FILE *fp = _Py_wfopen(filename, L"rb"); |
581 | 0 | if (fp == NULL) { |
582 | 0 | errno = 0; |
583 | 0 | return _PyStatus_OK(); |
584 | 0 | } |
585 | | |
586 | 0 | char buf[MAXPATHLEN + 1]; |
587 | 0 | size_t n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, fp); |
588 | 0 | buf[n] = '\0'; |
589 | 0 | fclose(fp); |
590 | |
|
591 | 0 | size_t dec_len; |
592 | 0 | wchar_t *pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len); |
593 | 0 | if (!pybuilddir) { |
594 | 0 | return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len); |
595 | 0 | } |
596 | | |
597 | | /* Path: <argv0_path> / <pybuilddir content> */ |
598 | 0 | if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) { |
599 | 0 | PyMem_RawFree(pybuilddir); |
600 | 0 | return PATHLEN_ERR(); |
601 | 0 | } |
602 | 0 | status = joinpath(exec_prefix, pybuilddir, exec_prefix_len); |
603 | 0 | PyMem_RawFree(pybuilddir); |
604 | 0 | if (_PyStatus_EXCEPTION(status)) { |
605 | 0 | return status; |
606 | 0 | } |
607 | | |
608 | 0 | *found = -1; |
609 | 0 | return _PyStatus_OK(); |
610 | 0 | } |
611 | | |
612 | | |
613 | | /* search_for_exec_prefix requires that argv0_path be no more than |
614 | | MAXPATHLEN bytes long. |
615 | | */ |
616 | | static PyStatus |
617 | | search_for_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
618 | | const wchar_t *argv0_path, |
619 | | wchar_t *exec_prefix, size_t exec_prefix_len, |
620 | | int *found) |
621 | 14 | { |
622 | 14 | PyStatus status; |
623 | | |
624 | | /* If PYTHONHOME is set, we believe it unconditionally */ |
625 | 14 | if (pathconfig->home) { |
626 | | /* Path: <home> / <lib_python> / "lib-dynload" */ |
627 | 14 | wchar_t *delim = wcschr(pathconfig->home, DELIM); |
628 | 14 | if (delim) { |
629 | 0 | if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) { |
630 | 0 | return PATHLEN_ERR(); |
631 | 0 | } |
632 | 0 | } |
633 | 14 | else { |
634 | 14 | if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) { |
635 | 0 | return PATHLEN_ERR(); |
636 | 0 | } |
637 | 14 | } |
638 | 14 | status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len); |
639 | 14 | if (_PyStatus_EXCEPTION(status)) { |
640 | 0 | return status; |
641 | 0 | } |
642 | 14 | status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len); |
643 | 14 | if (_PyStatus_EXCEPTION(status)) { |
644 | 0 | return status; |
645 | 0 | } |
646 | 14 | *found = 1; |
647 | 14 | return _PyStatus_OK(); |
648 | 14 | } |
649 | | |
650 | | /* Check for pybuilddir.txt */ |
651 | 0 | assert(*found == 0); |
652 | 0 | status = calculate_pybuilddir(argv0_path, exec_prefix, exec_prefix_len, |
653 | 0 | found); |
654 | 0 | if (_PyStatus_EXCEPTION(status)) { |
655 | 0 | return status; |
656 | 0 | } |
657 | 0 | if (*found) { |
658 | 0 | return _PyStatus_OK(); |
659 | 0 | } |
660 | | |
661 | | /* Search from argv0_path, until root is found */ |
662 | 0 | status = copy_absolute(exec_prefix, argv0_path, exec_prefix_len); |
663 | 0 | if (_PyStatus_EXCEPTION(status)) { |
664 | 0 | return status; |
665 | 0 | } |
666 | | |
667 | 0 | do { |
668 | | /* Path: <argv0_path or substring> / <lib_python> / "lib-dynload" */ |
669 | 0 | size_t n = wcslen(exec_prefix); |
670 | 0 | status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len); |
671 | 0 | if (_PyStatus_EXCEPTION(status)) { |
672 | 0 | return status; |
673 | 0 | } |
674 | 0 | status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len); |
675 | 0 | if (_PyStatus_EXCEPTION(status)) { |
676 | 0 | return status; |
677 | 0 | } |
678 | 0 | if (isdir(exec_prefix)) { |
679 | 0 | *found = 1; |
680 | 0 | return _PyStatus_OK(); |
681 | 0 | } |
682 | 0 | exec_prefix[n] = L'\0'; |
683 | 0 | reduce(exec_prefix); |
684 | 0 | } while (exec_prefix[0]); |
685 | | |
686 | | /* Look at configure's EXEC_PREFIX. |
687 | | |
688 | | Path: <EXEC_PREFIX macro> / <lib_python> / "lib-dynload" */ |
689 | 0 | if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) { |
690 | 0 | return PATHLEN_ERR(); |
691 | 0 | } |
692 | 0 | status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len); |
693 | 0 | if (_PyStatus_EXCEPTION(status)) { |
694 | 0 | return status; |
695 | 0 | } |
696 | 0 | status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len); |
697 | 0 | if (_PyStatus_EXCEPTION(status)) { |
698 | 0 | return status; |
699 | 0 | } |
700 | 0 | if (isdir(exec_prefix)) { |
701 | 0 | *found = 1; |
702 | 0 | return _PyStatus_OK(); |
703 | 0 | } |
704 | | |
705 | | /* Fail */ |
706 | 0 | *found = 0; |
707 | 0 | return _PyStatus_OK(); |
708 | 0 | } |
709 | | |
710 | | |
711 | | static PyStatus |
712 | | calculate_exec_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig, |
713 | | const wchar_t *argv0_path, |
714 | | wchar_t *exec_prefix, size_t exec_prefix_len) |
715 | 14 | { |
716 | 14 | PyStatus status; |
717 | | |
718 | 14 | status = search_for_exec_prefix(calculate, pathconfig, argv0_path, |
719 | 14 | exec_prefix, exec_prefix_len, |
720 | 14 | &calculate->exec_prefix_found); |
721 | 14 | if (_PyStatus_EXCEPTION(status)) { |
722 | 0 | return status; |
723 | 0 | } |
724 | | |
725 | 14 | if (!calculate->exec_prefix_found) { |
726 | 0 | if (calculate->warnings) { |
727 | 0 | fprintf(stderr, |
728 | 0 | "Could not find platform dependent libraries <exec_prefix>\n"); |
729 | 0 | } |
730 | 0 | if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) { |
731 | 0 | return PATHLEN_ERR(); |
732 | 0 | } |
733 | 0 | status = joinpath(exec_prefix, L"lib/lib-dynload", exec_prefix_len); |
734 | 0 | if (_PyStatus_EXCEPTION(status)) { |
735 | 0 | return status; |
736 | 0 | } |
737 | 0 | } |
738 | | /* If we found EXEC_PREFIX do *not* reduce it! (Yet.) */ |
739 | 14 | return _PyStatus_OK(); |
740 | 14 | } |
741 | | |
742 | | |
743 | | static PyStatus |
744 | | calculate_set_exec_prefix(PyCalculatePath *calculate, |
745 | | _PyPathConfig *pathconfig, |
746 | | wchar_t *exec_prefix) |
747 | 14 | { |
748 | 14 | if (calculate->exec_prefix_found > 0) { |
749 | 14 | reduce(exec_prefix); |
750 | 14 | reduce(exec_prefix); |
751 | 14 | reduce(exec_prefix); |
752 | 14 | if (!exec_prefix[0]) { |
753 | 0 | wcscpy(exec_prefix, separator); |
754 | 0 | } |
755 | | |
756 | 14 | pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix); |
757 | 14 | } |
758 | 0 | else { |
759 | 0 | pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix); |
760 | 0 | } |
761 | | |
762 | 14 | if (pathconfig->exec_prefix == NULL) { |
763 | 0 | return _PyStatus_NO_MEMORY(); |
764 | 0 | } |
765 | | |
766 | 14 | return _PyStatus_OK(); |
767 | 14 | } |
768 | | |
769 | | |
770 | | static PyStatus |
771 | | calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
772 | 14 | { |
773 | 14 | PyStatus status; |
774 | 14 | wchar_t program_full_path[MAXPATHLEN + 1]; |
775 | 14 | const size_t program_full_path_len = Py_ARRAY_LENGTH(program_full_path); |
776 | 14 | memset(program_full_path, 0, sizeof(program_full_path)); |
777 | | |
778 | | #ifdef __APPLE__ |
779 | | char execpath[MAXPATHLEN + 1]; |
780 | | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 |
781 | | uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1; |
782 | | #else |
783 | | unsigned long nsexeclength = Py_ARRAY_LENGTH(execpath) - 1; |
784 | | #endif |
785 | | #endif |
786 | | |
787 | | /* If there is no slash in the argv0 path, then we have to |
788 | | * assume python is on the user's $PATH, since there's no |
789 | | * other way to find a directory to start the search from. If |
790 | | * $PATH isn't exported, you lose. |
791 | | */ |
792 | 14 | if (wcschr(pathconfig->program_name, SEP)) { |
793 | 0 | if (safe_wcscpy(program_full_path, pathconfig->program_name, |
794 | 0 | program_full_path_len) < 0) { |
795 | 0 | return PATHLEN_ERR(); |
796 | 0 | } |
797 | 0 | } |
798 | | #ifdef __APPLE__ |
799 | | /* On Mac OS X, if a script uses an interpreter of the form |
800 | | * "#!/opt/python2.3/bin/python", the kernel only passes "python" |
801 | | * as argv[0], which falls through to the $PATH search below. |
802 | | * If /opt/python2.3/bin isn't in your path, or is near the end, |
803 | | * this algorithm may incorrectly find /usr/bin/python. To work |
804 | | * around this, we can use _NSGetExecutablePath to get a better |
805 | | * hint of what the intended interpreter was, although this |
806 | | * will fail if a relative path was used. but in that case, |
807 | | * absolutize() should help us out below |
808 | | */ |
809 | | else if(0 == _NSGetExecutablePath(execpath, &nsexeclength) && |
810 | | execpath[0] == SEP) |
811 | | { |
812 | | size_t len; |
813 | | wchar_t *path = Py_DecodeLocale(execpath, &len); |
814 | | if (path == NULL) { |
815 | | return DECODE_LOCALE_ERR("executable path", len); |
816 | | } |
817 | | if (safe_wcscpy(program_full_path, path, program_full_path_len) < 0) { |
818 | | PyMem_RawFree(path); |
819 | | return PATHLEN_ERR(); |
820 | | } |
821 | | PyMem_RawFree(path); |
822 | | } |
823 | | #endif /* __APPLE__ */ |
824 | 14 | else if (calculate->path_env) { |
825 | 14 | wchar_t *path = calculate->path_env; |
826 | 28 | while (1) { |
827 | 28 | wchar_t *delim = wcschr(path, DELIM); |
828 | | |
829 | 28 | if (delim) { |
830 | 28 | size_t len = delim - path; |
831 | 28 | if (len >= program_full_path_len) { |
832 | 0 | return PATHLEN_ERR(); |
833 | 0 | } |
834 | 28 | wcsncpy(program_full_path, path, len); |
835 | 28 | program_full_path[len] = '\0'; |
836 | 28 | } |
837 | 0 | else { |
838 | 0 | if (safe_wcscpy(program_full_path, path, |
839 | 0 | program_full_path_len) < 0) { |
840 | 0 | return PATHLEN_ERR(); |
841 | 0 | } |
842 | 0 | } |
843 | | |
844 | 28 | status = joinpath(program_full_path, pathconfig->program_name, |
845 | 28 | program_full_path_len); |
846 | 28 | if (_PyStatus_EXCEPTION(status)) { |
847 | 0 | return status; |
848 | 0 | } |
849 | | |
850 | 28 | if (isxfile(program_full_path)) { |
851 | 14 | break; |
852 | 14 | } |
853 | | |
854 | 14 | if (!delim) { |
855 | 0 | program_full_path[0] = L'\0'; |
856 | 0 | break; |
857 | 0 | } |
858 | 14 | path = delim + 1; |
859 | 14 | } |
860 | 14 | } |
861 | 0 | else { |
862 | 0 | program_full_path[0] = '\0'; |
863 | 0 | } |
864 | 14 | if (program_full_path[0] != SEP && program_full_path[0] != '\0') { |
865 | 0 | status = absolutize(program_full_path, program_full_path_len); |
866 | 0 | if (_PyStatus_EXCEPTION(status)) { |
867 | 0 | return status; |
868 | 0 | } |
869 | 0 | } |
870 | | #if defined(__CYGWIN__) || defined(__MINGW32__) |
871 | | /* For these platforms it is necessary to ensure that the .exe suffix |
872 | | * is appended to the filename, otherwise there is potential for |
873 | | * sys.executable to return the name of a directory under the same |
874 | | * path (bpo-28441). |
875 | | */ |
876 | | if (program_full_path[0] != '\0') { |
877 | | status = add_exe_suffix(program_full_path, program_full_path_len); |
878 | | if (_PyStatus_EXCEPTION(status)) { |
879 | | return status; |
880 | | } |
881 | | } |
882 | | #endif |
883 | | |
884 | 14 | pathconfig->program_full_path = _PyMem_RawWcsdup(program_full_path); |
885 | 14 | if (pathconfig->program_full_path == NULL) { |
886 | 0 | return _PyStatus_NO_MEMORY(); |
887 | 0 | } |
888 | 14 | return _PyStatus_OK(); |
889 | 14 | } |
890 | | |
891 | | |
892 | | static PyStatus |
893 | | calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path, |
894 | | wchar_t *argv0_path, size_t argv0_path_len) |
895 | 14 | { |
896 | 14 | if (safe_wcscpy(argv0_path, program_full_path, argv0_path_len) < 0) { |
897 | 0 | return PATHLEN_ERR(); |
898 | 0 | } |
899 | | |
900 | | #ifdef WITH_NEXT_FRAMEWORK |
901 | | NSModule pythonModule; |
902 | | |
903 | | /* On Mac OS X we have a special case if we're running from a framework. |
904 | | ** This is because the python home should be set relative to the library, |
905 | | ** which is in the framework, not relative to the executable, which may |
906 | | ** be outside of the framework. Except when we're in the build directory... |
907 | | */ |
908 | | pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize")); |
909 | | /* Use dylib functions to find out where the framework was loaded from */ |
910 | | const char* modPath = NSLibraryNameForModule(pythonModule); |
911 | | if (modPath != NULL) { |
912 | | /* We're in a framework. */ |
913 | | /* See if we might be in the build directory. The framework in the |
914 | | ** build directory is incomplete, it only has the .dylib and a few |
915 | | ** needed symlinks, it doesn't have the Lib directories and such. |
916 | | ** If we're running with the framework from the build directory we must |
917 | | ** be running the interpreter in the build directory, so we use the |
918 | | ** build-directory-specific logic to find Lib and such. |
919 | | */ |
920 | | PyStatus status; |
921 | | size_t len; |
922 | | wchar_t* wbuf = Py_DecodeLocale(modPath, &len); |
923 | | if (wbuf == NULL) { |
924 | | return DECODE_LOCALE_ERR("framework location", len); |
925 | | } |
926 | | |
927 | | if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) { |
928 | | return PATHLEN_ERR(); |
929 | | } |
930 | | reduce(argv0_path); |
931 | | status = joinpath(argv0_path, calculate->lib_python, argv0_path_len); |
932 | | if (_PyStatus_EXCEPTION(status)) { |
933 | | PyMem_RawFree(wbuf); |
934 | | return status; |
935 | | } |
936 | | status = joinpath(argv0_path, LANDMARK, argv0_path_len); |
937 | | if (_PyStatus_EXCEPTION(status)) { |
938 | | PyMem_RawFree(wbuf); |
939 | | return status; |
940 | | } |
941 | | if (!ismodule(argv0_path, Py_ARRAY_LENGTH(argv0_path))) { |
942 | | /* We are in the build directory so use the name of the |
943 | | executable - we know that the absolute path is passed */ |
944 | | if (safe_wcscpy(argv0_path, program_full_path, |
945 | | argv0_path_len) < 0) { |
946 | | return PATHLEN_ERR(); |
947 | | } |
948 | | } |
949 | | else { |
950 | | /* Use the location of the library as the program_full_path */ |
951 | | if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) { |
952 | | return PATHLEN_ERR(); |
953 | | } |
954 | | } |
955 | | PyMem_RawFree(wbuf); |
956 | | } |
957 | | #endif |
958 | | |
959 | 14 | #if HAVE_READLINK |
960 | 14 | wchar_t tmpbuffer[MAXPATHLEN + 1]; |
961 | 14 | const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer); |
962 | 14 | int linklen = _Py_wreadlink(program_full_path, tmpbuffer, buflen); |
963 | 28 | while (linklen != -1) { |
964 | 14 | if (tmpbuffer[0] == SEP) { |
965 | | /* tmpbuffer should never be longer than MAXPATHLEN, |
966 | | but extra check does not hurt */ |
967 | 14 | if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) { |
968 | 0 | return PATHLEN_ERR(); |
969 | 0 | } |
970 | 14 | } |
971 | 0 | else { |
972 | | /* Interpret relative to program_full_path */ |
973 | 0 | PyStatus status; |
974 | 0 | reduce(argv0_path); |
975 | 0 | status = joinpath(argv0_path, tmpbuffer, argv0_path_len); |
976 | 0 | if (_PyStatus_EXCEPTION(status)) { |
977 | 0 | return status; |
978 | 0 | } |
979 | 0 | } |
980 | 14 | linklen = _Py_wreadlink(argv0_path, tmpbuffer, buflen); |
981 | 14 | } |
982 | 14 | #endif /* HAVE_READLINK */ |
983 | | |
984 | 14 | reduce(argv0_path); |
985 | | /* At this point, argv0_path is guaranteed to be less than |
986 | | MAXPATHLEN bytes long. */ |
987 | 14 | return _PyStatus_OK(); |
988 | 14 | } |
989 | | |
990 | | |
991 | | /* Search for an "pyvenv.cfg" environment configuration file, first in the |
992 | | executable's directory and then in the parent directory. |
993 | | If found, open it for use when searching for prefixes. |
994 | | */ |
995 | | static PyStatus |
996 | | calculate_read_pyenv(PyCalculatePath *calculate, |
997 | | wchar_t *argv0_path, size_t argv0_path_len) |
998 | 14 | { |
999 | 14 | PyStatus status; |
1000 | 14 | const wchar_t *env_cfg = L"pyvenv.cfg"; |
1001 | 14 | FILE *env_file; |
1002 | | |
1003 | 14 | wchar_t filename[MAXPATHLEN+1]; |
1004 | 14 | const size_t filename_len = Py_ARRAY_LENGTH(filename); |
1005 | 14 | memset(filename, 0, sizeof(filename)); |
1006 | | |
1007 | | /* Filename: <argv0_path_len> / "pyvenv.cfg" */ |
1008 | 14 | if (safe_wcscpy(filename, argv0_path, filename_len) < 0) { |
1009 | 0 | return PATHLEN_ERR(); |
1010 | 0 | } |
1011 | | |
1012 | 14 | status = joinpath(filename, env_cfg, filename_len); |
1013 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1014 | 0 | return status; |
1015 | 0 | } |
1016 | 14 | env_file = _Py_wfopen(filename, L"r"); |
1017 | 14 | if (env_file == NULL) { |
1018 | 14 | errno = 0; |
1019 | | |
1020 | | /* Filename: <basename(basename(argv0_path_len))> / "pyvenv.cfg" */ |
1021 | 14 | reduce(filename); |
1022 | 14 | reduce(filename); |
1023 | 14 | status = joinpath(filename, env_cfg, filename_len); |
1024 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1025 | 0 | return status; |
1026 | 0 | } |
1027 | | |
1028 | 14 | env_file = _Py_wfopen(filename, L"r"); |
1029 | 14 | if (env_file == NULL) { |
1030 | 14 | errno = 0; |
1031 | 14 | return _PyStatus_OK(); |
1032 | 14 | } |
1033 | 14 | } |
1034 | | |
1035 | | /* Look for a 'home' variable and set argv0_path to it, if found */ |
1036 | 0 | wchar_t home[MAXPATHLEN+1]; |
1037 | 0 | memset(home, 0, sizeof(home)); |
1038 | |
|
1039 | 0 | if (_Py_FindEnvConfigValue(env_file, L"home", |
1040 | 0 | home, Py_ARRAY_LENGTH(home))) { |
1041 | 0 | if (safe_wcscpy(argv0_path, home, argv0_path_len) < 0) { |
1042 | 0 | fclose(env_file); |
1043 | 0 | return PATHLEN_ERR(); |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | fclose(env_file); |
1047 | 0 | return _PyStatus_OK(); |
1048 | 0 | } |
1049 | | |
1050 | | |
1051 | | static PyStatus |
1052 | | calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix, |
1053 | | wchar_t *zip_path, size_t zip_path_len) |
1054 | 14 | { |
1055 | 14 | PyStatus status; |
1056 | | |
1057 | 14 | if (calculate->prefix_found > 0) { |
1058 | | /* Use the reduced prefix returned by Py_GetPrefix() */ |
1059 | 14 | if (safe_wcscpy(zip_path, prefix, zip_path_len) < 0) { |
1060 | 0 | return PATHLEN_ERR(); |
1061 | 0 | } |
1062 | 14 | reduce(zip_path); |
1063 | 14 | reduce(zip_path); |
1064 | 14 | } |
1065 | 0 | else { |
1066 | 0 | if (safe_wcscpy(zip_path, calculate->prefix, zip_path_len) < 0) { |
1067 | 0 | return PATHLEN_ERR(); |
1068 | 0 | } |
1069 | 0 | } |
1070 | 14 | status = joinpath(zip_path, L"lib/python00.zip", zip_path_len); |
1071 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1072 | 0 | return status; |
1073 | 0 | } |
1074 | | |
1075 | | /* Replace "00" with version */ |
1076 | 14 | size_t bufsz = wcslen(zip_path); |
1077 | 14 | zip_path[bufsz - 6] = VERSION[0]; |
1078 | 14 | zip_path[bufsz - 5] = VERSION[2]; |
1079 | 14 | return _PyStatus_OK(); |
1080 | 14 | } |
1081 | | |
1082 | | |
1083 | | static PyStatus |
1084 | | calculate_module_search_path(PyCalculatePath *calculate, |
1085 | | _PyPathConfig *pathconfig, |
1086 | | const wchar_t *prefix, |
1087 | | const wchar_t *exec_prefix, |
1088 | | const wchar_t *zip_path) |
1089 | 14 | { |
1090 | | /* Calculate size of return buffer */ |
1091 | 14 | size_t bufsz = 0; |
1092 | 14 | if (calculate->pythonpath_env != NULL) { |
1093 | 14 | bufsz += wcslen(calculate->pythonpath_env) + 1; |
1094 | 14 | } |
1095 | | |
1096 | 14 | wchar_t *defpath = calculate->pythonpath; |
1097 | 14 | size_t prefixsz = wcslen(prefix) + 1; |
1098 | 14 | while (1) { |
1099 | 14 | wchar_t *delim = wcschr(defpath, DELIM); |
1100 | | |
1101 | 14 | if (defpath[0] != SEP) { |
1102 | | /* Paths are relative to prefix */ |
1103 | 14 | bufsz += prefixsz; |
1104 | 14 | } |
1105 | | |
1106 | 14 | if (delim) { |
1107 | 0 | bufsz += delim - defpath + 1; |
1108 | 0 | } |
1109 | 14 | else { |
1110 | 14 | bufsz += wcslen(defpath) + 1; |
1111 | 14 | break; |
1112 | 14 | } |
1113 | 0 | defpath = delim + 1; |
1114 | 0 | } |
1115 | | |
1116 | 14 | bufsz += wcslen(zip_path) + 1; |
1117 | 14 | bufsz += wcslen(exec_prefix) + 1; |
1118 | | |
1119 | | /* Allocate the buffer */ |
1120 | 14 | wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t)); |
1121 | 14 | if (buf == NULL) { |
1122 | 0 | return _PyStatus_NO_MEMORY(); |
1123 | 0 | } |
1124 | 14 | buf[0] = '\0'; |
1125 | | |
1126 | | /* Run-time value of $PYTHONPATH goes first */ |
1127 | 14 | if (calculate->pythonpath_env) { |
1128 | 14 | wcscpy(buf, calculate->pythonpath_env); |
1129 | 14 | wcscat(buf, delimiter); |
1130 | 14 | } |
1131 | | |
1132 | | /* Next is the default zip path */ |
1133 | 14 | wcscat(buf, zip_path); |
1134 | 14 | wcscat(buf, delimiter); |
1135 | | |
1136 | | /* Next goes merge of compile-time $PYTHONPATH with |
1137 | | * dynamically located prefix. |
1138 | | */ |
1139 | 14 | defpath = calculate->pythonpath; |
1140 | 14 | while (1) { |
1141 | 14 | wchar_t *delim = wcschr(defpath, DELIM); |
1142 | | |
1143 | 14 | if (defpath[0] != SEP) { |
1144 | 14 | wcscat(buf, prefix); |
1145 | 14 | if (prefixsz >= 2 && prefix[prefixsz - 2] != SEP && |
1146 | 14 | defpath[0] != (delim ? DELIM : L'\0')) |
1147 | 0 | { |
1148 | | /* not empty */ |
1149 | 0 | wcscat(buf, separator); |
1150 | 0 | } |
1151 | 14 | } |
1152 | | |
1153 | 14 | if (delim) { |
1154 | 0 | size_t len = delim - defpath + 1; |
1155 | 0 | size_t end = wcslen(buf) + len; |
1156 | 0 | wcsncat(buf, defpath, len); |
1157 | 0 | buf[end] = '\0'; |
1158 | 0 | } |
1159 | 14 | else { |
1160 | 14 | wcscat(buf, defpath); |
1161 | 14 | break; |
1162 | 14 | } |
1163 | 0 | defpath = delim + 1; |
1164 | 0 | } |
1165 | 14 | wcscat(buf, delimiter); |
1166 | | |
1167 | | /* Finally, on goes the directory for dynamic-load modules */ |
1168 | 14 | wcscat(buf, exec_prefix); |
1169 | | |
1170 | 14 | pathconfig->module_search_path = buf; |
1171 | 14 | return _PyStatus_OK(); |
1172 | 14 | } |
1173 | | |
1174 | | |
1175 | | static PyStatus |
1176 | | calculate_init(PyCalculatePath *calculate, const PyConfig *config) |
1177 | 14 | { |
1178 | 14 | size_t len; |
1179 | 14 | const char *path = getenv("PATH"); |
1180 | 14 | if (path) { |
1181 | 14 | calculate->path_env = Py_DecodeLocale(path, &len); |
1182 | 14 | if (!calculate->path_env) { |
1183 | 0 | return DECODE_LOCALE_ERR("PATH environment variable", len); |
1184 | 0 | } |
1185 | 14 | } |
1186 | | |
1187 | 14 | calculate->pythonpath = Py_DecodeLocale(PYTHONPATH, &len); |
1188 | 14 | if (!calculate->pythonpath) { |
1189 | 0 | return DECODE_LOCALE_ERR("PYTHONPATH define", len); |
1190 | 0 | } |
1191 | | |
1192 | 14 | calculate->prefix = Py_DecodeLocale(PREFIX, &len); |
1193 | 14 | if (!calculate->prefix) { |
1194 | 0 | return DECODE_LOCALE_ERR("PREFIX define", len); |
1195 | 0 | } |
1196 | 14 | calculate->exec_prefix = Py_DecodeLocale(EXEC_PREFIX, &len); |
1197 | 14 | if (!calculate->exec_prefix) { |
1198 | 0 | return DECODE_LOCALE_ERR("EXEC_PREFIX define", len); |
1199 | 0 | } |
1200 | 14 | calculate->lib_python = Py_DecodeLocale("lib/python" VERSION, &len); |
1201 | 14 | if (!calculate->lib_python) { |
1202 | 0 | return DECODE_LOCALE_ERR("EXEC_PREFIX define", len); |
1203 | 0 | } |
1204 | | |
1205 | 14 | calculate->warnings = config->pathconfig_warnings; |
1206 | 14 | calculate->pythonpath_env = config->pythonpath_env; |
1207 | | |
1208 | 14 | return _PyStatus_OK(); |
1209 | 14 | } |
1210 | | |
1211 | | |
1212 | | static void |
1213 | | calculate_free(PyCalculatePath *calculate) |
1214 | 14 | { |
1215 | 14 | PyMem_RawFree(calculate->pythonpath); |
1216 | 14 | PyMem_RawFree(calculate->prefix); |
1217 | 14 | PyMem_RawFree(calculate->exec_prefix); |
1218 | 14 | PyMem_RawFree(calculate->lib_python); |
1219 | 14 | PyMem_RawFree(calculate->path_env); |
1220 | 14 | } |
1221 | | |
1222 | | |
1223 | | static PyStatus |
1224 | | calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig) |
1225 | 14 | { |
1226 | 14 | PyStatus status; |
1227 | | |
1228 | 14 | if (pathconfig->program_full_path == NULL) { |
1229 | 14 | status = calculate_program_full_path(calculate, pathconfig); |
1230 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1231 | 0 | return status; |
1232 | 0 | } |
1233 | 14 | } |
1234 | | |
1235 | 14 | wchar_t argv0_path[MAXPATHLEN+1]; |
1236 | 14 | memset(argv0_path, 0, sizeof(argv0_path)); |
1237 | | |
1238 | 14 | status = calculate_argv0_path(calculate, pathconfig->program_full_path, |
1239 | 14 | argv0_path, Py_ARRAY_LENGTH(argv0_path)); |
1240 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1241 | 0 | return status; |
1242 | 0 | } |
1243 | | |
1244 | | /* If a pyvenv.cfg configure file is found, |
1245 | | argv0_path is overriden with its 'home' variable. */ |
1246 | 14 | status = calculate_read_pyenv(calculate, |
1247 | 14 | argv0_path, Py_ARRAY_LENGTH(argv0_path)); |
1248 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1249 | 0 | return status; |
1250 | 0 | } |
1251 | | |
1252 | 14 | wchar_t prefix[MAXPATHLEN+1]; |
1253 | 14 | memset(prefix, 0, sizeof(prefix)); |
1254 | 14 | status = calculate_prefix(calculate, pathconfig, |
1255 | 14 | argv0_path, |
1256 | 14 | prefix, Py_ARRAY_LENGTH(prefix)); |
1257 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1258 | 0 | return status; |
1259 | 0 | } |
1260 | | |
1261 | 14 | wchar_t zip_path[MAXPATHLEN+1]; /* ".../lib/pythonXY.zip" */ |
1262 | 14 | memset(zip_path, 0, sizeof(zip_path)); |
1263 | | |
1264 | 14 | status = calculate_zip_path(calculate, prefix, |
1265 | 14 | zip_path, Py_ARRAY_LENGTH(zip_path)); |
1266 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1267 | 0 | return status; |
1268 | 0 | } |
1269 | | |
1270 | 14 | wchar_t exec_prefix[MAXPATHLEN+1]; |
1271 | 14 | memset(exec_prefix, 0, sizeof(exec_prefix)); |
1272 | 14 | status = calculate_exec_prefix(calculate, pathconfig, argv0_path, |
1273 | 14 | exec_prefix, Py_ARRAY_LENGTH(exec_prefix)); |
1274 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1275 | 0 | return status; |
1276 | 0 | } |
1277 | | |
1278 | 14 | if ((!calculate->prefix_found || !calculate->exec_prefix_found) && |
1279 | 14 | calculate->warnings) |
1280 | 0 | { |
1281 | 0 | fprintf(stderr, |
1282 | 0 | "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n"); |
1283 | 0 | } |
1284 | | |
1285 | 14 | if (pathconfig->module_search_path == NULL) { |
1286 | 14 | status = calculate_module_search_path(calculate, pathconfig, |
1287 | 14 | prefix, exec_prefix, zip_path); |
1288 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1289 | 0 | return status; |
1290 | 0 | } |
1291 | 14 | } |
1292 | | |
1293 | 14 | if (pathconfig->prefix == NULL) { |
1294 | 14 | status = calculate_set_prefix(calculate, pathconfig, prefix); |
1295 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1296 | 0 | return status; |
1297 | 0 | } |
1298 | 14 | } |
1299 | | |
1300 | 14 | if (pathconfig->exec_prefix == NULL) { |
1301 | 14 | status = calculate_set_exec_prefix(calculate, pathconfig, exec_prefix); |
1302 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1303 | 0 | return status; |
1304 | 0 | } |
1305 | 14 | } |
1306 | | |
1307 | 14 | return _PyStatus_OK(); |
1308 | 14 | } |
1309 | | |
1310 | | |
1311 | | /* Calculate the Python path configuration. |
1312 | | |
1313 | | Inputs: |
1314 | | |
1315 | | - PATH environment variable |
1316 | | - Macros: PYTHONPATH, PREFIX, EXEC_PREFIX, VERSION (ex: "3.9"). |
1317 | | PREFIX and EXEC_PREFIX are generated by the configure script. |
1318 | | PYTHONPATH macro is the default search path. |
1319 | | - pybuilddir.txt file |
1320 | | - pyvenv.cfg configuration file |
1321 | | - PyConfig fields ('config' function argument): |
1322 | | |
1323 | | - pathconfig_warnings |
1324 | | - pythonpath_env (PYTHONPATH environment variable) |
1325 | | |
1326 | | - _PyPathConfig fields ('pathconfig' function argument): |
1327 | | |
1328 | | - program_name: see config_init_program_name() |
1329 | | - home: Py_SetPythonHome() or PYTHONHOME environment variable |
1330 | | |
1331 | | - current working directory: see copy_absolute() |
1332 | | |
1333 | | Outputs, 'pathconfig' fields: |
1334 | | |
1335 | | - program_full_path |
1336 | | - module_search_path |
1337 | | - prefix |
1338 | | - exec_prefix |
1339 | | |
1340 | | If a field is already set (non NULL), it is left unchanged. */ |
1341 | | PyStatus |
1342 | | _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config) |
1343 | 14 | { |
1344 | 14 | PyStatus status; |
1345 | 14 | PyCalculatePath calculate; |
1346 | 14 | memset(&calculate, 0, sizeof(calculate)); |
1347 | | |
1348 | 14 | status = calculate_init(&calculate, config); |
1349 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1350 | 0 | goto done; |
1351 | 0 | } |
1352 | | |
1353 | 14 | status = calculate_path(&calculate, pathconfig); |
1354 | 14 | if (_PyStatus_EXCEPTION(status)) { |
1355 | 0 | goto done; |
1356 | 0 | } |
1357 | | |
1358 | 14 | status = _PyStatus_OK(); |
1359 | | |
1360 | 14 | done: |
1361 | 14 | calculate_free(&calculate); |
1362 | 14 | return status; |
1363 | 14 | } |
1364 | | |
1365 | | #ifdef __cplusplus |
1366 | | } |
1367 | | #endif |