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     uint32_t nsexeclength = Py_ARRAY_LENGTH(execpath) - 1;
781 #endif
782 
783     /* If there is no slash in the argv0 path, then we have to
784      * assume python is on the user's $PATH, since there's no
785      * other way to find a directory to start the search from.  If
786      * $PATH isn't exported, you lose.
787      */
788     if (wcschr(pathconfig->program_name, SEP)) {
789         if (safe_wcscpy(program_full_path, pathconfig->program_name,
790                         program_full_path_len) < 0) {
791             return PATHLEN_ERR();
792         }
793     }
794 #ifdef __APPLE__
795      /* On Mac OS X, if a script uses an interpreter of the form
796       * "#!/opt/python2.3/bin/python", the kernel only passes "python"
797       * as argv[0], which falls through to the $PATH search below.
798       * If /opt/python2.3/bin isn't in your path, or is near the end,
799       * this algorithm may incorrectly find /usr/bin/python. To work
800       * around this, we can use _NSGetExecutablePath to get a better
801       * hint of what the intended interpreter was, although this
802       * will fail if a relative path was used. but in that case,
803       * absolutize() should help us out below
804       */
805     else if(0 == _NSGetExecutablePath(execpath, &nsexeclength) &&
806             execpath[0] == SEP)
807     {
808         size_t len;
809         wchar_t *path = Py_DecodeLocale(execpath, &len);
810         if (path == NULL) {
811             return DECODE_LOCALE_ERR("executable path", len);
812         }
813         if (safe_wcscpy(program_full_path, path, program_full_path_len) < 0) {
814             PyMem_RawFree(path);
815             return PATHLEN_ERR();
816         }
817         PyMem_RawFree(path);
818     }
819 #endif /* __APPLE__ */
820     else if (calculate->path_env) {
821         wchar_t *path = calculate->path_env;
822         while (1) {
823             wchar_t *delim = wcschr(path, DELIM);
824 
825             if (delim) {
826                 size_t len = delim - path;
827                 if (len >= program_full_path_len) {
828                     return PATHLEN_ERR();
829                 }
830                 wcsncpy(program_full_path, path, len);
831                 program_full_path[len] = '\0';
832             }
833             else {
834                 if (safe_wcscpy(program_full_path, path,
835                                 program_full_path_len) < 0) {
836                     return PATHLEN_ERR();
837                 }
838             }
839 
840             status = joinpath(program_full_path, pathconfig->program_name,
841                               program_full_path_len);
842             if (_PyStatus_EXCEPTION(status)) {
843                 return status;
844             }
845 
846             if (isxfile(program_full_path)) {
847                 break;
848             }
849 
850             if (!delim) {
851                 program_full_path[0] = L'\0';
852                 break;
853             }
854             path = delim + 1;
855         }
856     }
857     else {
858         program_full_path[0] = '\0';
859     }
860     if (program_full_path[0] != SEP && program_full_path[0] != '\0') {
861         status = absolutize(program_full_path, program_full_path_len);
862         if (_PyStatus_EXCEPTION(status)) {
863             return status;
864         }
865     }
866 #if defined(__CYGWIN__) || defined(__MINGW32__)
867     /* For these platforms it is necessary to ensure that the .exe suffix
868      * is appended to the filename, otherwise there is potential for
869      * sys.executable to return the name of a directory under the same
870      * path (bpo-28441).
871      */
872     if (program_full_path[0] != '\0') {
873         status = add_exe_suffix(program_full_path, program_full_path_len);
874         if (_PyStatus_EXCEPTION(status)) {
875             return status;
876         }
877     }
878 #endif
879 
880     pathconfig->program_full_path = _PyMem_RawWcsdup(program_full_path);
881     if (pathconfig->program_full_path == NULL) {
882         return _PyStatus_NO_MEMORY();
883     }
884     return _PyStatus_OK();
885 }
886 
887 
888 static PyStatus
calculate_argv0_path(PyCalculatePath * calculate,const wchar_t * program_full_path,wchar_t * argv0_path,size_t argv0_path_len)889 calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path,
890                      wchar_t *argv0_path, size_t argv0_path_len)
891 {
892     if (safe_wcscpy(argv0_path, program_full_path, argv0_path_len) < 0) {
893         return PATHLEN_ERR();
894     }
895 
896 #ifdef WITH_NEXT_FRAMEWORK
897     NSModule pythonModule;
898 
899     /* On Mac OS X we have a special case if we're running from a framework.
900     ** This is because the python home should be set relative to the library,
901     ** which is in the framework, not relative to the executable, which may
902     ** be outside of the framework. Except when we're in the build directory...
903     */
904     pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize"));
905     /* Use dylib functions to find out where the framework was loaded from */
906     const char* modPath = NSLibraryNameForModule(pythonModule);
907     if (modPath != NULL) {
908         /* We're in a framework. */
909         /* See if we might be in the build directory. The framework in the
910         ** build directory is incomplete, it only has the .dylib and a few
911         ** needed symlinks, it doesn't have the Lib directories and such.
912         ** If we're running with the framework from the build directory we must
913         ** be running the interpreter in the build directory, so we use the
914         ** build-directory-specific logic to find Lib and such.
915         */
916         PyStatus status;
917         size_t len;
918         wchar_t* wbuf = Py_DecodeLocale(modPath, &len);
919         if (wbuf == NULL) {
920             return DECODE_LOCALE_ERR("framework location", len);
921         }
922 
923         if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) {
924             return PATHLEN_ERR();
925         }
926         reduce(argv0_path);
927         status = joinpath(argv0_path, calculate->lib_python, argv0_path_len);
928         if (_PyStatus_EXCEPTION(status)) {
929             PyMem_RawFree(wbuf);
930             return status;
931         }
932         status = joinpath(argv0_path, LANDMARK, argv0_path_len);
933         if (_PyStatus_EXCEPTION(status)) {
934             PyMem_RawFree(wbuf);
935             return status;
936         }
937         if (!ismodule(argv0_path, Py_ARRAY_LENGTH(argv0_path))) {
938             /* We are in the build directory so use the name of the
939                executable - we know that the absolute path is passed */
940             if (safe_wcscpy(argv0_path, program_full_path,
941                             argv0_path_len) < 0) {
942                 return PATHLEN_ERR();
943             }
944         }
945         else {
946             /* Use the location of the library as the program_full_path */
947             if (safe_wcscpy(argv0_path, wbuf, argv0_path_len) < 0) {
948                 return PATHLEN_ERR();
949             }
950         }
951         PyMem_RawFree(wbuf);
952     }
953 #endif
954 
955 #if HAVE_READLINK
956     wchar_t tmpbuffer[MAXPATHLEN + 1];
957     const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer);
958     int linklen = _Py_wreadlink(program_full_path, tmpbuffer, buflen);
959     while (linklen != -1) {
960         if (tmpbuffer[0] == SEP) {
961             /* tmpbuffer should never be longer than MAXPATHLEN,
962                but extra check does not hurt */
963             if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
964                 return PATHLEN_ERR();
965             }
966         }
967         else {
968             /* Interpret relative to program_full_path */
969             PyStatus status;
970             reduce(argv0_path);
971             status = joinpath(argv0_path, tmpbuffer, argv0_path_len);
972             if (_PyStatus_EXCEPTION(status)) {
973                 return status;
974             }
975         }
976         linklen = _Py_wreadlink(argv0_path, tmpbuffer, buflen);
977     }
978 #endif /* HAVE_READLINK */
979 
980     reduce(argv0_path);
981     /* At this point, argv0_path is guaranteed to be less than
982        MAXPATHLEN bytes long. */
983     return _PyStatus_OK();
984 }
985 
986 
987 /* Search for an "pyvenv.cfg" environment configuration file, first in the
988    executable's directory and then in the parent directory.
989    If found, open it for use when searching for prefixes.
990 */
991 static PyStatus
calculate_read_pyenv(PyCalculatePath * calculate,wchar_t * argv0_path,size_t argv0_path_len)992 calculate_read_pyenv(PyCalculatePath *calculate,
993                      wchar_t *argv0_path, size_t argv0_path_len)
994 {
995     PyStatus status;
996     const wchar_t *env_cfg = L"pyvenv.cfg";
997     FILE *env_file;
998 
999     wchar_t filename[MAXPATHLEN+1];
1000     const size_t filename_len = Py_ARRAY_LENGTH(filename);
1001     memset(filename, 0, sizeof(filename));
1002 
1003     /* Filename: <argv0_path_len> / "pyvenv.cfg" */
1004     if (safe_wcscpy(filename, argv0_path, filename_len) < 0) {
1005         return PATHLEN_ERR();
1006     }
1007 
1008     status = joinpath(filename, env_cfg, filename_len);
1009     if (_PyStatus_EXCEPTION(status)) {
1010         return status;
1011     }
1012     env_file = _Py_wfopen(filename, L"r");
1013     if (env_file == NULL) {
1014         errno = 0;
1015 
1016         /* Filename: <basename(basename(argv0_path_len))> / "pyvenv.cfg" */
1017         reduce(filename);
1018         reduce(filename);
1019         status = joinpath(filename, env_cfg, filename_len);
1020         if (_PyStatus_EXCEPTION(status)) {
1021             return status;
1022         }
1023 
1024         env_file = _Py_wfopen(filename, L"r");
1025         if (env_file == NULL) {
1026             errno = 0;
1027             return _PyStatus_OK();
1028         }
1029     }
1030 
1031     /* Look for a 'home' variable and set argv0_path to it, if found */
1032     wchar_t home[MAXPATHLEN+1];
1033     memset(home, 0, sizeof(home));
1034 
1035     if (_Py_FindEnvConfigValue(env_file, L"home",
1036                                home, Py_ARRAY_LENGTH(home))) {
1037         if (safe_wcscpy(argv0_path, home, argv0_path_len) < 0) {
1038             fclose(env_file);
1039             return PATHLEN_ERR();
1040         }
1041     }
1042     fclose(env_file);
1043     return _PyStatus_OK();
1044 }
1045 
1046 
1047 static PyStatus
calculate_zip_path(PyCalculatePath * calculate,const wchar_t * prefix,wchar_t * zip_path,size_t zip_path_len)1048 calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix,
1049                    wchar_t *zip_path, size_t zip_path_len)
1050 {
1051     PyStatus status;
1052 
1053     if (calculate->prefix_found > 0) {
1054         /* Use the reduced prefix returned by Py_GetPrefix() */
1055         if (safe_wcscpy(zip_path, prefix, zip_path_len) < 0) {
1056             return PATHLEN_ERR();
1057         }
1058         reduce(zip_path);
1059         reduce(zip_path);
1060     }
1061     else {
1062         if (safe_wcscpy(zip_path, calculate->prefix, zip_path_len) < 0) {
1063             return PATHLEN_ERR();
1064         }
1065     }
1066     status = joinpath(zip_path, L"lib/python00.zip", zip_path_len);
1067     if (_PyStatus_EXCEPTION(status)) {
1068         return status;
1069     }
1070 
1071     /* Replace "00" with version */
1072     size_t bufsz = wcslen(zip_path);
1073     zip_path[bufsz - 6] = VERSION[0];
1074     zip_path[bufsz - 5] = VERSION[2];
1075     return _PyStatus_OK();
1076 }
1077 
1078 
1079 static PyStatus
calculate_module_search_path(PyCalculatePath * calculate,_PyPathConfig * pathconfig,const wchar_t * prefix,const wchar_t * exec_prefix,const wchar_t * zip_path)1080 calculate_module_search_path(PyCalculatePath *calculate,
1081                              _PyPathConfig *pathconfig,
1082                              const wchar_t *prefix,
1083                              const wchar_t *exec_prefix,
1084                              const wchar_t *zip_path)
1085 {
1086     /* Calculate size of return buffer */
1087     size_t bufsz = 0;
1088     if (calculate->pythonpath_env != NULL) {
1089         bufsz += wcslen(calculate->pythonpath_env) + 1;
1090     }
1091 
1092     wchar_t *defpath = calculate->pythonpath;
1093     size_t prefixsz = wcslen(prefix) + 1;
1094     while (1) {
1095         wchar_t *delim = wcschr(defpath, DELIM);
1096 
1097         if (defpath[0] != SEP) {
1098             /* Paths are relative to prefix */
1099             bufsz += prefixsz;
1100         }
1101 
1102         if (delim) {
1103             bufsz += delim - defpath + 1;
1104         }
1105         else {
1106             bufsz += wcslen(defpath) + 1;
1107             break;
1108         }
1109         defpath = delim + 1;
1110     }
1111 
1112     bufsz += wcslen(zip_path) + 1;
1113     bufsz += wcslen(exec_prefix) + 1;
1114 
1115     /* Allocate the buffer */
1116     wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
1117     if (buf == NULL) {
1118         return _PyStatus_NO_MEMORY();
1119     }
1120     buf[0] = '\0';
1121 
1122     /* Run-time value of $PYTHONPATH goes first */
1123     if (calculate->pythonpath_env) {
1124         wcscpy(buf, calculate->pythonpath_env);
1125         wcscat(buf, delimiter);
1126     }
1127 
1128     /* Next is the default zip path */
1129     wcscat(buf, zip_path);
1130     wcscat(buf, delimiter);
1131 
1132     /* Next goes merge of compile-time $PYTHONPATH with
1133      * dynamically located prefix.
1134      */
1135     defpath = calculate->pythonpath;
1136     while (1) {
1137         wchar_t *delim = wcschr(defpath, DELIM);
1138 
1139         if (defpath[0] != SEP) {
1140             wcscat(buf, prefix);
1141             if (prefixsz >= 2 && prefix[prefixsz - 2] != SEP &&
1142                 defpath[0] != (delim ? DELIM : L'\0'))
1143             {
1144                 /* not empty */
1145                 wcscat(buf, separator);
1146             }
1147         }
1148 
1149         if (delim) {
1150             size_t len = delim - defpath + 1;
1151             size_t end = wcslen(buf) + len;
1152             wcsncat(buf, defpath, len);
1153             buf[end] = '\0';
1154         }
1155         else {
1156             wcscat(buf, defpath);
1157             break;
1158         }
1159         defpath = delim + 1;
1160     }
1161     wcscat(buf, delimiter);
1162 
1163     /* Finally, on goes the directory for dynamic-load modules */
1164     wcscat(buf, exec_prefix);
1165 
1166     pathconfig->module_search_path = buf;
1167     return _PyStatus_OK();
1168 }
1169 
1170 
1171 static PyStatus
calculate_init(PyCalculatePath * calculate,const PyConfig * config)1172 calculate_init(PyCalculatePath *calculate, const PyConfig *config)
1173 {
1174     size_t len;
1175     const char *path = getenv("PATH");
1176     if (path) {
1177         calculate->path_env = Py_DecodeLocale(path, &len);
1178         if (!calculate->path_env) {
1179             return DECODE_LOCALE_ERR("PATH environment variable", len);
1180         }
1181     }
1182 
1183     calculate->pythonpath = Py_DecodeLocale(PYTHONPATH, &len);
1184     if (!calculate->pythonpath) {
1185         return DECODE_LOCALE_ERR("PYTHONPATH define", len);
1186     }
1187 
1188     calculate->prefix = Py_DecodeLocale(PREFIX, &len);
1189     if (!calculate->prefix) {
1190         return DECODE_LOCALE_ERR("PREFIX define", len);
1191     }
1192     calculate->exec_prefix = Py_DecodeLocale(EXEC_PREFIX, &len);
1193     if (!calculate->exec_prefix) {
1194         return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
1195     }
1196     calculate->lib_python = Py_DecodeLocale("lib/python" VERSION, &len);
1197     if (!calculate->lib_python) {
1198         return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
1199     }
1200 
1201     calculate->warnings = config->pathconfig_warnings;
1202     calculate->pythonpath_env = config->pythonpath_env;
1203 
1204     return _PyStatus_OK();
1205 }
1206 
1207 
1208 static void
calculate_free(PyCalculatePath * calculate)1209 calculate_free(PyCalculatePath *calculate)
1210 {
1211     PyMem_RawFree(calculate->pythonpath);
1212     PyMem_RawFree(calculate->prefix);
1213     PyMem_RawFree(calculate->exec_prefix);
1214     PyMem_RawFree(calculate->lib_python);
1215     PyMem_RawFree(calculate->path_env);
1216 }
1217 
1218 
1219 static PyStatus
calculate_path(PyCalculatePath * calculate,_PyPathConfig * pathconfig)1220 calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
1221 {
1222     PyStatus status;
1223 
1224     if (pathconfig->program_full_path == NULL) {
1225         status = calculate_program_full_path(calculate, pathconfig);
1226         if (_PyStatus_EXCEPTION(status)) {
1227             return status;
1228         }
1229     }
1230 
1231     wchar_t argv0_path[MAXPATHLEN+1];
1232     memset(argv0_path, 0, sizeof(argv0_path));
1233 
1234     status = calculate_argv0_path(calculate, pathconfig->program_full_path,
1235                                   argv0_path, Py_ARRAY_LENGTH(argv0_path));
1236     if (_PyStatus_EXCEPTION(status)) {
1237         return status;
1238     }
1239 
1240     /* If a pyvenv.cfg configure file is found,
1241        argv0_path is overriden with its 'home' variable. */
1242     status = calculate_read_pyenv(calculate,
1243                                   argv0_path, Py_ARRAY_LENGTH(argv0_path));
1244     if (_PyStatus_EXCEPTION(status)) {
1245         return status;
1246     }
1247 
1248     wchar_t prefix[MAXPATHLEN+1];
1249     memset(prefix, 0, sizeof(prefix));
1250     status = calculate_prefix(calculate, pathconfig,
1251                               argv0_path,
1252                               prefix, Py_ARRAY_LENGTH(prefix));
1253     if (_PyStatus_EXCEPTION(status)) {
1254         return status;
1255     }
1256 
1257     wchar_t zip_path[MAXPATHLEN+1];    /* ".../lib/pythonXY.zip" */
1258     memset(zip_path, 0, sizeof(zip_path));
1259 
1260     status = calculate_zip_path(calculate, prefix,
1261                                 zip_path, Py_ARRAY_LENGTH(zip_path));
1262     if (_PyStatus_EXCEPTION(status)) {
1263         return status;
1264     }
1265 
1266     wchar_t exec_prefix[MAXPATHLEN+1];
1267     memset(exec_prefix, 0, sizeof(exec_prefix));
1268     status = calculate_exec_prefix(calculate, pathconfig, argv0_path,
1269                                    exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
1270     if (_PyStatus_EXCEPTION(status)) {
1271         return status;
1272     }
1273 
1274     if ((!calculate->prefix_found || !calculate->exec_prefix_found) &&
1275         calculate->warnings)
1276     {
1277         fprintf(stderr,
1278                 "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
1279     }
1280 
1281     if (pathconfig->module_search_path == NULL) {
1282         status = calculate_module_search_path(calculate, pathconfig,
1283                                               prefix, exec_prefix, zip_path);
1284         if (_PyStatus_EXCEPTION(status)) {
1285             return status;
1286         }
1287     }
1288 
1289     if (pathconfig->prefix == NULL) {
1290         status = calculate_set_prefix(calculate, pathconfig, prefix);
1291         if (_PyStatus_EXCEPTION(status)) {
1292             return status;
1293         }
1294     }
1295 
1296     if (pathconfig->exec_prefix == NULL) {
1297         status = calculate_set_exec_prefix(calculate, pathconfig, exec_prefix);
1298         if (_PyStatus_EXCEPTION(status)) {
1299             return status;
1300         }
1301     }
1302 
1303     return _PyStatus_OK();
1304 }
1305 
1306 
1307 /* Calculate the Python path configuration.
1308 
1309    Inputs:
1310 
1311    - PATH environment variable
1312    - Macros: PYTHONPATH, PREFIX, EXEC_PREFIX, VERSION (ex: "3.9").
1313      PREFIX and EXEC_PREFIX are generated by the configure script.
1314      PYTHONPATH macro is the default search path.
1315    - pybuilddir.txt file
1316    - pyvenv.cfg configuration file
1317    - PyConfig fields ('config' function argument):
1318 
1319      - pathconfig_warnings
1320      - pythonpath_env (PYTHONPATH environment variable)
1321 
1322    - _PyPathConfig fields ('pathconfig' function argument):
1323 
1324      - program_name: see config_init_program_name()
1325      - home: Py_SetPythonHome() or PYTHONHOME environment variable
1326 
1327    - current working directory: see copy_absolute()
1328 
1329    Outputs, 'pathconfig' fields:
1330 
1331    - program_full_path
1332    - module_search_path
1333    - prefix
1334    - exec_prefix
1335 
1336    If a field is already set (non NULL), it is left unchanged. */
1337 PyStatus
_PyPathConfig_Calculate(_PyPathConfig * pathconfig,const PyConfig * config)1338 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
1339 {
1340     PyStatus status;
1341     PyCalculatePath calculate;
1342     memset(&calculate, 0, sizeof(calculate));
1343 
1344     status = calculate_init(&calculate, config);
1345     if (_PyStatus_EXCEPTION(status)) {
1346         goto done;
1347     }
1348 
1349     status = calculate_path(&calculate, pathconfig);
1350     if (_PyStatus_EXCEPTION(status)) {
1351         goto done;
1352     }
1353 
1354     status = _PyStatus_OK();
1355 
1356 done:
1357     calculate_free(&calculate);
1358     return status;
1359 }
1360 
1361 #ifdef __cplusplus
1362 }
1363 #endif
1364