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