Coverage Report

Created: 2025-07-11 06:59

/src/Python-3.8.3/Modules/getpath.c
Line
Count
Source (jump to first uncovered line)
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
28
{
147
28
    int err;
148
28
    char *fname;
149
28
    fname = _Py_EncodeLocaleRaw(path, NULL);
150
28
    if (fname == NULL) {
151
0
        errno = EINVAL;
152
0
        return -1;
153
0
    }
154
28
    err = stat(fname, buf);
155
28
    PyMem_RawFree(fname);
156
28
    return err;
157
28
}
158
159
160
static void
161
reduce(wchar_t *dir)
162
140
{
163
140
    size_t i = wcslen(dir);
164
1.26k
    while (i > 0 && dir[i] != SEP) {
165
1.12k
        --i;
166
1.12k
    }
167
140
    dir[i] = '\0';
168
140
}
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
28
{
209
28
    struct stat buf;
210
28
    if (_Py_wstat(filename, &buf) != 0) {
211
14
        return 0;
212
14
    }
213
14
    if (!S_ISREG(buf.st_mode)) {
214
0
        return 0;
215
0
    }
216
14
    if ((buf.st_mode & 0111) == 0) {
217
0
        return 0;
218
0
    }
219
14
    return 1;
220
14
}
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
112
{
243
112
    size_t n, k;
244
112
    if (stuff[0] != SEP) {
245
112
        n = wcslen(buffer);
246
112
        if (n >= buflen) {
247
0
            return PATHLEN_ERR();
248
0
        }
249
250
112
        if (n > 0 && buffer[n-1] != SEP) {
251
112
            buffer[n++] = SEP;
252
112
        }
253
112
    }
254
0
    else {
255
0
        n = 0;
256
0
    }
257
258
112
    k = wcslen(stuff);
259
112
    if (n + k >= buflen) {
260
0
        return PATHLEN_ERR();
261
0
    }
262
112
    wcsncpy(buffer+n, stuff, k);
263
112
    buffer[n+k] = '\0';
264
265
112
    return _PyStatus_OK();
266
112
}
267
268
269
static inline int
270
safe_wcscpy(wchar_t *dst, const wchar_t *src, size_t n)
271
84
{
272
84
    size_t srclen = wcslen(src);
273
84
    if (n <= srclen) {
274
0
        dst[0] = L'\0';
275
0
        return -1;
276
0
    }
277
84
    memcpy(dst, src, (srclen + 1) * sizeof(wchar_t));
278
84
    return 0;
279
84
}
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
14
{
373
14
    wchar_t path[MAXPATHLEN+1];
374
14
    memset(path, 0, sizeof(path));
375
14
    size_t path_len = Py_ARRAY_LENGTH(path);
376
377
14
    PyStatus status;
378
379
    /* If PYTHONHOME is set, we believe it unconditionally */
380
14
    if (pathconfig->home) {
381
        /* Path: <home> / <lib_python> */
382
14
        if (safe_wcscpy(prefix, pathconfig->home, prefix_len) < 0) {
383
0
            return PATHLEN_ERR();
384
0
        }
385
14
        wchar_t *delim = wcschr(prefix, DELIM);
386
14
        if (delim) {
387
0
            *delim = L'\0';
388
0
        }
389
14
        status = joinpath(prefix, calculate->lib_python, prefix_len);
390
14
        if (_PyStatus_EXCEPTION(status)) {
391
0
            return status;
392
0
        }
393
14
        *found = 1;
394
14
        return _PyStatus_OK();
395
14
    }
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
14
{
496
14
    PyStatus status;
497
498
14
    status = search_for_prefix(calculate, pathconfig, argv0_path,
499
14
                               prefix, prefix_len,
500
14
                               &calculate->prefix_found);
501
14
    if (_PyStatus_EXCEPTION(status)) {
502
0
        return status;
503
0
    }
504
505
14
    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
14
    return _PyStatus_OK();
519
14
}
520
521
522
static PyStatus
523
calculate_set_prefix(PyCalculatePath *calculate, _PyPathConfig *pathconfig,
524
                     wchar_t *prefix)
525
14
{
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
14
    if (calculate->prefix_found > 0) {
532
14
        reduce(prefix);
533
14
        reduce(prefix);
534
        /* The prefix is the root directory, but reduce() chopped
535
         * off the "/". */
536
14
        if (!prefix[0]) {
537
0
            wcscpy(prefix, separator);
538
0
        }
539
14
        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
540
14
    }
541
0
    else {
542
0
        pathconfig->prefix = _PyMem_RawWcsdup(calculate->prefix);
543
0
    }
544
545
14
    if (pathconfig->prefix == NULL) {
546
0
        return _PyStatus_NO_MEMORY();
547
0
    }
548
14
    return _PyStatus_OK();
549
14
}
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
14
{
622
14
    PyStatus status;
623
624
    /* If PYTHONHOME is set, we believe it unconditionally */
625
14
    if (pathconfig->home) {
626
        /* Path: <home> / <lib_python> / "lib-dynload" */
627
14
        wchar_t *delim = wcschr(pathconfig->home, DELIM);
628
14
        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
14
        else {
634
14
            if (safe_wcscpy(exec_prefix, pathconfig->home, exec_prefix_len) < 0) {
635
0
                return PATHLEN_ERR();
636
0
            }
637
14
        }
638
14
        status = joinpath(exec_prefix, calculate->lib_python, exec_prefix_len);
639
14
        if (_PyStatus_EXCEPTION(status)) {
640
0
            return status;
641
0
        }
642
14
        status = joinpath(exec_prefix, L"lib-dynload", exec_prefix_len);
643
14
        if (_PyStatus_EXCEPTION(status)) {
644
0
            return status;
645
0
        }
646
14
        *found = 1;
647
14
        return _PyStatus_OK();
648
14
    }
649
650
    /* Check for pybuilddir.txt */
651
0
    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
14
{
716
14
    PyStatus status;
717
718
14
    status = search_for_exec_prefix(calculate, pathconfig, argv0_path,
719
14
                                    exec_prefix, exec_prefix_len,
720
14
                                    &calculate->exec_prefix_found);
721
14
    if (_PyStatus_EXCEPTION(status)) {
722
0
        return status;
723
0
    }
724
725
14
    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
14
    return _PyStatus_OK();
740
14
}
741
742
743
static PyStatus
744
calculate_set_exec_prefix(PyCalculatePath *calculate,
745
                          _PyPathConfig *pathconfig,
746
                          wchar_t *exec_prefix)
747
14
{
748
14
    if (calculate->exec_prefix_found > 0) {
749
14
        reduce(exec_prefix);
750
14
        reduce(exec_prefix);
751
14
        reduce(exec_prefix);
752
14
        if (!exec_prefix[0]) {
753
0
            wcscpy(exec_prefix, separator);
754
0
        }
755
756
14
        pathconfig->exec_prefix = _PyMem_RawWcsdup(exec_prefix);
757
14
    }
758
0
    else {
759
0
        pathconfig->exec_prefix = _PyMem_RawWcsdup(calculate->exec_prefix);
760
0
    }
761
762
14
    if (pathconfig->exec_prefix == NULL) {
763
0
        return _PyStatus_NO_MEMORY();
764
0
    }
765
766
14
    return _PyStatus_OK();
767
14
}
768
769
770
static PyStatus
771
calculate_program_full_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
772
14
{
773
14
    PyStatus status;
774
14
    wchar_t program_full_path[MAXPATHLEN + 1];
775
14
    const size_t program_full_path_len = Py_ARRAY_LENGTH(program_full_path);
776
14
    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
14
    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
14
    else if (calculate->path_env) {
825
14
        wchar_t *path = calculate->path_env;
826
28
        while (1) {
827
28
            wchar_t *delim = wcschr(path, DELIM);
828
829
28
            if (delim) {
830
28
                size_t len = delim - path;
831
28
                if (len >= program_full_path_len) {
832
0
                    return PATHLEN_ERR();
833
0
                }
834
28
                wcsncpy(program_full_path, path, len);
835
28
                program_full_path[len] = '\0';
836
28
            }
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
28
            status = joinpath(program_full_path, pathconfig->program_name,
845
28
                              program_full_path_len);
846
28
            if (_PyStatus_EXCEPTION(status)) {
847
0
                return status;
848
0
            }
849
850
28
            if (isxfile(program_full_path)) {
851
14
                break;
852
14
            }
853
854
14
            if (!delim) {
855
0
                program_full_path[0] = L'\0';
856
0
                break;
857
0
            }
858
14
            path = delim + 1;
859
14
        }
860
14
    }
861
0
    else {
862
0
        program_full_path[0] = '\0';
863
0
    }
864
14
    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
14
    pathconfig->program_full_path = _PyMem_RawWcsdup(program_full_path);
885
14
    if (pathconfig->program_full_path == NULL) {
886
0
        return _PyStatus_NO_MEMORY();
887
0
    }
888
14
    return _PyStatus_OK();
889
14
}
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
14
{
896
14
    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
14
#if HAVE_READLINK
960
14
    wchar_t tmpbuffer[MAXPATHLEN + 1];
961
14
    const size_t buflen = Py_ARRAY_LENGTH(tmpbuffer);
962
14
    int linklen = _Py_wreadlink(program_full_path, tmpbuffer, buflen);
963
28
    while (linklen != -1) {
964
14
        if (tmpbuffer[0] == SEP) {
965
            /* tmpbuffer should never be longer than MAXPATHLEN,
966
               but extra check does not hurt */
967
14
            if (safe_wcscpy(argv0_path, tmpbuffer, argv0_path_len) < 0) {
968
0
                return PATHLEN_ERR();
969
0
            }
970
14
        }
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
14
        linklen = _Py_wreadlink(argv0_path, tmpbuffer, buflen);
981
14
    }
982
14
#endif /* HAVE_READLINK */
983
984
14
    reduce(argv0_path);
985
    /* At this point, argv0_path is guaranteed to be less than
986
       MAXPATHLEN bytes long. */
987
14
    return _PyStatus_OK();
988
14
}
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
14
{
999
14
    PyStatus status;
1000
14
    const wchar_t *env_cfg = L"pyvenv.cfg";
1001
14
    FILE *env_file;
1002
1003
14
    wchar_t filename[MAXPATHLEN+1];
1004
14
    const size_t filename_len = Py_ARRAY_LENGTH(filename);
1005
14
    memset(filename, 0, sizeof(filename));
1006
1007
    /* Filename: <argv0_path_len> / "pyvenv.cfg" */
1008
14
    if (safe_wcscpy(filename, argv0_path, filename_len) < 0) {
1009
0
        return PATHLEN_ERR();
1010
0
    }
1011
1012
14
    status = joinpath(filename, env_cfg, filename_len);
1013
14
    if (_PyStatus_EXCEPTION(status)) {
1014
0
        return status;
1015
0
    }
1016
14
    env_file = _Py_wfopen(filename, L"r");
1017
14
    if (env_file == NULL) {
1018
14
        errno = 0;
1019
1020
        /* Filename: <basename(basename(argv0_path_len))> / "pyvenv.cfg" */
1021
14
        reduce(filename);
1022
14
        reduce(filename);
1023
14
        status = joinpath(filename, env_cfg, filename_len);
1024
14
        if (_PyStatus_EXCEPTION(status)) {
1025
0
            return status;
1026
0
        }
1027
1028
14
        env_file = _Py_wfopen(filename, L"r");
1029
14
        if (env_file == NULL) {
1030
14
            errno = 0;
1031
14
            return _PyStatus_OK();
1032
14
        }
1033
14
    }
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
14
{
1055
14
    PyStatus status;
1056
1057
14
    if (calculate->prefix_found > 0) {
1058
        /* Use the reduced prefix returned by Py_GetPrefix() */
1059
14
        if (safe_wcscpy(zip_path, prefix, zip_path_len) < 0) {
1060
0
            return PATHLEN_ERR();
1061
0
        }
1062
14
        reduce(zip_path);
1063
14
        reduce(zip_path);
1064
14
    }
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
14
    status = joinpath(zip_path, L"lib/python00.zip", zip_path_len);
1071
14
    if (_PyStatus_EXCEPTION(status)) {
1072
0
        return status;
1073
0
    }
1074
1075
    /* Replace "00" with version */
1076
14
    size_t bufsz = wcslen(zip_path);
1077
14
    zip_path[bufsz - 6] = VERSION[0];
1078
14
    zip_path[bufsz - 5] = VERSION[2];
1079
14
    return _PyStatus_OK();
1080
14
}
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
14
{
1090
    /* Calculate size of return buffer */
1091
14
    size_t bufsz = 0;
1092
14
    if (calculate->pythonpath_env != NULL) {
1093
14
        bufsz += wcslen(calculate->pythonpath_env) + 1;
1094
14
    }
1095
1096
14
    wchar_t *defpath = calculate->pythonpath;
1097
14
    size_t prefixsz = wcslen(prefix) + 1;
1098
14
    while (1) {
1099
14
        wchar_t *delim = wcschr(defpath, DELIM);
1100
1101
14
        if (defpath[0] != SEP) {
1102
            /* Paths are relative to prefix */
1103
14
            bufsz += prefixsz;
1104
14
        }
1105
1106
14
        if (delim) {
1107
0
            bufsz += delim - defpath + 1;
1108
0
        }
1109
14
        else {
1110
14
            bufsz += wcslen(defpath) + 1;
1111
14
            break;
1112
14
        }
1113
0
        defpath = delim + 1;
1114
0
    }
1115
1116
14
    bufsz += wcslen(zip_path) + 1;
1117
14
    bufsz += wcslen(exec_prefix) + 1;
1118
1119
    /* Allocate the buffer */
1120
14
    wchar_t *buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
1121
14
    if (buf == NULL) {
1122
0
        return _PyStatus_NO_MEMORY();
1123
0
    }
1124
14
    buf[0] = '\0';
1125
1126
    /* Run-time value of $PYTHONPATH goes first */
1127
14
    if (calculate->pythonpath_env) {
1128
14
        wcscpy(buf, calculate->pythonpath_env);
1129
14
        wcscat(buf, delimiter);
1130
14
    }
1131
1132
    /* Next is the default zip path */
1133
14
    wcscat(buf, zip_path);
1134
14
    wcscat(buf, delimiter);
1135
1136
    /* Next goes merge of compile-time $PYTHONPATH with
1137
     * dynamically located prefix.
1138
     */
1139
14
    defpath = calculate->pythonpath;
1140
14
    while (1) {
1141
14
        wchar_t *delim = wcschr(defpath, DELIM);
1142
1143
14
        if (defpath[0] != SEP) {
1144
14
            wcscat(buf, prefix);
1145
14
            if (prefixsz >= 2 && prefix[prefixsz - 2] != SEP &&
1146
14
                defpath[0] != (delim ? DELIM : L'\0'))
1147
0
            {
1148
                /* not empty */
1149
0
                wcscat(buf, separator);
1150
0
            }
1151
14
        }
1152
1153
14
        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
14
        else {
1160
14
            wcscat(buf, defpath);
1161
14
            break;
1162
14
        }
1163
0
        defpath = delim + 1;
1164
0
    }
1165
14
    wcscat(buf, delimiter);
1166
1167
    /* Finally, on goes the directory for dynamic-load modules */
1168
14
    wcscat(buf, exec_prefix);
1169
1170
14
    pathconfig->module_search_path = buf;
1171
14
    return _PyStatus_OK();
1172
14
}
1173
1174
1175
static PyStatus
1176
calculate_init(PyCalculatePath *calculate, const PyConfig *config)
1177
14
{
1178
14
    size_t len;
1179
14
    const char *path = getenv("PATH");
1180
14
    if (path) {
1181
14
        calculate->path_env = Py_DecodeLocale(path, &len);
1182
14
        if (!calculate->path_env) {
1183
0
            return DECODE_LOCALE_ERR("PATH environment variable", len);
1184
0
        }
1185
14
    }
1186
1187
14
    calculate->pythonpath = Py_DecodeLocale(PYTHONPATH, &len);
1188
14
    if (!calculate->pythonpath) {
1189
0
        return DECODE_LOCALE_ERR("PYTHONPATH define", len);
1190
0
    }
1191
1192
14
    calculate->prefix = Py_DecodeLocale(PREFIX, &len);
1193
14
    if (!calculate->prefix) {
1194
0
        return DECODE_LOCALE_ERR("PREFIX define", len);
1195
0
    }
1196
14
    calculate->exec_prefix = Py_DecodeLocale(EXEC_PREFIX, &len);
1197
14
    if (!calculate->exec_prefix) {
1198
0
        return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
1199
0
    }
1200
14
    calculate->lib_python = Py_DecodeLocale("lib/python" VERSION, &len);
1201
14
    if (!calculate->lib_python) {
1202
0
        return DECODE_LOCALE_ERR("EXEC_PREFIX define", len);
1203
0
    }
1204
1205
14
    calculate->warnings = config->pathconfig_warnings;
1206
14
    calculate->pythonpath_env = config->pythonpath_env;
1207
1208
14
    return _PyStatus_OK();
1209
14
}
1210
1211
1212
static void
1213
calculate_free(PyCalculatePath *calculate)
1214
14
{
1215
14
    PyMem_RawFree(calculate->pythonpath);
1216
14
    PyMem_RawFree(calculate->prefix);
1217
14
    PyMem_RawFree(calculate->exec_prefix);
1218
14
    PyMem_RawFree(calculate->lib_python);
1219
14
    PyMem_RawFree(calculate->path_env);
1220
14
}
1221
1222
1223
static PyStatus
1224
calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
1225
14
{
1226
14
    PyStatus status;
1227
1228
14
    if (pathconfig->program_full_path == NULL) {
1229
14
        status = calculate_program_full_path(calculate, pathconfig);
1230
14
        if (_PyStatus_EXCEPTION(status)) {
1231
0
            return status;
1232
0
        }
1233
14
    }
1234
1235
14
    wchar_t argv0_path[MAXPATHLEN+1];
1236
14
    memset(argv0_path, 0, sizeof(argv0_path));
1237
1238
14
    status = calculate_argv0_path(calculate, pathconfig->program_full_path,
1239
14
                                  argv0_path, Py_ARRAY_LENGTH(argv0_path));
1240
14
    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
14
    status = calculate_read_pyenv(calculate,
1247
14
                                  argv0_path, Py_ARRAY_LENGTH(argv0_path));
1248
14
    if (_PyStatus_EXCEPTION(status)) {
1249
0
        return status;
1250
0
    }
1251
1252
14
    wchar_t prefix[MAXPATHLEN+1];
1253
14
    memset(prefix, 0, sizeof(prefix));
1254
14
    status = calculate_prefix(calculate, pathconfig,
1255
14
                              argv0_path,
1256
14
                              prefix, Py_ARRAY_LENGTH(prefix));
1257
14
    if (_PyStatus_EXCEPTION(status)) {
1258
0
        return status;
1259
0
    }
1260
1261
14
    wchar_t zip_path[MAXPATHLEN+1];    /* ".../lib/pythonXY.zip" */
1262
14
    memset(zip_path, 0, sizeof(zip_path));
1263
1264
14
    status = calculate_zip_path(calculate, prefix,
1265
14
                                zip_path, Py_ARRAY_LENGTH(zip_path));
1266
14
    if (_PyStatus_EXCEPTION(status)) {
1267
0
        return status;
1268
0
    }
1269
1270
14
    wchar_t exec_prefix[MAXPATHLEN+1];
1271
14
    memset(exec_prefix, 0, sizeof(exec_prefix));
1272
14
    status = calculate_exec_prefix(calculate, pathconfig, argv0_path,
1273
14
                                   exec_prefix, Py_ARRAY_LENGTH(exec_prefix));
1274
14
    if (_PyStatus_EXCEPTION(status)) {
1275
0
        return status;
1276
0
    }
1277
1278
14
    if ((!calculate->prefix_found || !calculate->exec_prefix_found) &&
1279
14
        calculate->warnings)
1280
0
    {
1281
0
        fprintf(stderr,
1282
0
                "Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
1283
0
    }
1284
1285
14
    if (pathconfig->module_search_path == NULL) {
1286
14
        status = calculate_module_search_path(calculate, pathconfig,
1287
14
                                              prefix, exec_prefix, zip_path);
1288
14
        if (_PyStatus_EXCEPTION(status)) {
1289
0
            return status;
1290
0
        }
1291
14
    }
1292
1293
14
    if (pathconfig->prefix == NULL) {
1294
14
        status = calculate_set_prefix(calculate, pathconfig, prefix);
1295
14
        if (_PyStatus_EXCEPTION(status)) {
1296
0
            return status;
1297
0
        }
1298
14
    }
1299
1300
14
    if (pathconfig->exec_prefix == NULL) {
1301
14
        status = calculate_set_exec_prefix(calculate, pathconfig, exec_prefix);
1302
14
        if (_PyStatus_EXCEPTION(status)) {
1303
0
            return status;
1304
0
        }
1305
14
    }
1306
1307
14
    return _PyStatus_OK();
1308
14
}
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
14
{
1344
14
    PyStatus status;
1345
14
    PyCalculatePath calculate;
1346
14
    memset(&calculate, 0, sizeof(calculate));
1347
1348
14
    status = calculate_init(&calculate, config);
1349
14
    if (_PyStatus_EXCEPTION(status)) {
1350
0
        goto done;
1351
0
    }
1352
1353
14
    status = calculate_path(&calculate, pathconfig);
1354
14
    if (_PyStatus_EXCEPTION(status)) {
1355
0
        goto done;
1356
0
    }
1357
1358
14
    status = _PyStatus_OK();
1359
1360
14
done:
1361
14
    calculate_free(&calculate);
1362
14
    return status;
1363
14
}
1364
1365
#ifdef __cplusplus
1366
}
1367
#endif