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 return status;
1267 }
1268 if (env_file == NULL) {
1269 /* pyvenv.cfg not found */
1270 return _PyStatus_OK();
1271 }
1272
1273 /* Look for a 'home' variable and set argv0_path to it, if found */
1274 wchar_t *home = NULL;
1275 status = _Py_FindEnvConfigValue(env_file, L"home", &home);
1276 if (_PyStatus_EXCEPTION(status)) {
1277 fclose(env_file);
1278 return status;
1279 }
1280
1281 if (home) {
1282 PyMem_RawFree(calculate->argv0_path);
1283 calculate->argv0_path = home;
1284 }
1285 fclose(env_file);
1286 return _PyStatus_OK();
1287 }
1288
1289
1290 static PyStatus
calculate_zip_path(PyCalculatePath * calculate)1291 calculate_zip_path(PyCalculatePath *calculate)
1292 {
1293 PyStatus res;
1294
1295 /* Path: <platlibdir> / "pythonXY.zip" */
1296 wchar_t *path = joinpath2(calculate->platlibdir,
1297 L"python" Py_STRINGIFY(PY_MAJOR_VERSION) Py_STRINGIFY(PY_MINOR_VERSION)
1298 L".zip");
1299 if (path == NULL) {
1300 return _PyStatus_NO_MEMORY();
1301 }
1302
1303 if (calculate->prefix_found > 0) {
1304 /* Use the reduced prefix returned by Py_GetPrefix()
1305
1306 Path: <basename(basename(prefix))> / <platlibdir> / "pythonXY.zip" */
1307 wchar_t *parent = _PyMem_RawWcsdup(calculate->prefix);
1308 if (parent == NULL) {
1309 res = _PyStatus_NO_MEMORY();
1310 goto done;
1311 }
1312 reduce(parent);
1313 reduce(parent);
1314 calculate->zip_path = joinpath2(parent, path);
1315 PyMem_RawFree(parent);
1316 }
1317 else {
1318 calculate->zip_path = joinpath2(calculate->prefix_macro, path);
1319 }
1320
1321 if (calculate->zip_path == NULL) {
1322 res = _PyStatus_NO_MEMORY();
1323 goto done;
1324 }
1325
1326 /* Replace "00" with version */
1327 size_t len = wcslen(calculate->zip_path);
1328 calculate->zip_path[len - 6] = VERSION[0];
1329 calculate->zip_path[len - 5] = VERSION[2];
1330
1331 res = _PyStatus_OK();
1332
1333 done:
1334 PyMem_RawFree(path);
1335 return res;
1336 }
1337
1338
1339 static PyStatus
calculate_module_search_path(PyCalculatePath * calculate,_PyPathConfig * pathconfig)1340 calculate_module_search_path(PyCalculatePath *calculate,
1341 _PyPathConfig *pathconfig)
1342 {
1343 /* Calculate size of return buffer */
1344 size_t bufsz = 0;
1345 if (calculate->pythonpath_env != NULL) {
1346 bufsz += wcslen(calculate->pythonpath_env) + 1;
1347 }
1348
1349 wchar_t *defpath = calculate->pythonpath_macro;
1350 size_t prefixsz = wcslen(calculate->prefix) + 1;
1351 while (1) {
1352 wchar_t *delim = wcschr(defpath, DELIM);
1353
1354 if (!_Py_isabs(defpath)) {
1355 /* Paths are relative to prefix */
1356 bufsz += prefixsz;
1357 }
1358
1359 if (delim) {
1360 bufsz += delim - defpath + 1;
1361 }
1362 else {
1363 bufsz += wcslen(defpath) + 1;
1364 break;
1365 }
1366 defpath = delim + 1;
1367 }
1368
1369 bufsz += wcslen(calculate->zip_path) + 1;
1370 bufsz += wcslen(calculate->exec_prefix) + 1;
1371
1372 /* Allocate the buffer */
1373 wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
1374 if (buf == NULL) {
1375 return _PyStatus_NO_MEMORY();
1376 }
1377 buf[0] = '\0';
1378
1379 /* Run-time value of $PYTHONPATH goes first */
1380 if (calculate->pythonpath_env) {
1381 wcscpy(buf, calculate->pythonpath_env);
1382 wcscat(buf, delimiter);
1383 }
1384
1385 /* Next is the default zip path */
1386 wcscat(buf, calculate->zip_path);
1387 wcscat(buf, delimiter);
1388
1389 /* Next goes merge of compile-time $PYTHONPATH with
1390 * dynamically located prefix.
1391 */
1392 defpath = calculate->pythonpath_macro;
1393 while (1) {
1394 wchar_t *delim = wcschr(defpath, DELIM);
1395
1396 if (!_Py_isabs(defpath)) {
1397 wcscat(buf, calculate->prefix);
1398 if (prefixsz >= 2 && calculate->prefix[prefixsz - 2] != SEP &&
1399 defpath[0] != (delim ? DELIM : L'\0'))
1400 {
1401 /* not empty */
1402 wcscat(buf, separator);
1403 }
1404 }
1405
1406 if (delim) {
1407 size_t len = delim - defpath + 1;
1408 size_t end = wcslen(buf) + len;
1409 wcsncat(buf, defpath, len);
1410 buf[end] = '\0';
1411 }
1412 else {
1413 wcscat(buf, defpath);
1414 break;
1415 }
1416 defpath = delim + 1;
1417 }
1418 wcscat(buf, delimiter);
1419
1420 /* Finally, on goes the directory for dynamic-load modules */
1421 wcscat(buf, calculate->exec_prefix);
1422
1423 pathconfig->module_search_path = buf;
1424 return _PyStatus_OK();
1425 }
1426
1427
1428 static PyStatus
calculate_init(PyCalculatePath * calculate,const PyConfig * config)1429 calculate_init(PyCalculatePath *calculate, const PyConfig *config)
1430 {
1431 size_t len;
1432
1433 calculate->warnings = config->pathconfig_warnings;
1434 calculate->pythonpath_env = config->pythonpath_env;
1435 calculate->platlibdir = config->platlibdir;
1436
1437 const char *path = getenv("PATH");
1438 if (path) {
1439 calculate->path_env = Py_DecodeLocale(path, &len);
1440 if (!calculate->path_env) {
1441 return DECODE_LOCALE_ERR("PATH environment variable", len);
1442 }
1443 }
1444
1445 /* Decode macros */
1446 calculate->pythonpath_macro = Py_DecodeLocale(PYTHONPATH, &len);
1447 if (!calculate->pythonpath_macro) {
1448 return DECODE_LOCALE_ERR("PYTHONPATH macro", len);
1449 }
1450 calculate->prefix_macro = Py_DecodeLocale(PREFIX, &len);
1451 if (!calculate->prefix_macro) {
1452 return DECODE_LOCALE_ERR("PREFIX macro", len);
1453 }
1454 calculate->exec_prefix_macro = Py_DecodeLocale(EXEC_PREFIX, &len);
1455 if (!calculate->exec_prefix_macro) {
1456 return DECODE_LOCALE_ERR("EXEC_PREFIX macro", len);
1457 }
1458 calculate->vpath_macro = Py_DecodeLocale(VPATH, &len);
1459 if (!calculate->vpath_macro) {
1460 return DECODE_LOCALE_ERR("VPATH macro", len);
1461 }
1462
1463 // <platlibdir> / "pythonX.Y"
1464 wchar_t *pyversion = Py_DecodeLocale("python" VERSION, &len);
1465 if (!pyversion) {
1466 return DECODE_LOCALE_ERR("VERSION macro", len);
1467 }
1468 calculate->lib_python = joinpath2(config->platlibdir, pyversion);
1469 PyMem_RawFree(pyversion);
1470 if (calculate->lib_python == NULL) {
1471 return _PyStatus_NO_MEMORY();
1472 }
1473
1474 return _PyStatus_OK();
1475 }
1476
1477
1478 static void
calculate_free(PyCalculatePath * calculate)1479 calculate_free(PyCalculatePath *calculate)
1480 {
1481 PyMem_RawFree(calculate->pythonpath_macro);
1482 PyMem_RawFree(calculate->prefix_macro);
1483 PyMem_RawFree(calculate->exec_prefix_macro);
1484 PyMem_RawFree(calculate->vpath_macro);
1485 PyMem_RawFree(calculate->lib_python);
1486 PyMem_RawFree(calculate->path_env);
1487 PyMem_RawFree(calculate->zip_path);
1488 PyMem_RawFree(calculate->argv0_path);
1489 PyMem_RawFree(calculate->prefix);
1490 PyMem_RawFree(calculate->exec_prefix);
1491 }
1492
1493
1494 static PyStatus
calculate_path(PyCalculatePath * calculate,_PyPathConfig * pathconfig)1495 calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
1496 {
1497 PyStatus status;
1498
1499 if (pathconfig->program_full_path == NULL) {
1500 status = calculate_program(calculate, pathconfig);
1501 if (_PyStatus_EXCEPTION(status)) {
1502 return status;
1503 }
1504 }
1505
1506 status = calculate_argv0_path(calculate, pathconfig);
1507 if (_PyStatus_EXCEPTION(status)) {
1508 return status;
1509 }
1510
1511 /* If a pyvenv.cfg configure file is found,
1512 argv0_path is overriden with its 'home' variable. */
1513 status = calculate_read_pyenv(calculate);
1514 if (_PyStatus_EXCEPTION(status)) {
1515 return status;
1516 }
1517
1518 status = calculate_prefix(calculate, pathconfig);
1519 if (_PyStatus_EXCEPTION(status)) {
1520 return status;
1521 }
1522
1523 status = calculate_zip_path(calculate);
1524 if (_PyStatus_EXCEPTION(status)) {
1525 return status;
1526 }
1527
1528 status = calculate_exec_prefix(calculate, pathconfig);
1529 if (_PyStatus_EXCEPTION(status)) {
1530 return status;
1531 }
1532
1533 if ((!calculate->prefix_found || !calculate->exec_prefix_found)
1534 && calculate->warnings)
1535 {
1536 fprintf(stderr,
1537 "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
1538 }
1539
1540 if (pathconfig->module_search_path == NULL) {
1541 status = calculate_module_search_path(calculate, pathconfig);
1542 if (_PyStatus_EXCEPTION(status)) {
1543 return status;
1544 }
1545 }
1546
1547 if (pathconfig->prefix == NULL) {
1548 status = calculate_set_prefix(calculate, pathconfig);
1549 if (_PyStatus_EXCEPTION(status)) {
1550 return status;
1551 }
1552 }
1553
1554 if (pathconfig->exec_prefix == NULL) {
1555 status = calculate_set_exec_prefix(calculate, pathconfig);
1556 if (_PyStatus_EXCEPTION(status)) {
1557 return status;
1558 }
1559 }
1560 return _PyStatus_OK();
1561 }
1562
1563
1564 /* Calculate the Python path configuration.
1565
1566 Inputs:
1567
1568 - PATH environment variable
1569 - Macros: PYTHONPATH, PREFIX, EXEC_PREFIX, VERSION (ex: "3.9").
1570 PREFIX and EXEC_PREFIX are generated by the configure script.
1571 PYTHONPATH macro is the default search path.
1572 - pybuilddir.txt file
1573 - pyvenv.cfg configuration file
1574 - PyConfig fields ('config' function argument):
1575
1576 - pathconfig_warnings
1577 - pythonpath_env (PYTHONPATH environment variable)
1578
1579 - _PyPathConfig fields ('pathconfig' function argument):
1580
1581 - program_name: see config_init_program_name()
1582 - home: Py_SetPythonHome() or PYTHONHOME environment variable
1583
1584 - current working directory: see copy_absolute()
1585
1586 Outputs, 'pathconfig' fields:
1587
1588 - program_full_path
1589 - module_search_path
1590 - prefix
1591 - exec_prefix
1592
1593 If a field is already set (non NULL), it is left unchanged. */
1594 PyStatus
_PyPathConfig_Calculate(_PyPathConfig * pathconfig,const PyConfig * config)1595 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
1596 {
1597 PyStatus status;
1598 PyCalculatePath calculate;
1599 memset(&calculate, 0, sizeof(calculate));
1600
1601 status = calculate_init(&calculate, config);
1602 if (_PyStatus_EXCEPTION(status)) {
1603 goto done;
1604 }
1605
1606 status = calculate_path(&calculate, pathconfig);
1607 if (_PyStatus_EXCEPTION(status)) {
1608 goto done;
1609 }
1610
1611 /* program_full_path must an either an empty string or an absolute path */
1612 assert(wcslen(pathconfig->program_full_path) == 0
1613 || _Py_isabs(pathconfig->program_full_path));
1614
1615 status = _PyStatus_OK();
1616
1617 done:
1618 calculate_free(&calculate);
1619 return status;
1620 }
1621
1622 #ifdef __cplusplus
1623 }
1624 #endif
1625