Coverage Report

Created: 2026-06-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/Python-3.8.3/Modules/getpath.c
Line
Count
Source
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
0
#define LANDMARK L"os.py"
114
#endif
115
116
#define DECODE_LOCALE_ERR(NAME, LEN) \
117
0
    ((LEN) == (size_t)-2) \
118
0
     ? _PyStatus_ERR("cannot decode " NAME) \
119
0
     : _PyStatus_NO_MEMORY()
120
121
0
#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
145
_Py_wstat(const wchar_t* path, struct stat *buf)
146
26
{
147
26
    int err;
148
26
    char *fname;
149
26
    fname = _Py_EncodeLocaleRaw(path, NULL);
150
26
    if (fname == NULL) {
151
0
        errno = EINVAL;
152
0
        return -1;
153
0
    }
154
26
    err = stat(fname, buf);
155
26
    PyMem_RawFree(fname);
156
26
    return err;
157
26
}
158
159
160
static void
161
reduce(wchar_t *dir)
162
130
{
163
130
    size_t i = wcslen(dir);
164
1.17k
    while (i > 0 && dir[i] != SEP) {
165
1.04k
        --i;
166
1.04k
    }
167
130
    dir[i] = '\0';
168
130
}
169
170
171
/* Is file, not directory */
172
static int
173
isfile(const wchar_t *filename)
174
0
{
175
0
    struct stat buf;
176
0
    if (_Py_wstat(filename, &buf) != 0) {
177
0
        return 0;
178
0
    }
179
0
    if (!S_ISREG(buf.st_mode)) {
180
0
        return 0;
181
0
    }
182
0
    return 1;
183
0
}
184
185
186
/* Is module -- check for .pyc too */
187
static int
188
ismodule(wchar_t *filename, size_t filename_len)
189
0
{
190
0
    if (isfile(filename)) {
191
0
        return 1;
192
0
    }
193
194
    /* Check for the compiled version of prefix. */
195
0
    if (wcslen(filename) + 2 <= filename_len) {
196
0
        wcscat(filename, L"c");
197
0
        if (isfile(filename)) {
198
0
            return 1;
199
0
        }
200
0
    }
201
0
    return 0;
202
0
}
203
204
205
/* Is executable file */
206
static int
207
isxfile(const wchar_t *filename)
208
26
{
209
26
    struct stat buf;
210
26
    if (_Py_wstat(filename, &buf) != 0) {
211
13
        return 0;
212
13
    }
213
13
    if (!S_ISREG(buf.st_mode)) {
214
0
        return 0;
215
0
    }
216
13
    if ((buf.st_mode & 0111) == 0) {
217
0
        return 0;
218
0
    }
219
13
    return 1;
220
13
}
221
222
223
/* Is directory */
224
static int
225
isdir(wchar_t *filename)
226
0
{
227
0
    struct stat buf;
228
0
    if (_Py_wstat(filename, &buf) != 0) {
229
0
        return 0;
230
0
    }
231
0
    if (!S_ISDIR(buf.st_mode)) {
232
0
        return 0;
233
0
    }
234
0
    return 1;
235
0
}
236
237
238
/* Add a path component, by appending stuff to buffer.
239
   buflen: 'buffer' length in characters including trailing NUL. */
240
static PyStatus
241
joinpath(wchar_t *buffer, const wchar_t *stuff, size_t buflen)
242
104
{
243
104
    size_t n, k;
244
104
    if (stuff[0] != SEP) {
245
104
        n = wcslen(buffer);
246
104
        if (n >= buflen) {
247
0
            return PATHLEN_ERR();
248
0
        }
249
250
104
        if (n > 0 && buffer[n-1] != SEP) {
251
104
            buffer[n++] = SEP;
252
104
        }
253
104
    }
254
0
    else {
255
0
        n = 0;
256
0
    }
257
258
104
    k = wcslen(stuff);
259
104
    if (n + k >= buflen) {
260
0
        return PATHLEN_ERR();
261
0
    }
262
104
    wcsncpy(buffer+n, stuff, k);
263
104
    buffer[n+k] = '\0';
264
265
104
    return _PyStatus_OK();
266
104
}
267
268
269
static inline int
270
safe_wcscpy(wchar_t *dst, const wchar_t *src, size_t n)
271
78
{
272
78
    size_t srclen = wcslen(src);
273
78
    if (n <= srclen) {
274
0
        dst[0] = L'\0';
275
0
        return -1;
276
0
    }
277
78
    memcpy(dst, src, (srclen + 1) * sizeof(wchar_t));
278
78
    return 0;
279
78
}
280
281
282
/* copy_absolute requires that path be allocated at least
283
   'pathlen' characters (including trailing NUL). */
284
static PyStatus
285
copy_absolute(wchar_t *path, const wchar_t *p, size_t pathlen)
286
0
{
287
0
    if (p[0] == SEP) {
288
0
        if (safe_wcscpy(path, p, pathlen) < 0) {
289
0
            return PATHLEN_ERR();
290
0
        }
291
0
    }
292
0
    else {
293
0
        if (!_Py_wgetcwd(path, pathlen)) {
294
            /* unable to get the current directory */
295
0
            if (safe_wcscpy(path, p, pathlen) < 0) {
296
0
                return PATHLEN_ERR();
297
0
            }
298
0
            return _PyStatus_OK();
299
0
        }
300
0
        if (p[0] == '.' && p[1] == SEP) {
301
0
            p += 2;
302
0
        }
303
0
        PyStatus status = joinpath(path, p, pathlen);
304
0
        if (_PyStatus_EXCEPTION(status)) {
305
0
            return status;
306
0
        }
307
0
    }
308
0
    return _PyStatus_OK();
309
0
}
310
311
312
/* path_len: path length in characters including trailing NUL */
313
static PyStatus
314
absolutize(wchar_t *path, size_t path_len)
315
0
{
316
0
    if (path[0] == SEP) {
317
0
        return _PyStatus_OK();
318
0
    }
319
320
0
    wchar_t abs_path[MAXPATHLEN+1];
321
0
    PyStatus status = copy_absolute(abs_path, path, Py_ARRAY_LENGTH(abs_path));
322
0
    if (_PyStatus_EXCEPTION(status)) {
323
0
        return status;
324
0
    }
325
326
0
    if (safe_wcscpy(path, abs_path, path_len) < 0) {
327
0
        return PATHLEN_ERR();
328
0
    }
329
0
    return _PyStatus_OK();
330
0
}
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
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
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
13
{
373
13
    wchar_t path[MAXPATHLEN+1];
374
13
    memset(path, 0, sizeof(path));
375
13
    size_t path_len = Py_ARRAY_LENGTH(path);
376
377
13
    PyStatus status;
378
379
    /* If PYTHONHOME is set, we believe it unconditionally */
380
13
    if (pathconfig->home) {
381
        /* Path: <home> / <lib_python> */
382
13
        if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) {
383
0
            return PATHLEN_ERR();
384
0
        }
385
13
        wchar_t *delim = wcschr(prefix, DELIM);
386
13
        if (delim) {
387
0
            *delim = L'\0';
388
0
        }
389
13
        status = joinpath(prefix, calculate->lib_python, prefix_len);
390
13
        if (_PyStatus_EXCEPTION(status)) {
391
0
            return status;
392
0
        }
393
13
        *found = 1;
394
13
        return _PyStatus_OK();
395
13
    }
396
397
    /* Check to see if argv[0] is in the build directory */
398
0
    if (safe_wcscpy(path, argv0_path, path_len) < 0) {
399
0
        return PATHLEN_ERR();
400
0
    }
401
0
    status = joinpath(path, L"Modules/Setup.local", path_len);
402
0
    if (_PyStatus_EXCEPTION(status)) {
403
0
        return status;
404
0
    }
405
406
0
    if (isfile(path)) {
407
        /* Check VPATH to see if argv0_path is in the build directory.
408
           VPATH can be empty. */
409
0
        wchar_t *vpath = Py_DecodeLocale(VPATH, NULL);
410
0
        if (vpath != NULL) {
411
            /* Path: <argv0_path> / <vpath> / Lib / LANDMARK */
412
0
            if (safe_wcscpy(prefix, argv0_path, prefix_len) < 0) {
413
0
                return PATHLEN_ERR();
414
0
            }
415
0
            status = joinpath(prefix, vpath, prefix_len);
416
0
            PyMem_RawFree(vpath);
417
0
            if (_PyStatus_EXCEPTION(status)) {
418
0
                return status;
419
0
            }
420
421
0
            status = joinpath(prefix, L"Lib", prefix_len);
422
0
            if (_PyStatus_EXCEPTION(status)) {
423
0
                return status;
424
0
            }
425
0
            status = joinpath(prefix, LANDMARK, prefix_len);
426
0
            if (_PyStatus_EXCEPTION(status)) {
427
0
                return status;
428
0
            }
429
430
0
            if (ismodule(prefix, prefix_len)) {
431
0
                *found = -1;
432
0
                reduce(prefix);
433
0
                return _PyStatus_OK();
434
0
            }
435
0
        }
436
0
    }
437
438
    /* Search from argv0_path, until root is found */
439
0
    status = copy_absolute(prefix, argv0_path, prefix_len);
440
0
    if (_PyStatus_EXCEPTION(status)) {
441
0
        return status;
442
0
    }
443
444
0
    do {
445
        /* Path: <argv0_path or substring> / <lib_python> / LANDMARK */
446
0
        size_t n = wcslen(prefix);
447
0
        status = joinpath(prefix, calculate->lib_python, prefix_len);
448
0
        if (_PyStatus_EXCEPTION(status)) {
449
0
            return status;
450
0
        }
451
0
        status = joinpath(prefix, LANDMARK, prefix_len);
452
0
        if (_PyStatus_EXCEPTION(status)) {
453
0
            return status;
454
0
        }
455
456
0
        if (ismodule(prefix, prefix_len)) {
457
0
            *found = 1;
458
0
            reduce(prefix);
459
0
            return _PyStatus_OK();
460
0
        }
461
0
        prefix[n] = L'\0';
462
0
        reduce(prefix);
463
0
    } while (prefix[0]);
464
465
    /* Look at configure's PREFIX.
466
       Path: <PREFIX macro> / <lib_python> / LANDMARK */
467
0
    if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
468
0
        return PATHLEN_ERR();
469
0
    }
470
0
    status = joinpath(prefix, calculate->lib_python, prefix_len);
471
0
    if (_PyStatus_EXCEPTION(status)) {
472
0
        return status;
473
0
    }
474
0
    status = joinpath(prefix, LANDMARK, prefix_len);
475
0
    if (_PyStatus_EXCEPTION(status)) {
476
0
        return status;
477
0
    }
478
479
0
    if (ismodule(prefix, prefix_len)) {
480
0
        *found = 1;
481
0
        reduce(prefix);
482
0
        return _PyStatus_OK();
483
0
    }
484
485
    /* Fail */
486
0
    *found = 0;
487
0
    return _PyStatus_OK();
488
0
}
489
490
491
static PyStatus
492
calculate_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
493
                 const wchar_t *argv0_path,
494
                 wchar_t *prefix, size_t prefix_len)
495
13
{
496
13
    PyStatus status;
497
498
13
    status = search_for_prefix(calculate, pathconfig, argv0_path,
499
13
                               prefix, prefix_len,
500
13
                               &calculate->prefix_found);
501
13
    if (_PyStatus_EXCEPTION(status)) {
502
0
        return status;
503
0
    }
504
505
13
    if (!calculate->prefix_found) {
506
0
        if (calculate->warnings) {
507
0
            fprintf(stderr,
508
0
                "Could not find platform independent libraries <prefix>\n");
509
0
        }
510
0
        if (safe_wcscpy(prefix, calculate->prefix, prefix_len) < 0) {
511
0
            return PATHLEN_ERR();
512
0
        }
513
0
        status = joinpath(prefix, calculate->lib_python, prefix_len);
514
0
        if (_PyStatus_EXCEPTION(status)) {
515
0
            return status;
516
0
        }
517
0
    }
518
13
    return _PyStatus_OK();
519
13
}
520
521
522
static PyStatus
523
calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
524
                     wchar_t *prefix)
525
13
{
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
13
    if (calculate->prefix_found > 0) {
532
13
        reduce(prefix);
533
13
        reduce(prefix);
534
        /* The prefix is the root directory, but reduce() chopped
535
         * off the "/". */
536
13
        if (!prefix[0]) {
537
0
            wcscpy(prefix, separator);
538
0
        }
539
13
        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
540
13
    }
541
0
    else {
542
0
        pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix);
543
0
    }
544
545
13
    if (pathconfig->prefix == NULL) {
546
0
        return _PyStatus_NO_MEMORY();
547
0
    }
548
13
    return _PyStatus_OK();
549
13
}
550
551
552
static PyStatus
553
calculate_pybuilddir(const wchar_t *argv0_path,
554
                     wchar_t *exec_prefix, size_t exec_prefix_len,
555
                     int *found)
556
0
{
557
0
    PyStatus status;
558
559
0
    wchar_t filename[MAXPATHLEN+1];
560
0
    memset(filename, 0, sizeof(filename));
561
0
    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
0
    if (safe_wcscpy(filename, argv0_path, filename_len) < 0) {
569
0
        return PATHLEN_ERR();
570
0
    }
571
0
    status = joinpath(filename, L"pybuilddir.txt", filename_len);
572
0
    if (_PyStatus_EXCEPTION(status)) {
573
0
        return status;
574
0
    }
575
576
0
    if (!isfile(filename)) {
577
0
        return _PyStatus_OK();
578
0
    }
579
580
0
    FILE *fp = _Py_wfopen(filename, L"rb");
581
0
    if (fp == NULL) {
582
0
        errno = 0;
583
0
        return _PyStatus_OK();
584
0
    }
585
586
0
    char buf[MAXPATHLEN + 1];
587
0
    size_t n = fread(buf, 1, Py_ARRAY_LENGTH(buf) - 1, fp);
588
0
    buf[n] = '\0';
589
0
    fclose(fp);
590
591
0
    size_t dec_len;
592
0
    wchar_t *pybuilddir = _Py_DecodeUTF8_surrogateescape(buf, n, &dec_len);
593
0
    if (!pybuilddir) {
594
0
        return DECODE_LOCALE_ERR("pybuilddir.txt", dec_len);
595
0
    }
596
597
    /* Path: <argv0_path> / <pybuilddir content> */
598
0
    if (safe_wcscpy(exec_prefix, argv0_path, exec_prefix_len) < 0) {
599
0
        PyMem_RawFree(pybuilddir);
600
0
        return PATHLEN_ERR();
601
0
    }
602
0
    status = joinpath(exec_prefix, pybuilddir, exec_prefix_len);
603
0
    PyMem_RawFree(pybuilddir);
604
0
    if (_PyStatus_EXCEPTION(status)) {
605
0
        return status;
606
0
    }
607
608
0
    *found = -1;
609
0
    return _PyStatus_OK();
610
0
}
611
612
613
/* search_for_exec_prefix requires that argv0_path be no more than
614
   MAXPATHLEN bytes long.
615
*/
616
static PyStatus
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
13
{
622
13
    PyStatus status;
623
624
    /* If PYTHONHOME is set, we believe it unconditionally */
625
13
    if (pathconfig->home) {
626
        /* Path: <home> / <lib_python> / "lib-dynload" */
627
13
        wchar_t *delim = wcschr(pathconfig->home, DELIM);
628
13
        if (delim) {
629
0
            if (safe_wcscpy(exec_prefix, delim+1, exec_prefix_len) < 0) {
630
0
                return PATHLEN_ERR();
631
0
            }
632
0
        }
633
13
        else {
634
13
            if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) {
635
0
                return PATHLEN_ERR();
636
0
            }
637
13
        }
638
13
        status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
639
13
        if (_PyStatus_EXCEPTION(status)) {
640
0
            return status;
641
0
        }
642
13
        status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
643
13
        if (_PyStatus_EXCEPTION(status)) {
644
0
            return status;
645
0
        }
646
13
        *found = 1;
647
13
        return _PyStatus_OK();
648
13
    }
649
650
    /* Check for pybuilddir.txt */
651
13
    assert(*found == 0);
652
0
    status = calculate_pybuilddir(argv0_path, exec_prefix, exec_prefix_len,
653
0
                                  found);
654
0
    if (_PyStatus_EXCEPTION(status)) {
655
0
        return status;
656
0
    }
657
0
    if (*found) {
658
0
        return _PyStatus_OK();
659
0
    }
660
661
    /* Search from argv0_path, until root is found */
662
0
    status = copy_absolute(exec_prefix, argv0_path, exec_prefix_len);
663
0
    if (_PyStatus_EXCEPTION(status)) {
664
0
        return status;
665
0
    }
666
667
0
    do {
668
        /* Path: <argv0_path or substring> / <lib_python> / "lib-dynload" */
669
0
        size_t n = wcslen(exec_prefix);
670
0
        status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
671
0
        if (_PyStatus_EXCEPTION(status)) {
672
0
            return status;
673
0
        }
674
0
        status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
675
0
        if (_PyStatus_EXCEPTION(status)) {
676
0
            return status;
677
0
        }
678
0
        if (isdir(exec_prefix)) {
679
0
            *found = 1;
680
0
            return _PyStatus_OK();
681
0
        }
682
0
        exec_prefix[n] = L'\0';
683
0
        reduce(exec_prefix);
684
0
    } while (exec_prefix[0]);
685
686
    /* Look at configure's EXEC_PREFIX.
687
688
       Path: <EXEC_PREFIX macro> / <lib_python> / "lib-dynload" */
689
0
    if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
690
0
        return PATHLEN_ERR();
691
0
    }
692
0
    status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
693
0
    if (_PyStatus_EXCEPTION(status)) {
694
0
        return status;
695
0
    }
696
0
    status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
697
0
    if (_PyStatus_EXCEPTION(status)) {
698
0
        return status;
699
0
    }
700
0
    if (isdir(exec_prefix)) {
701
0
        *found = 1;
702
0
        return _PyStatus_OK();
703
0
    }
704
705
    /* Fail */
706
0
    *found = 0;
707
0
    return _PyStatus_OK();
708
0
}
709
710
711
static PyStatus
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
13
{
716
13
    PyStatus status;
717
718
13
    status = search_for_exec_prefix(calculate, pathconfig, argv0_path,
719
13
                                    exec_prefix, exec_prefix_len,
720
13
                                    &calculate->exec_prefix_found);
721
13
    if (_PyStatus_EXCEPTION(status)) {
722
0
        return status;
723
0
    }
724
725
13
    if (!calculate->exec_prefix_found) {
726
0
        if (calculate->warnings) {
727
0
            fprintf(stderr,
728
0
                "Could not find platform dependent libraries <exec_prefix>\n");
729
0
        }
730
0
        if (safe_wcscpy(exec_prefix, calculate->exec_prefix, exec_prefix_len) < 0) {
731
0
            return PATHLEN_ERR();
732
0
        }
733
0
        status = joinpath(exec_prefix, L"lib/lib-dynload", exec_prefix_len);
734
0
        if (_PyStatus_EXCEPTION(status)) {
735
0
            return status;
736
0
        }
737
0
    }
738
    /* If we found EXEC_PREFIX do *not* reduce it!  (Yet.) */
739
13
    return _PyStatus_OK();
740
13
}
741
742
743
static PyStatus
744
calculate_set_exec_prefix(PyCalculatePath *calculate,
745
                          _PyPathConfig *pathconfig,
746
                          wchar_t *exec_prefix)
747
13
{
748
13
    if (calculate->exec_prefix_found > 0) {
749
13
        reduce(exec_prefix);
750
13
        reduce(exec_prefix);
751
13
        reduce(exec_prefix);
752
13
        if (!exec_prefix[0]) {
753
0
            wcscpy(exec_prefix, separator);
754
0
        }
755
756
13
        pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
757
13
    }
758
0
    else {
759
0
        pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix);
760
0
    }
761
762
13
    if (pathconfig->exec_prefix == NULL) {
763
0
        return _PyStatus_NO_MEMORY();
764
0
    }
765
766
13
    return _PyStatus_OK();
767
13
}
768
769
770
static PyStatus
771
calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
772
13
{
773
13
    PyStatus status;
774
13
    wchar_t program_full_path[MAXPATHLEN + 1];
775
13
    const size_t program_full_path_len = Py_ARRAY_LENGTH(program_full_path);
776
13
    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
13
    if (wcschr(pathconfig->program_name, SEP)) {
793
0
        if (safe_wcscpy(program_full_path, pathconfig->program_name,
794
0
                        program_full_path_len) < 0) {
795
0
            return PATHLEN_ERR();
796
0
        }
797
0
    }
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
13
    else if (calculate->path_env) {
825
13
        wchar_t *path = calculate->path_env;
826
26
        while (1) {
827
26
            wchar_t *delim = wcschr(path, DELIM);
828
829
26
            if (delim) {
830
26
                size_t len = delim - path;
831
26
                if (len >= program_full_path_len) {
832
0
                    return PATHLEN_ERR();
833
0
                }
834
26
                wcsncpy(program_full_path, path, len);
835
26
                program_full_path[len] = '\0';
836
26
            }
837
0
            else {
838
0
                if (safe_wcscpy(program_full_path, path,
839
0
                                program_full_path_len) < 0) {
840
0
                    return PATHLEN_ERR();
841
0
                }
842
0
            }
843
844
26
            status = joinpath(program_full_path, pathconfig->program_name,
845
26
                              program_full_path_len);
846
26
            if (_PyStatus_EXCEPTION(status)) {
847
0
                return status;
848
0
            }
849
850
26
            if (isxfile(program_full_path)) {
851
13
                break;
852
13
            }
853
854
13
            if (!delim) {
855
0
                program_full_path[0] = L'\0';
856
0
                break;
857
0
            }
858
13
            path = delim + 1;
859
13
        }
860
13
    }
861
0
    else {
862
0
        program_full_path[0] = '\0';
863
0
    }
864
13
    if (program_full_path[0] != SEP && program_full_path[0] != '\0') {
865
0
        status = absolutize(program_full_path, program_full_path_len);
866
0
        if (_PyStatus_EXCEPTION(status)) {
867
0
            return status;
868
0
        }
869
0
    }
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
13
    pathconfig->program_full_path = _PyMem_RawWcsdup(program_full_path);
885
13
    if (pathconfig->program_full_path == NULL) {
886
0
        return _PyStatus_NO_MEMORY();
887
0
    }
888
13
    return _PyStatus_OK();
889
13
}
890
891
892
static PyStatus
893
calculate_argv0_path(PyCalculatePath *calculate, const wchar_t *program_full_path,
894
                     wchar_t *argv0_path, size_t argv0_path_len)
895
13
{
896
13
    if (safe_wcscpy(argv0_path, program_full_path, argv0_path_len) < 0) {
897
0
        return PATHLEN_ERR();
898
0
    }
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
13
#if HAVE_READLINK
960
13
    wchar_t tmpbuffer[MAXPATHLEN + 1];
961
13
    const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer);
962
13
    int linklen = _Py_wreadlink(program_full_path, tmpbuffer, buflen);
963
26
    while (linklen != -1) {
964
13
        if (tmpbuffer[0] == SEP) {
965
            /* tmpbuffer should never be longer than MAXPATHLEN,
966
               but extra check does not hurt */
967
13
            if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
968
0
                return PATHLEN_ERR();
969
0
            }
970
13
        }
971
0
        else {
972
            /* Interpret relative to program_full_path */
973
0
            PyStatus status;
974
0
            reduce(argv0_path);
975
0
            status = joinpath(argv0_path, tmpbuffer, argv0_path_len);
976
0
            if (_PyStatus_EXCEPTION(status)) {
977
0
                return status;
978
0
            }
979
0
        }
980
13
        linklen = _Py_wreadlink(argv0_path, tmpbuffer, buflen);
981
13
    }
982
13
#endif /* HAVE_READLINK */
983
984
13
    reduce(argv0_path);
985
    /* At this point, argv0_path is guaranteed to be less than
986
       MAXPATHLEN bytes long. */
987
13
    return _PyStatus_OK();
988
13
}
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
996
calculate_read_pyenv(PyCalculatePath *calculate,
997
                     wchar_t *argv0_path, size_t argv0_path_len)
998
13
{
999
13
    PyStatus status;
1000
13
    const wchar_t *env_cfg = L"pyvenv.cfg";
1001
13
    FILE *env_file;
1002
1003
13
    wchar_t filename[MAXPATHLEN+1];
1004
13
    const size_t filename_len = Py_ARRAY_LENGTH(filename);
1005
13
    memset(filename, 0, sizeof(filename));
1006
1007
    /* Filename: <argv0_path_len> / "pyvenv.cfg" */
1008
13
    if (safe_wcscpy(filename, argv0_path, filename_len) < 0) {
1009
0
        return PATHLEN_ERR();
1010
0
    }
1011
1012
13
    status = joinpath(filename, env_cfg, filename_len);
1013
13
    if (_PyStatus_EXCEPTION(status)) {
1014
0
        return status;
1015
0
    }
1016
13
    env_file = _Py_wfopen(filename, L"r");
1017
13
    if (env_file == NULL) {
1018
13
        errno = 0;
1019
1020
        /* Filename: <basename(basename(argv0_path_len))> / "pyvenv.cfg" */
1021
13
        reduce(filename);
1022
13
        reduce(filename);
1023
13
        status = joinpath(filename, env_cfg, filename_len);
1024
13
        if (_PyStatus_EXCEPTION(status)) {
1025
0
            return status;
1026
0
        }
1027
1028
13
        env_file = _Py_wfopen(filename, L"r");
1029
13
        if (env_file == NULL) {
1030
13
            errno = 0;
1031
13
            return _PyStatus_OK();
1032
13
        }
1033
13
    }
1034
1035
    /* Look for a 'home' variable and set argv0_path to it, if found */
1036
0
    wchar_t home[MAXPATHLEN+1];
1037
0
    memset(home, 0, sizeof(home));
1038
1039
0
    if (_Py_FindEnvConfigValue(env_file, L"home",
1040
0
                               home, Py_ARRAY_LENGTH(home))) {
1041
0
        if (safe_wcscpy(argv0_path, home, argv0_path_len) < 0) {
1042
0
            fclose(env_file);
1043
0
            return PATHLEN_ERR();
1044
0
        }
1045
0
    }
1046
0
    fclose(env_file);
1047
0
    return _PyStatus_OK();
1048
0
}
1049
1050
1051
static PyStatus
1052
calculate_zip_path(PyCalculatePath *calculate, const wchar_t *prefix,
1053
                   wchar_t *zip_path, size_t zip_path_len)
1054
13
{
1055
13
    PyStatus status;
1056
1057
13
    if (calculate->prefix_found > 0) {
1058
        /* Use the reduced prefix returned by Py_GetPrefix() */
1059
13
        if (safe_wcscpy(zip_path, prefix, zip_path_len) < 0) {
1060
0
            return PATHLEN_ERR();
1061
0
        }
1062
13
        reduce(zip_path);
1063
13
        reduce(zip_path);
1064
13
    }
1065
0
    else {
1066
0
        if (safe_wcscpy(zip_path, calculate->prefix, zip_path_len) < 0) {
1067
0
            return PATHLEN_ERR();
1068
0
        }
1069
0
    }
1070
13
    status = joinpath(zip_path, L"lib/python00.zip", zip_path_len);
1071
13
    if (_PyStatus_EXCEPTION(status)) {
1072
0
        return status;
1073
0
    }
1074
1075
    /* Replace "00" with version */
1076
13
    size_t bufsz = wcslen(zip_path);
1077
13
    zip_path[bufsz - 6] = VERSION[0];
1078
13
    zip_path[bufsz - 5] = VERSION[2];
1079
13
    return _PyStatus_OK();
1080
13
}
1081
1082
1083
static PyStatus
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
13
{
1090
    /* Calculate size of return buffer */
1091
13
    size_t bufsz = 0;
1092
13
    if (calculate->pythonpath_env != NULL) {
1093
13
        bufsz += wcslen(calculate->pythonpath_env) + 1;
1094
13
    }
1095
1096
13
    wchar_t *defpath = calculate->pythonpath;
1097
13
    size_t prefixsz = wcslen(prefix) + 1;
1098
13
    while (1) {
1099
13
        wchar_t *delim = wcschr(defpath, DELIM);
1100
1101
13
        if (defpath[0] != SEP) {
1102
            /* Paths are relative to prefix */
1103
13
            bufsz += prefixsz;
1104
13
        }
1105
1106
13
        if (delim) {
1107
0
            bufsz += delim - defpath + 1;
1108
0
        }
1109
13
        else {
1110
13
            bufsz += wcslen(defpath) + 1;
1111
13
            break;
1112
13
        }
1113
0
        defpath = delim + 1;
1114
0
    }
1115
1116
13
    bufsz += wcslen(zip_path) + 1;
1117
13
    bufsz += wcslen(exec_prefix) + 1;
1118
1119
    /* Allocate the buffer */
1120
13
    wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
1121
13
    if (buf == NULL) {
1122
0
        return _PyStatus_NO_MEMORY();
1123
0
    }
1124
13
    buf[0] = '\0';
1125
1126
    /* Run-time value of $PYTHONPATH goes first */
1127
13
    if (calculate->pythonpath_env) {
1128
13
        wcscpy(buf, calculate->pythonpath_env);
1129
13
        wcscat(buf, delimiter);
1130
13
    }
1131
1132
    /* Next is the default zip path */
1133
13
    wcscat(buf, zip_path);
1134
13
    wcscat(buf, delimiter);
1135
1136
    /* Next goes merge of compile-time $PYTHONPATH with
1137
     * dynamically located prefix.
1138
     */
1139
13
    defpath = calculate->pythonpath;
1140
13
    while (1) {
1141
13
        wchar_t *delim = wcschr(defpath, DELIM);
1142
1143
13
        if (defpath[0] != SEP) {
1144
13
            wcscat(buf, prefix);
1145
13
            if (prefixsz >= 2 && prefix[prefixsz - 2] != SEP &&
1146
13
                defpath[0] != (delim ? DELIM : L'\0'))
1147
0
            {
1148
                /* not empty */
1149
0
                wcscat(buf, separator);
1150
0
            }
1151
13
        }
1152
1153
13
        if (delim) {
1154
0
            size_t len = delim - defpath + 1;
1155
0
            size_t end = wcslen(buf) + len;
1156
0
            wcsncat(buf, defpath, len);
1157
0
            buf[end] = '\0';
1158
0
        }
1159
13
        else {
1160
13
            wcscat(buf, defpath);
1161
13
            break;
1162
13
        }
1163
0
        defpath = delim + 1;
1164
0
    }
1165
13
    wcscat(buf, delimiter);
1166
1167
    /* Finally, on goes the directory for dynamic-load modules */
1168
13
    wcscat(buf, exec_prefix);
1169
1170
13
    pathconfig->module_search_path = buf;
1171
13
    return _PyStatus_OK();
1172
13
}
1173
1174
1175
static PyStatus
1176
calculate_init(PyCalculatePath *calculate, const PyConfig *config)
1177
13
{
1178
13
    size_t len;
1179
13
    const char *path = getenv("PATH");
1180
13
    if (path) {
1181
13
        calculate->path_env = Py_DecodeLocale(path, &len);
1182
13
        if (!calculate->path_env) {
1183
0
            return DECODE_LOCALE_ERR("PATH environment variable", len);
1184
0
        }
1185
13
    }
1186
1187
13
    calculate->pythonpath = Py_DecodeLocale(PYTHONPATH, &len);
1188
13
    if (!calculate->pythonpath) {
1189
0
        return DECODE_LOCALE_ERR("PYTHONPATH define", len);
1190
0
    }
1191
1192
13
    calculate->prefix = Py_DecodeLocale(PREFIX, &len);
1193
13
    if (!calculate->prefix) {
1194
0
        return DECODE_LOCALE_ERR("PREFIX define", len);
1195
0
    }
1196
13
    calculate->exec_prefix = Py_DecodeLocale(EXEC_PREFIX, &len);
1197
13
    if (!calculate->exec_prefix) {
1198
0
        return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
1199
0
    }
1200
13
    calculate->lib_python = Py_DecodeLocale("lib/python" VERSION, &len);
1201
13
    if (!calculate->lib_python) {
1202
0
        return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
1203
0
    }
1204
1205
13
    calculate->warnings = config->pathconfig_warnings;
1206
13
    calculate->pythonpath_env = config->pythonpath_env;
1207
1208
13
    return _PyStatus_OK();
1209
13
}
1210
1211
1212
static void
1213
calculate_free(PyCalculatePath *calculate)
1214
13
{
1215
13
    PyMem_RawFree(calculate->pythonpath);
1216
13
    PyMem_RawFree(calculate->prefix);
1217
13
    PyMem_RawFree(calculate->exec_prefix);
1218
13
    PyMem_RawFree(calculate->lib_python);
1219
13
    PyMem_RawFree(calculate->path_env);
1220
13
}
1221
1222
1223
static PyStatus
1224
calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
1225
13
{
1226
13
    PyStatus status;
1227
1228
13
    if (pathconfig->program_full_path == NULL) {
1229
13
        status = calculate_program_full_path(calculate, pathconfig);
1230
13
        if (_PyStatus_EXCEPTION(status)) {
1231
0
            return status;
1232
0
        }
1233
13
    }
1234
1235
13
    wchar_t argv0_path[MAXPATHLEN+1];
1236
13
    memset(argv0_path, 0, sizeof(argv0_path));
1237
1238
13
    status = calculate_argv0_path(calculate, pathconfig->program_full_path,
1239
13
                                  argv0_path, Py_ARRAY_LENGTH(argv0_path));
1240
13
    if (_PyStatus_EXCEPTION(status)) {
1241
0
        return status;
1242
0
    }
1243
1244
    /* If a pyvenv.cfg configure file is found,
1245
       argv0_path is overriden with its 'home' variable. */
1246
13
    status = calculate_read_pyenv(calculate,
1247
13
                                  argv0_path, Py_ARRAY_LENGTH(argv0_path));
1248
13
    if (_PyStatus_EXCEPTION(status)) {
1249
0
        return status;
1250
0
    }
1251
1252
13
    wchar_t prefix[MAXPATHLEN+1];
1253
13
    memset(prefix, 0, sizeof(prefix));
1254
13
    status = calculate_prefix(calculate, pathconfig,
1255
13
                              argv0_path,
1256
13
                              prefix, Py_ARRAY_LENGTH(prefix));
1257
13
    if (_PyStatus_EXCEPTION(status)) {
1258
0
        return status;
1259
0
    }
1260
1261
13
    wchar_t zip_path[MAXPATHLEN+1];    /* ".../lib/pythonXY.zip" */
1262
13
    memset(zip_path, 0, sizeof(zip_path));
1263
1264
13
    status = calculate_zip_path(calculate, prefix,
1265
13
                                zip_path, Py_ARRAY_LENGTH(zip_path));
1266
13
    if (_PyStatus_EXCEPTION(status)) {
1267
0
        return status;
1268
0
    }
1269
1270
13
    wchar_t exec_prefix[MAXPATHLEN+1];
1271
13
    memset(exec_prefix, 0, sizeof(exec_prefix));
1272
13
    status = calculate_exec_prefix(calculate, pathconfig, argv0_path,
1273
13
                                   exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
1274
13
    if (_PyStatus_EXCEPTION(status)) {
1275
0
        return status;
1276
0
    }
1277
1278
13
    if ((!calculate->prefix_found || !calculate->exec_prefix_found) &&
1279
0
        calculate->warnings)
1280
0
    {
1281
0
        fprintf(stderr,
1282
0
                "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
1283
0
    }
1284
1285
13
    if (pathconfig->module_search_path == NULL) {
1286
13
        status = calculate_module_search_path(calculate, pathconfig,
1287
13
                                              prefix, exec_prefix, zip_path);
1288
13
        if (_PyStatus_EXCEPTION(status)) {
1289
0
            return status;
1290
0
        }
1291
13
    }
1292
1293
13
    if (pathconfig->prefix == NULL) {
1294
13
        status = calculate_set_prefix(calculate, pathconfig, prefix);
1295
13
        if (_PyStatus_EXCEPTION(status)) {
1296
0
            return status;
1297
0
        }
1298
13
    }
1299
1300
13
    if (pathconfig->exec_prefix == NULL) {
1301
13
        status = calculate_set_exec_prefix(calculate, pathconfig, exec_prefix);
1302
13
        if (_PyStatus_EXCEPTION(status)) {
1303
0
            return status;
1304
0
        }
1305
13
    }
1306
1307
13
    return _PyStatus_OK();
1308
13
}
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
1342
_PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
1343
13
{
1344
13
    PyStatus status;
1345
13
    PyCalculatePath calculate;
1346
13
    memset(&calculate, 0, sizeof(calculate));
1347
1348
13
    status = calculate_init(&calculate, config);
1349
13
    if (_PyStatus_EXCEPTION(status)) {
1350
0
        goto done;
1351
0
    }
1352
1353
13
    status = calculate_path(&calculate, pathconfig);
1354
13
    if (_PyStatus_EXCEPTION(status)) {
1355
0
        goto done;
1356
0
    }
1357
1358
13
    status = _PyStatus_OK();
1359
1360
13
done:
1361
13
    calculate_free(&calculate);
1362
13
    return status;
1363
13
}
1364
1365
#ifdef __cplusplus
1366
}
1367
#endif