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