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