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