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