1 /* Return the initial module search path. */
2 
3 #include "Python.h"
4 #include "internal/pystate.h"
5 #include "osdefs.h"
6 
7 #include <sys/types.h>
8 #include <string.h>
9 
10 #ifdef __APPLE__
11 #  include <mach-o/dyld.h>
12 #endif
13 
14 /* Search in some common locations for the associated Python libraries.
15  *
16  * Two directories must be found, the platform independent directory
17  * (prefix), containing the common .py and .pyc files, and the platform
18  * dependent directory (exec_prefix), containing the shared library
19  * modules.  Note that prefix and exec_prefix can be the same directory,
20  * but for some installations, they are different.
21  *
22  * Py_GetPath() carries out separate searches for prefix and exec_prefix.
23  * Each search tries a number of different locations until a ``landmark''
24  * file or directory is found.  If no prefix or exec_prefix is found, a
25  * warning message is issued and the preprocessor defined PREFIX and
26  * EXEC_PREFIX are used (even though they will not work); python carries on
27  * as best as is possible, but most imports will fail.
28  *
29  * Before any searches are done, the location of the executable is
30  * determined.  If argv[0] has one or more slashes in it, it is used
31  * unchanged.  Otherwise, it must have been invoked from the shell's path,
32  * so we search $PATH for the named executable and use that.  If the
33  * executable was not found on $PATH (or there was no $PATH environment
34  * variable), the original argv[0] string is used.
35  *
36  * Next, the executable location is examined to see if it is a symbolic
37  * link.  If so, the link is chased (correctly interpreting a relative
38  * pathname if one is found) and the directory of the link target is used.
39  *
40  * Finally, argv0_path is set to the directory containing the executable
41  * (i.e. the last component is stripped).
42  *
43  * With argv0_path in hand, we perform a number of steps.  The same steps
44  * are performed for prefix and for exec_prefix, but with a different
45  * landmark.
46  *
47  * Step 1. Are we running python out of the build directory?  This is
48  * checked by looking for a different kind of landmark relative to
49  * argv0_path.  For prefix, the landmark's path is derived from the VPATH
50  * preprocessor variable (taking into account that its value is almost, but
51  * not quite, what we need).  For exec_prefix, the landmark is
52  * pybuilddir.txt.  If the landmark is found, we're done.
53  *
54  * For the remaining steps, the prefix landmark will always be
55  * lib/python$VERSION/os.py and the exec_prefix will always be
56  * lib/python$VERSION/lib-dynload, where $VERSION is Python's version
57  * number as supplied by the Makefile.  Note that this means that no more
58  * build directory checking is performed; if the first step did not find
59  * the landmarks, the assumption is that python is running from an
60  * installed setup.
61  *
62  * Step 2. See if the $PYTHONHOME environment variable points to the
63  * installed location of the Python libraries.  If $PYTHONHOME is set, then
64  * it points to prefix and exec_prefix.  $PYTHONHOME can be a single
65  * directory, which is used for both, or the prefix and exec_prefix
66  * directories separated by a colon.
67  *
68  * Step 3. Try to find prefix and exec_prefix relative to argv0_path,
69  * backtracking up the path until it is exhausted.  This is the most common
70  * step to succeed.  Note that if prefix and exec_prefix are different,
71  * exec_prefix is more likely to be found; however if exec_prefix is a
72  * subdirectory of prefix, both will be found.
73  *
74  * Step 4. Search the directories pointed to by the preprocessor variables
75  * PREFIX and EXEC_PREFIX.  These are supplied by the Makefile but can be
76  * passed in as options to the configure script.
77  *
78  * That's it!
79  *
80  * Well, almost.  Once we have determined prefix and exec_prefix, the
81  * preprocessor variable PYTHONPATH is used to construct a path.  Each
82  * relative path on PYTHONPATH is prefixed with prefix.  Then the directory
83  * containing the shared library modules is appended.  The environment
84  * variable $PYTHONPATH is inserted in front of it all.  Finally, the
85  * prefix and exec_prefix globals are tweaked so they reflect the values
86  * expected by other code, by stripping the "lib/python$VERSION/..." stuff
87  * off.  If either points to the build directory, the globals are reset to
88  * the corresponding preprocessor variables (so sys.prefix will reflect the
89  * installation location, even though sys.path points into the build
90  * directory).  This seems to make more sense given that currently the only
91  * known use of sys.prefix and sys.exec_prefix is for the ILU installation
92  * process to find the installed Python tree.
93  *
94  * An embedding application can use Py_SetPath() to override all of
95  * these authomatic path computations.
96  *
97  * NOTE: Windows MSVC builds use PC/getpathp.c instead!
98  */
99 
100 #ifdef __cplusplus
101 extern "C" {
102 #endif
103 
104 
105 #if !defined(PREFIX) || !defined(EXEC_PREFIX) || !defined(VERSION) || !defined(VPATH)
106 #error "PREFIX, EXEC_PREFIX, VERSION, and VPATH must be constant defined"
107 #endif
108 
109 #ifndef LANDMARK
110 #define LANDMARK L"os.py"
111 #endif
112 
113 #define DECODE_LOCALE_ERR(NAME, LEN) \
114     ((LEN) == (size_t)-2) \
115      ? _Py_INIT_USER_ERR("cannot decode " NAME) \
116      : _Py_INIT_NO_MEMORY()
117 
118 typedef struct {
119     wchar_t *path_env;                 /* PATH environment variable */
120 
121     wchar_t *pythonpath;               /* PYTHONPATH define */
122     wchar_t *prefix;                   /* PREFIX define */
123     wchar_t *exec_prefix;              /* EXEC_PREFIX define */
124 
125     wchar_t *lib_python;               /* "lib/pythonX.Y" */
126     wchar_t argv0_path[MAXPATHLEN+1];
127     wchar_t zip_path[MAXPATHLEN+1];    /* ".../lib/pythonXY.zip" */
128 
129     int prefix_found;         /* found platform independent libraries? */
130     int exec_prefix_found;    /* found the platform dependent libraries? */
131 } PyCalculatePath;
132 
133 static const wchar_t delimiter[2] = {DELIM, '\0'};
134 static const wchar_t separator[2] = {SEP, '\0'};
135 
136 
137 /* Get file status. Encode the path to the locale encoding. */
138 static int
_Py_wstat(const wchar_t * path,struct stat * buf)139 _Py_wstat(const wchar_t* path, struct stat *buf)
140 {
141     int err;
142     char *fname;
143     fname = _Py_EncodeLocaleRaw(path, NULL);
144     if (fname == NULL) {
145         errno = EINVAL;
146         return -1;
147     }
148     err = stat(fname, buf);
149     PyMem_RawFree(fname);
150     return err;
151 }
152 
153 
154 static void
reduce(wchar_t * dir)155 reduce(wchar_t *dir)
156 {
157     size_t i = wcslen(dir);
158     while (i > 0 && dir[i] != SEP)
159         --i;
160     dir[i] = '\0';
161 }
162 
163 
164 static int
isfile(wchar_t * filename)165 isfile(wchar_t *filename)          /* Is file, not directory */
166 {
167     struct stat buf;
168     if (_Py_wstat(filename, &buf) != 0) {
169         return 0;
170     }
171     if (!S_ISREG(buf.st_mode)) {
172         return 0;
173     }
174     return 1;
175 }
176 
177 
178 static int
ismodule(wchar_t * filename)179 ismodule(wchar_t *filename)        /* Is module -- check for .pyc too */
180 {
181     if (isfile(filename)) {
182         return 1;
183     }
184 
185     /* Check for the compiled version of prefix. */
186     if (wcslen(filename) < MAXPATHLEN) {
187         wcscat(filename, L"c");
188         if (isfile(filename)) {
189             return 1;
190         }
191     }
192     return 0;
193 }
194 
195 
196 /* Is executable file */
197 static int
isxfile(wchar_t * filename)198 isxfile(wchar_t *filename)
199 {
200     struct stat buf;
201     if (_Py_wstat(filename, &buf) != 0) {
202         return 0;
203     }
204     if (!S_ISREG(buf.st_mode)) {
205         return 0;
206     }
207     if ((buf.st_mode & 0111) == 0) {
208         return 0;
209     }
210     return 1;
211 }
212 
213 
214 /* Is directory */
215 static int
isdir(wchar_t * filename)216 isdir(wchar_t *filename)
217 {
218     struct stat buf;
219     if (_Py_wstat(filename, &buf) != 0) {
220         return 0;
221     }
222     if (!S_ISDIR(buf.st_mode)) {
223         return 0;
224     }
225     return 1;
226 }
227 
228 
229 /* Add a path component, by appending stuff to buffer.
230    buffer must have at least MAXPATHLEN + 1 bytes allocated, and contain a
231    NUL-terminated string with no more than MAXPATHLEN characters (not counting
232    the trailing NUL).  It's a fatal error if it contains a string longer than
233    that (callers must be careful!).  If these requirements are met, it's
234    guaranteed that buffer will still be a NUL-terminated string with no more
235    than MAXPATHLEN characters at exit.  If stuff is too long, only as much of
236    stuff as fits will be appended.
237 */
238 static void
joinpath(wchar_t * buffer,wchar_t * stuff)239 joinpath(wchar_t *buffer, wchar_t *stuff)
240 {
241     size_t n, k;
242     if (stuff[0] == SEP) {
243         n = 0;
244     }
245     else {
246         n = wcslen(buffer);
247         if (n > 0 && buffer[n-1] != SEP && n < MAXPATHLEN) {
248             buffer[n++] = SEP;
249         }
250     }
251     if (n > MAXPATHLEN) {
252         Py_FatalError("buffer overflow in getpath.c's joinpath()");
253     }
254     k = wcslen(stuff);
255     if (n + k > MAXPATHLEN) {
256         k = MAXPATHLEN - n;
257     }
258     wcsncpy(buffer+n, stuff, k);
259     buffer[n+k] = '\0';
260 }
261 
262 
263 /* copy_absolute requires that path be allocated at least
264    MAXPATHLEN + 1 bytes and that p be no more than MAXPATHLEN bytes. */
265 static void
copy_absolute(wchar_t * path,wchar_t * p,size_t pathlen)266 copy_absolute(wchar_t *path, wchar_t *p, size_t pathlen)
267 {
268     if (p[0] == SEP) {
269         wcscpy(path, p);
270     }
271     else {
272         if (!_Py_wgetcwd(path, pathlen)) {
273             /* unable to get the current directory */
274             wcscpy(path, p);
275             return;
276         }
277         if (p[0] == '.' && p[1] == SEP) {
278             p += 2;
279         }
280         joinpath(path, p);
281     }
282 }
283 
284 
285 /* absolutize() requires that path be allocated at least MAXPATHLEN+1 bytes. */
286 static void
absolutize(wchar_t * path)287 absolutize(wchar_t *path)
288 {
289     wchar_t buffer[MAXPATHLEN+1];
290 
291     if (path[0] == SEP) {
292         return;
293     }
294     copy_absolute(buffer, path, MAXPATHLEN+1);
295     wcscpy(path, buffer);
296 }
297 
298 
299 #if defined(__CYGWIN__) || defined(__MINGW32__)
300 /* add_exe_suffix requires that progpath be allocated at least
301    MAXPATHLEN + 1 bytes.
302 */
303 
304 #ifndef EXE_SUFFIX
305 #define EXE_SUFFIX L".exe"
306 #endif
307 
308 static void
add_exe_suffix(wchar_t * progpath)309 add_exe_suffix(wchar_t *progpath)
310 {
311     /* Check for already have an executable suffix */
312     size_t n = wcslen(progpath);
313     size_t s = wcslen(EXE_SUFFIX);
314     if (wcsncasecmp(EXE_SUFFIX, progpath+n-s, s) != 0) {
315         if (n + s > MAXPATHLEN) {
316             Py_FatalError("progpath overflow in getpath.c's add_exe_suffix()");
317         }
318         /* Save original path for revert */
319         wchar_t orig[MAXPATHLEN+1];
320         wcsncpy(orig, progpath, MAXPATHLEN);
321 
322         wcsncpy(progpath+n, EXE_SUFFIX, s);
323         progpath[n+s] = '\0';
324 
325         if (!isxfile(progpath)) {
326             /* Path that added suffix is invalid */
327             wcsncpy(progpath, orig, MAXPATHLEN);
328         }
329     }
330 }
331 #endif
332 
333 
334 /* search_for_prefix requires that argv0_path be no more than MAXPATHLEN
335    bytes long.
336 */
337 static int
search_for_prefix(const _PyCoreConfig * core_config,PyCalculatePath * calculate,wchar_t * prefix)338 search_for_prefix(const _PyCoreConfig *core_config,
339                   PyCalculatePath *calculate, wchar_t *prefix)
340 {
341     size_t n;
342     wchar_t *vpath;
343 
344     /* If PYTHONHOME is set, we believe it unconditionally */
345     if (core_config->home) {
346         wcsncpy(prefix, core_config->home, MAXPATHLEN);
347         prefix[MAXPATHLEN] = L'\0';
348         wchar_t *delim = wcschr(prefix, DELIM);
349         if (delim) {
350             *delim = L'\0';
351         }
352         joinpath(prefix, calculate->lib_python);
353         joinpath(prefix, LANDMARK);
354         return 1;
355     }
356 
357     /* Check to see if argv[0] is in the build directory */
358     wcsncpy(prefix, calculate->argv0_path, MAXPATHLEN);
359     prefix[MAXPATHLEN] = L'\0';
360     joinpath(prefix, L"Modules/Setup");
361     if (isfile(prefix)) {
362         /* Check VPATH to see if argv0_path is in the build directory. */
363         vpath = Py_DecodeLocale(VPATH, NULL);
364         if (vpath != NULL) {
365             wcsncpy(prefix, calculate->argv0_path, MAXPATHLEN);
366             prefix[MAXPATHLEN] = L'\0';
367             joinpath(prefix, vpath);
368             PyMem_RawFree(vpath);
369             joinpath(prefix, L"Lib");
370             joinpath(prefix, LANDMARK);
371             if (ismodule(prefix)) {
372                 return -1;
373             }
374         }
375     }
376 
377     /* Search from argv0_path, until root is found */
378     copy_absolute(prefix, calculate->argv0_path, MAXPATHLEN+1);
379     do {
380         n = wcslen(prefix);
381         joinpath(prefix, calculate->lib_python);
382         joinpath(prefix, LANDMARK);
383         if (ismodule(prefix)) {
384             return 1;
385         }
386         prefix[n] = L'\0';
387         reduce(prefix);
388     } while (prefix[0]);
389 
390     /* Look at configure's PREFIX */
391     wcsncpy(prefix, calculate->prefix, MAXPATHLEN);
392     prefix[MAXPATHLEN] = L'\0';
393     joinpath(prefix, calculate->lib_python);
394     joinpath(prefix, LANDMARK);
395     if (ismodule(prefix)) {
396         return 1;
397     }
398 
399     /* Fail */
400     return 0;
401 }
402 
403 
404 static void
calculate_prefix(const _PyCoreConfig * core_config,PyCalculatePath * calculate,wchar_t * prefix)405 calculate_prefix(const _PyCoreConfig *core_config,
406                  PyCalculatePath *calculate, wchar_t *prefix)
407 {
408     calculate->prefix_found = search_for_prefix(core_config, calculate, prefix);
409     if (!calculate->prefix_found) {
410         if (!Py_FrozenFlag) {
411             fprintf(stderr,
412                 "Could not find platform independent libraries <prefix>\n");
413         }
414         wcsncpy(prefix, calculate->prefix, MAXPATHLEN);
415         joinpath(prefix, calculate->lib_python);
416     }
417     else {
418         reduce(prefix);
419     }
420 }
421 
422 
423 static void
calculate_reduce_prefix(PyCalculatePath * calculate,wchar_t * prefix)424 calculate_reduce_prefix(PyCalculatePath *calculate, wchar_t *prefix)
425 {
426     /* Reduce prefix and exec_prefix to their essence,
427      * e.g. /usr/local/lib/python1.5 is reduced to /usr/local.
428      * If we're loading relative to the build directory,
429      * return the compiled-in defaults instead.
430      */
431     if (calculate->prefix_found > 0) {
432         reduce(prefix);
433         reduce(prefix);
434         /* The prefix is the root directory, but reduce() chopped
435          * off the "/". */
436         if (!prefix[0]) {
437             wcscpy(prefix, separator);
438         }
439     }
440     else {
441         wcsncpy(prefix, calculate->prefix, MAXPATHLEN);
442     }
443 }
444 
445 
446 /* search_for_exec_prefix requires that argv0_path be no more than
447    MAXPATHLEN bytes long.
448 */
449 static int
search_for_exec_prefix(const _PyCoreConfig * core_config,PyCalculatePath * calculate,wchar_t * exec_prefix)450 search_for_exec_prefix(const _PyCoreConfig *core_config,
451                        PyCalculatePath *calculate, wchar_t *exec_prefix)
452 {
453     size_t n;
454 
455     /* If PYTHONHOME is set, we believe it unconditionally */
456     if (core_config->home) {
457         wchar_t *delim = wcschr(core_config->home, DELIM);
458         if (delim) {
459             wcsncpy(exec_prefix, delim+1, MAXPATHLEN);
460         }
461         else {
462             wcsncpy(exec_prefix, core_config->home, MAXPATHLEN);
463         }
464         exec_prefix[MAXPATHLEN] = L'\0';
465         joinpath(exec_prefix, calculate->lib_python);
466         joinpath(exec_prefix, L"lib-dynload");
467         return 1;
468     }
469 
470     /* Check to see if argv[0] is in the build directory. "pybuilddir.txt"
471        is written by setup.py and contains the relative path to the location
472        of shared library modules. */
473     wcsncpy(exec_prefix, calculate->argv0_path, MAXPATHLEN);
474     exec_prefix[MAXPATHLEN] = L'\0';
475     joinpath(exec_prefix, L"pybuilddir.txt");
476     if (isfile(exec_prefix)) {
477         FILE *f = _Py_wfopen(exec_prefix, L"rb");
478         if (f == NULL) {
479             errno = 0;
480         }
481         else {
482             char buf[MAXPATHLEN+1];
483             wchar_t *rel_builddir_path;
484             n = fread(buf, 1, MAXPATHLEN, f);
485             buf[n] = '\0';
486             fclose(f);
487             rel_builddir_path = _Py_DecodeUTF8_surrogateescape(buf, n);
488             if (rel_builddir_path) {
489                 wcsncpy(exec_prefix, calculate->argv0_path, MAXPATHLEN);
490                 exec_prefix[MAXPATHLEN] = L'\0';
491                 joinpath(exec_prefix, rel_builddir_path);
492                 PyMem_RawFree(rel_builddir_path );
493                 return -1;
494             }
495         }
496     }
497 
498     /* Search from argv0_path, until root is found */
499     copy_absolute(exec_prefix, calculate->argv0_path, MAXPATHLEN+1);
500     do {
501         n = wcslen(exec_prefix);
502         joinpath(exec_prefix, calculate->lib_python);
503         joinpath(exec_prefix, L"lib-dynload");
504         if (isdir(exec_prefix)) {
505             return 1;
506         }
507         exec_prefix[n] = L'\0';
508         reduce(exec_prefix);
509     } while (exec_prefix[0]);
510 
511     /* Look at configure's EXEC_PREFIX */
512     wcsncpy(exec_prefix, calculate->exec_prefix, MAXPATHLEN);
513     exec_prefix[MAXPATHLEN] = L'\0';
514     joinpath(exec_prefix, calculate->lib_python);
515     joinpath(exec_prefix, L"lib-dynload");
516     if (isdir(exec_prefix)) {
517         return 1;
518     }
519 
520     /* Fail */
521     return 0;
522 }
523 
524 
525 static void
calculate_exec_prefix(const _PyCoreConfig * core_config,PyCalculatePath * calculate,wchar_t * exec_prefix)526 calculate_exec_prefix(const _PyCoreConfig *core_config,
527                       PyCalculatePath *calculate, wchar_t *exec_prefix)
528 {
529     calculate->exec_prefix_found = search_for_exec_prefix(core_config,
530                                                           calculate,
531                                                           exec_prefix);
532     if (!calculate->exec_prefix_found) {
533         if (!Py_FrozenFlag) {
534             fprintf(stderr,
535                 "Could not find platform dependent libraries <exec_prefix>\n");
536         }
537         wcsncpy(exec_prefix, calculate->exec_prefix, MAXPATHLEN);
538         joinpath(exec_prefix, L"lib/lib-dynload");
539     }
540     /* If we found EXEC_PREFIX do *not* reduce it!  (Yet.) */
541 }
542 
543 
544 static void
calculate_reduce_exec_prefix(PyCalculatePath * calculate,wchar_t * exec_prefix)545 calculate_reduce_exec_prefix(PyCalculatePath *calculate, wchar_t *exec_prefix)
546 {
547     if (calculate->exec_prefix_found > 0) {
548         reduce(exec_prefix);
549         reduce(exec_prefix);
550         reduce(exec_prefix);
551         if (!exec_prefix[0]) {
552             wcscpy(exec_prefix, separator);
553         }
554     }
555     else {
556         wcsncpy(exec_prefix, calculate->exec_prefix, MAXPATHLEN);
557     }
558 }
559 
560 
561 static _PyInitError
calculate_program_full_path(const _PyCoreConfig * core_config,PyCalculatePath * calculate,_PyPathConfig * config)562 calculate_program_full_path(const _PyCoreConfig *core_config,
563                             PyCalculatePath *calculate, _PyPathConfig *config)
564 {
565     wchar_t program_full_path[MAXPATHLEN+1];
566     memset(program_full_path, 0, sizeof(program_full_path));
567 
568 #ifdef __APPLE__
569     uint32_t nsexeclength = MAXPATHLEN;
570     char execpath[MAXPATHLEN+1];
571 #endif
572 
573     /* If there is no slash in the argv0 path, then we have to
574      * assume python is on the user's $PATH, since there's no
575      * other way to find a directory to start the search from.  If
576      * $PATH isn't exported, you lose.
577      */
578     if (wcschr(core_config->program_name, SEP)) {
579         wcsncpy(program_full_path, core_config->program_name, MAXPATHLEN);
580     }
581 #ifdef __APPLE__
582      /* On Mac OS X, if a script uses an interpreter of the form
583       * "#!/opt/python2.3/bin/python", the kernel only passes "python"
584       * as argv[0], which falls through to the $PATH search below.
585       * If /opt/python2.3/bin isn't in your path, or is near the end,
586       * this algorithm may incorrectly find /usr/bin/python. To work
587       * around this, we can use _NSGetExecutablePath to get a better
588       * hint of what the intended interpreter was, although this
589       * will fail if a relative path was used. but in that case,
590       * absolutize() should help us out below
591       */
592     else if(0 == _NSGetExecutablePath(execpath, &nsexeclength) &&
593             execpath[0] == SEP)
594     {
595         size_t len;
596         wchar_t *path = Py_DecodeLocale(execpath, &len);
597         if (path == NULL) {
598             return DECODE_LOCALE_ERR("executable path", len);
599         }
600         wcsncpy(program_full_path, path, MAXPATHLEN);
601         PyMem_RawFree(path);
602     }
603 #endif /* __APPLE__ */
604     else if (calculate->path_env) {
605         wchar_t *path = calculate->path_env;
606         while (1) {
607             wchar_t *delim = wcschr(path, DELIM);
608 
609             if (delim) {
610                 size_t len = delim - path;
611                 if (len > MAXPATHLEN) {
612                     len = MAXPATHLEN;
613                 }
614                 wcsncpy(program_full_path, path, len);
615                 program_full_path[len] = '\0';
616             }
617             else {
618                 wcsncpy(program_full_path, path, MAXPATHLEN);
619             }
620 
621             joinpath(program_full_path, core_config->program_name);
622             if (isxfile(program_full_path)) {
623                 break;
624             }
625 
626             if (!delim) {
627                 program_full_path[0] = L'\0';
628                 break;
629             }
630             path = delim + 1;
631         }
632     }
633     else {
634         program_full_path[0] = '\0';
635     }
636     if (program_full_path[0] != SEP && program_full_path[0] != '\0') {
637         absolutize(program_full_path);
638     }
639 #if defined(__CYGWIN__) || defined(__MINGW32__)
640     /* For these platforms it is necessary to ensure that the .exe suffix
641      * is appended to the filename, otherwise there is potential for
642      * sys.executable to return the name of a directory under the same
643      * path (bpo-28441).
644      */
645     if (program_full_path[0] != '\0') {
646         add_exe_suffix(program_full_path);
647     }
648 #endif
649 
650     config->program_full_path = _PyMem_RawWcsdup(program_full_path);
651     if (config->program_full_path == NULL) {
652         return _Py_INIT_NO_MEMORY();
653     }
654     return _Py_INIT_OK();
655 }
656 
657 
658 static _PyInitError
calculate_argv0_path(PyCalculatePath * calculate,const wchar_t * program_full_path)659 calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path)
660 {
661     wcsncpy(calculate->argv0_path, program_full_path, MAXPATHLEN);
662     calculate->argv0_path[MAXPATHLEN] = '\0';
663 
664 #ifdef WITH_NEXT_FRAMEWORK
665     NSModule pythonModule;
666 
667     /* On Mac OS X we have a special case if we're running from a framework.
668     ** This is because the python home should be set relative to the library,
669     ** which is in the framework, not relative to the executable, which may
670     ** be outside of the framework. Except when we're in the build directory...
671     */
672     pythonModule = NSModuleForSymbol(NSLookupAndBindSymbol("_Py_Initialize"));
673     /* Use dylib functions to find out where the framework was loaded from */
674     const char* modPath = NSLibraryNameForModule(pythonModule);
675     if (modPath != NULL) {
676         /* We're in a framework. */
677         /* See if we might be in the build directory. The framework in the
678         ** build directory is incomplete, it only has the .dylib and a few
679         ** needed symlinks, it doesn't have the Lib directories and such.
680         ** If we're running with the framework from the build directory we must
681         ** be running the interpreter in the build directory, so we use the
682         ** build-directory-specific logic to find Lib and such.
683         */
684         size_t len;
685         wchar_t* wbuf = Py_DecodeLocale(modPath, &len);
686         if (wbuf == NULL) {
687             return DECODE_LOCALE_ERR("framework location", len);
688         }
689 
690         wcsncpy(calculate->argv0_path, wbuf, MAXPATHLEN);
691         reduce(calculate->argv0_path);
692         joinpath(calculate->argv0_path, calculate->lib_python);
693         joinpath(calculate->argv0_path, LANDMARK);
694         if (!ismodule(calculate->argv0_path)) {
695             /* We are in the build directory so use the name of the
696                executable - we know that the absolute path is passed */
697             wcsncpy(calculate->argv0_path, program_full_path, MAXPATHLEN);
698         }
699         else {
700             /* Use the location of the library as the program_full_path */
701             wcsncpy(calculate->argv0_path, wbuf, MAXPATHLEN);
702         }
703         PyMem_RawFree(wbuf);
704     }
705 #endif
706 
707 #if HAVE_READLINK
708     wchar_t tmpbuffer[MAXPATHLEN+1];
709     int linklen = _Py_wreadlink(program_full_path, tmpbuffer, MAXPATHLEN);
710     while (linklen != -1) {
711         if (tmpbuffer[0] == SEP) {
712             /* tmpbuffer should never be longer than MAXPATHLEN,
713                but extra check does not hurt */
714             wcsncpy(calculate->argv0_path, tmpbuffer, MAXPATHLEN);
715         }
716         else {
717             /* Interpret relative to program_full_path */
718             reduce(calculate->argv0_path);
719             joinpath(calculate->argv0_path, tmpbuffer);
720         }
721         linklen = _Py_wreadlink(calculate->argv0_path, tmpbuffer, MAXPATHLEN);
722     }
723 #endif /* HAVE_READLINK */
724 
725     reduce(calculate->argv0_path);
726     /* At this point, argv0_path is guaranteed to be less than
727        MAXPATHLEN bytes long. */
728     return _Py_INIT_OK();
729 }
730 
731 
732 /* Search for an "pyvenv.cfg" environment configuration file, first in the
733    executable's directory and then in the parent directory.
734    If found, open it for use when searching for prefixes.
735 */
736 static void
calculate_read_pyenv(PyCalculatePath * calculate)737 calculate_read_pyenv(PyCalculatePath *calculate)
738 {
739     wchar_t tmpbuffer[MAXPATHLEN+1];
740     wchar_t *env_cfg = L"pyvenv.cfg";
741     FILE *env_file;
742 
743     wcscpy(tmpbuffer, calculate->argv0_path);
744 
745     joinpath(tmpbuffer, env_cfg);
746     env_file = _Py_wfopen(tmpbuffer, L"r");
747     if (env_file == NULL) {
748         errno = 0;
749 
750         reduce(tmpbuffer);
751         reduce(tmpbuffer);
752         joinpath(tmpbuffer, env_cfg);
753 
754         env_file = _Py_wfopen(tmpbuffer, L"r");
755         if (env_file == NULL) {
756             errno = 0;
757         }
758     }
759 
760     if (env_file == NULL) {
761         return;
762     }
763 
764     /* Look for a 'home' variable and set argv0_path to it, if found */
765     if (_Py_FindEnvConfigValue(env_file, L"home", tmpbuffer, MAXPATHLEN)) {
766         wcscpy(calculate->argv0_path, tmpbuffer);
767     }
768     fclose(env_file);
769 }
770 
771 
772 static void
calculate_zip_path(PyCalculatePath * calculate,const wchar_t * prefix)773 calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix)
774 {
775     wcsncpy(calculate->zip_path, prefix, MAXPATHLEN);
776     calculate->zip_path[MAXPATHLEN] = L'\0';
777 
778     if (calculate->prefix_found > 0) {
779         /* Use the reduced prefix returned by Py_GetPrefix() */
780         reduce(calculate->zip_path);
781         reduce(calculate->zip_path);
782     }
783     else {
784         wcsncpy(calculate->zip_path, calculate->prefix, MAXPATHLEN);
785     }
786     joinpath(calculate->zip_path, L"lib/python00.zip");
787 
788     /* Replace "00" with version */
789     size_t bufsz = wcslen(calculate->zip_path);
790     calculate->zip_path[bufsz - 6] = VERSION[0];
791     calculate->zip_path[bufsz - 5] = VERSION[2];
792 }
793 
794 
795 static _PyInitError
calculate_module_search_path(const _PyCoreConfig * core_config,PyCalculatePath * calculate,const wchar_t * prefix,const wchar_t * exec_prefix,_PyPathConfig * config)796 calculate_module_search_path(const _PyCoreConfig *core_config,
797                              PyCalculatePath *calculate,
798                              const wchar_t *prefix, const wchar_t *exec_prefix,
799                              _PyPathConfig *config)
800 {
801     /* Calculate size of return buffer */
802     size_t bufsz = 0;
803     if (core_config->module_search_path_env != NULL) {
804         bufsz += wcslen(core_config->module_search_path_env) + 1;
805     }
806 
807     wchar_t *defpath = calculate->pythonpath;
808     size_t prefixsz = wcslen(prefix) + 1;
809     while (1) {
810         wchar_t *delim = wcschr(defpath, DELIM);
811 
812         if (defpath[0] != SEP) {
813             /* Paths are relative to prefix */
814             bufsz += prefixsz;
815         }
816 
817         if (delim) {
818             bufsz += delim - defpath + 1;
819         }
820         else {
821             bufsz += wcslen(defpath) + 1;
822             break;
823         }
824         defpath = delim + 1;
825     }
826 
827     bufsz += wcslen(calculate->zip_path) + 1;
828     bufsz += wcslen(exec_prefix) + 1;
829 
830     /* Allocate the buffer */
831     wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
832     if (buf == NULL) {
833         return _Py_INIT_NO_MEMORY();
834     }
835     buf[0] = '\0';
836 
837     /* Run-time value of $PYTHONPATH goes first */
838     if (core_config->module_search_path_env) {
839         wcscpy(buf, core_config->module_search_path_env);
840         wcscat(buf, delimiter);
841     }
842 
843     /* Next is the default zip path */
844     wcscat(buf, calculate->zip_path);
845     wcscat(buf, delimiter);
846 
847     /* Next goes merge of compile-time $PYTHONPATH with
848      * dynamically located prefix.
849      */
850     defpath = calculate->pythonpath;
851     while (1) {
852         wchar_t *delim = wcschr(defpath, DELIM);
853 
854         if (defpath[0] != SEP) {
855             wcscat(buf, prefix);
856             if (prefixsz >= 2 && prefix[prefixsz - 2] != SEP &&
857                 defpath[0] != (delim ? DELIM : L'\0'))
858             {
859                 /* not empty */
860                 wcscat(buf, separator);
861             }
862         }
863 
864         if (delim) {
865             size_t len = delim - defpath + 1;
866             size_t end = wcslen(buf) + len;
867             wcsncat(buf, defpath, len);
868             buf[end] = '\0';
869         }
870         else {
871             wcscat(buf, defpath);
872             break;
873         }
874         defpath = delim + 1;
875     }
876     wcscat(buf, delimiter);
877 
878     /* Finally, on goes the directory for dynamic-load modules */
879     wcscat(buf, exec_prefix);
880 
881     config->module_search_path = buf;
882     return _Py_INIT_OK();
883 }
884 
885 
886 static _PyInitError
calculate_init(PyCalculatePath * calculate,const _PyCoreConfig * core_config)887 calculate_init(PyCalculatePath *calculate,
888                const _PyCoreConfig *core_config)
889 {
890     size_t len;
891     const char *path = getenv("PATH");
892     if (path) {
893         calculate->path_env = Py_DecodeLocale(path, &len);
894         if (!calculate->path_env) {
895             return DECODE_LOCALE_ERR("PATH environment variable", len);
896         }
897     }
898 
899     calculate->pythonpath = Py_DecodeLocale(PYTHONPATH, &len);
900     if (!calculate->pythonpath) {
901         return DECODE_LOCALE_ERR("PYTHONPATH define", len);
902     }
903     calculate->prefix = Py_DecodeLocale(PREFIX, &len);
904     if (!calculate->prefix) {
905         return DECODE_LOCALE_ERR("PREFIX define", len);
906     }
907     calculate->exec_prefix = Py_DecodeLocale(EXEC_PREFIX, &len);
908     if (!calculate->exec_prefix) {
909         return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
910     }
911     calculate->lib_python = Py_DecodeLocale("lib/python" VERSION, &len);
912     if (!calculate->lib_python) {
913         return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
914     }
915     return _Py_INIT_OK();
916 }
917 
918 
919 static void
calculate_free(PyCalculatePath * calculate)920 calculate_free(PyCalculatePath *calculate)
921 {
922     PyMem_RawFree(calculate->pythonpath);
923     PyMem_RawFree(calculate->prefix);
924     PyMem_RawFree(calculate->exec_prefix);
925     PyMem_RawFree(calculate->lib_python);
926     PyMem_RawFree(calculate->path_env);
927 }
928 
929 
930 static _PyInitError
calculate_path_impl(const _PyCoreConfig * core_config,PyCalculatePath * calculate,_PyPathConfig * config)931 calculate_path_impl(const _PyCoreConfig *core_config,
932                     PyCalculatePath *calculate, _PyPathConfig *config)
933 {
934     _PyInitError err;
935 
936     err = calculate_program_full_path(core_config, calculate, config);
937     if (_Py_INIT_FAILED(err)) {
938         return err;
939     }
940 
941     err = calculate_argv0_path(calculate, config->program_full_path);
942     if (_Py_INIT_FAILED(err)) {
943         return err;
944     }
945 
946     calculate_read_pyenv(calculate);
947 
948     wchar_t prefix[MAXPATHLEN+1];
949     memset(prefix, 0, sizeof(prefix));
950     calculate_prefix(core_config, calculate, prefix);
951 
952     calculate_zip_path(calculate, prefix);
953 
954     wchar_t exec_prefix[MAXPATHLEN+1];
955     memset(exec_prefix, 0, sizeof(exec_prefix));
956     calculate_exec_prefix(core_config, calculate, exec_prefix);
957 
958     if ((!calculate->prefix_found || !calculate->exec_prefix_found) &&
959         !Py_FrozenFlag)
960     {
961         fprintf(stderr,
962                 "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
963     }
964 
965     err = calculate_module_search_path(core_config, calculate,
966                                        prefix, exec_prefix, config);
967     if (_Py_INIT_FAILED(err)) {
968         return err;
969     }
970 
971     calculate_reduce_prefix(calculate, prefix);
972 
973     config->prefix = _PyMem_RawWcsdup(prefix);
974     if (config->prefix == NULL) {
975         return _Py_INIT_NO_MEMORY();
976     }
977 
978     calculate_reduce_exec_prefix(calculate, exec_prefix);
979 
980     config->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
981     if (config->exec_prefix == NULL) {
982         return _Py_INIT_NO_MEMORY();
983     }
984 
985     return _Py_INIT_OK();
986 }
987 
988 
989 _PyInitError
_PyPathConfig_Calculate(_PyPathConfig * config,const _PyCoreConfig * core_config)990 _PyPathConfig_Calculate(_PyPathConfig *config, const _PyCoreConfig *core_config)
991 {
992     PyCalculatePath calculate;
993     memset(&calculate, 0, sizeof(calculate));
994 
995     _PyInitError err = calculate_init(&calculate, core_config);
996     if (_Py_INIT_FAILED(err)) {
997         goto done;
998     }
999 
1000     err = calculate_path_impl(core_config, &calculate, config);
1001     if (_Py_INIT_FAILED(err)) {
1002         goto done;
1003     }
1004 
1005     err = _Py_INIT_OK();
1006 
1007 done:
1008     calculate_free(&calculate);
1009     return err;
1010 }
1011 
1012 #ifdef __cplusplus
1013 }
1014 #endif
1015