Coverage Report

Created: 2026-07-14 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Modules/getpath.c
Line
Count
Source
1
/* Return the initial module search path. */
2
3
#include "Python.h"
4
#include "pycore_fileutils.h"     // _Py_abspath()
5
#include "pycore_initconfig.h"    // _PyStatus_EXCEPTION()
6
#include "pycore_pathconfig.h"    // _PyPathConfig_ReadGlobal()
7
#include "pycore_pymem.h"         // _PyMem_RawWcsdup()
8
#include "pycore_pystate.h"       // _PyThreadState_GET()
9
10
#include "marshal.h"              // PyMarshal_ReadObjectFromString
11
#include "osdefs.h"               // DELIM
12
#include <wchar.h>
13
14
#ifdef MS_WINDOWS
15
#  include <windows.h>            // GetFullPathNameW(), MAX_PATH
16
#  include <pathcch.h>
17
#endif
18
19
#ifdef __APPLE__
20
#  include <mach-o/dyld.h>
21
#endif
22
23
#ifdef HAVE_DLFCN_H
24
#  include <dlfcn.h>
25
#endif
26
27
/* Reference the precompiled getpath.py */
28
#include "Python/frozen_modules/getpath.h"
29
30
#if (!defined(PREFIX) || !defined(EXEC_PREFIX) \
31
        || !defined(VERSION) || !defined(VPATH) \
32
        || !defined(PLATLIBDIR))
33
#error "PREFIX, EXEC_PREFIX, VERSION, VPATH and PLATLIBDIR macros must be defined"
34
#endif
35
36
#if !defined(PYTHONPATH)
37
#define PYTHONPATH NULL
38
#endif
39
40
#if !defined(PYDEBUGEXT)
41
36
#define PYDEBUGEXT NULL
42
#endif
43
44
#if !defined(PYWINVER)
45
#ifdef MS_DLL_ID
46
#define PYWINVER MS_DLL_ID
47
#else
48
36
#define PYWINVER NULL
49
#endif
50
#endif
51
52
#if !defined(EXE_SUFFIX)
53
#if defined(MS_WINDOWS) || defined(__CYGWIN__) || defined(__MINGW32__)
54
#define EXE_SUFFIX L".exe"
55
#else
56
36
#define EXE_SUFFIX NULL
57
#endif
58
#endif
59
60
61
/* HELPER FUNCTIONS for getpath.py */
62
63
static PyObject *
64
getpath_abspath(PyObject *Py_UNUSED(self), PyObject *args)
65
0
{
66
0
    PyObject *r = NULL;
67
0
    PyObject *pathobj;
68
0
    wchar_t *path;
69
0
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
70
0
        return NULL;
71
0
    }
72
0
    Py_ssize_t len;
73
0
    path = PyUnicode_AsWideCharString(pathobj, &len);
74
0
    if (path) {
75
0
        wchar_t *abs;
76
0
        if (_Py_abspath((const wchar_t *)_Py_normpath(path, -1), &abs) == 0 && abs) {
77
0
            r = PyUnicode_FromWideChar(abs, -1);
78
0
            PyMem_RawFree((void *)abs);
79
0
        } else {
80
0
            PyErr_SetString(PyExc_OSError, "failed to make path absolute");
81
0
        }
82
0
        PyMem_Free((void *)path);
83
0
    }
84
0
    return r;
85
0
}
86
87
88
static PyObject *
89
getpath_basename(PyObject *Py_UNUSED(self), PyObject *args)
90
0
{
91
0
    PyObject *path;
92
0
    if (!PyArg_ParseTuple(args, "U", &path)) {
93
0
        return NULL;
94
0
    }
95
0
    Py_ssize_t end = PyUnicode_GET_LENGTH(path);
96
0
    Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1);
97
0
    if (pos < 0) {
98
0
        return Py_NewRef(path);
99
0
    }
100
0
    return PyUnicode_Substring(path, pos + 1, end);
101
0
}
102
103
104
static PyObject *
105
getpath_dirname(PyObject *Py_UNUSED(self), PyObject *args)
106
108
{
107
108
    PyObject *path;
108
108
    if (!PyArg_ParseTuple(args, "U", &path)) {
109
0
        return NULL;
110
0
    }
111
108
    Py_ssize_t end = PyUnicode_GET_LENGTH(path);
112
108
    Py_ssize_t pos = PyUnicode_FindChar(path, SEP, 0, end, -1);
113
108
    if (pos < 0) {
114
0
        return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
115
0
    }
116
108
    return PyUnicode_Substring(path, 0, pos);
117
108
}
118
119
120
static PyObject *
121
getpath_isabs(PyObject *Py_UNUSED(self), PyObject *args)
122
0
{
123
0
    PyObject *r = NULL;
124
0
    PyObject *pathobj;
125
0
    const wchar_t *path;
126
0
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
127
0
        return NULL;
128
0
    }
129
0
    path = PyUnicode_AsWideCharString(pathobj, NULL);
130
0
    if (path) {
131
0
        r = _Py_isabs(path) ? Py_True : Py_False;
132
0
        PyMem_Free((void *)path);
133
0
    }
134
0
    return Py_XNewRef(r);
135
0
}
136
137
138
static PyObject *
139
getpath_hassuffix(PyObject *Py_UNUSED(self), PyObject *args)
140
0
{
141
0
    PyObject *r = NULL;
142
0
    PyObject *pathobj;
143
0
    PyObject *suffixobj;
144
0
    const wchar_t *path;
145
0
    const wchar_t *suffix;
146
0
    if (!PyArg_ParseTuple(args, "UU", &pathobj, &suffixobj)) {
147
0
        return NULL;
148
0
    }
149
0
    Py_ssize_t len, suffixLen;
150
0
    path = PyUnicode_AsWideCharString(pathobj, &len);
151
0
    if (path) {
152
0
        suffix = PyUnicode_AsWideCharString(suffixobj, &suffixLen);
153
0
        if (suffix) {
154
0
            if (suffixLen > len ||
155
#ifdef MS_WINDOWS
156
                wcsicmp(&path[len - suffixLen], suffix) != 0
157
#else
158
0
                wcscmp(&path[len - suffixLen], suffix) != 0
159
0
#endif
160
0
            ) {
161
0
                r = Py_NewRef(Py_False);
162
0
            } else {
163
0
                r = Py_NewRef(Py_True);
164
0
            }
165
0
            PyMem_Free((void *)suffix);
166
0
        }
167
0
        PyMem_Free((void *)path);
168
0
    }
169
0
    return r;
170
0
}
171
172
173
static PyObject *
174
getpath_isdir(PyObject *Py_UNUSED(self), PyObject *args)
175
72
{
176
72
    PyObject *r = NULL;
177
72
    PyObject *pathobj;
178
72
    const wchar_t *path;
179
72
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
180
0
        return NULL;
181
0
    }
182
72
    path = PyUnicode_AsWideCharString(pathobj, NULL);
183
72
    if (path) {
184
#ifdef MS_WINDOWS
185
        DWORD attr = GetFileAttributesW(path);
186
        r = (attr != INVALID_FILE_ATTRIBUTES) &&
187
            (attr & FILE_ATTRIBUTE_DIRECTORY) ? Py_True : Py_False;
188
#else
189
72
        struct stat st;
190
72
        r = (_Py_wstat(path, &st) == 0) && S_ISDIR(st.st_mode) ? Py_True : Py_False;
191
72
#endif
192
72
        PyMem_Free((void *)path);
193
72
    }
194
72
    return Py_XNewRef(r);
195
72
}
196
197
198
static PyObject *
199
getpath_isfile(PyObject *Py_UNUSED(self), PyObject *args)
200
36
{
201
36
    PyObject *pathobj;
202
36
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
203
0
        return NULL;
204
0
    }
205
206
36
    int isfile;
207
#ifdef MS_WINDOWS
208
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
209
    if (path == NULL) {
210
        return NULL;
211
    }
212
213
    DWORD attr = GetFileAttributesW(path);
214
    PyMem_Free(path);
215
    isfile = ((attr != INVALID_FILE_ATTRIBUTES)
216
               && !(attr & FILE_ATTRIBUTE_DIRECTORY));
217
#else
218
36
    struct stat st;
219
36
    int res = _Py_stat(pathobj, &st);
220
36
    if (res == -2) {
221
0
        return NULL;
222
0
    }
223
36
    isfile = ((res == 0) && S_ISREG(st.st_mode));
224
36
#endif
225
36
    return PyBool_FromLong(isfile);
226
36
}
227
228
229
static PyObject *
230
getpath_isxfile(PyObject *Py_UNUSED(self), PyObject *args)
231
72
{
232
72
    PyObject *pathobj;
233
72
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
234
0
        return NULL;
235
0
    }
236
237
72
    int isxfile;
238
#ifdef MS_WINDOWS
239
    Py_ssize_t cchPath;
240
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, &cchPath);
241
    if (path == NULL) {
242
        return NULL;
243
    }
244
245
    DWORD attr = GetFileAttributesW(path);
246
    PyMem_Free(path);
247
    isxfile = (attr != INVALID_FILE_ATTRIBUTES) &&
248
              !(attr & FILE_ATTRIBUTE_DIRECTORY) &&
249
              (cchPath >= 4) &&
250
              (CompareStringOrdinal(path + cchPath - 4, -1, L".exe", -1, 1 /* ignore case */) == CSTR_EQUAL);
251
#else
252
72
    struct stat st;
253
72
    int res = _Py_stat(pathobj, &st);
254
72
    if (res == -2) {
255
0
        return NULL;
256
0
    }
257
72
    isxfile = ((res == 0)
258
72
               && S_ISREG(st.st_mode)
259
36
               && (st.st_mode & 0111));
260
72
#endif
261
72
    return PyBool_FromLong(isxfile);
262
72
}
263
264
265
static PyObject *
266
getpath_joinpath(PyObject *Py_UNUSED(self), PyObject *args)
267
288
{
268
288
    if (!PyTuple_Check(args)) {
269
0
        PyErr_SetString(PyExc_TypeError, "requires tuple of arguments");
270
0
        return NULL;
271
0
    }
272
288
    Py_ssize_t n = PyTuple_GET_SIZE(args);
273
288
    if (n == 0) {
274
0
        return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
275
0
    }
276
    /* Convert all parts to wchar and accumulate max final length */
277
288
    wchar_t **parts = (wchar_t **)PyMem_Malloc(n * sizeof(wchar_t *));
278
288
    if (parts == NULL) {
279
0
        PyErr_NoMemory();
280
0
        return NULL;
281
0
    }
282
288
    memset(parts, 0, n * sizeof(wchar_t *));
283
288
    Py_ssize_t cchFinal = 0;
284
288
    Py_ssize_t first = 0;
285
286
864
    for (Py_ssize_t i = 0; i < n; ++i) {
287
576
        PyObject *s = PyTuple_GET_ITEM(args, i);
288
576
        Py_ssize_t cch;
289
576
        if (s == Py_None) {
290
0
            cch = 0;
291
576
        } else if (PyUnicode_Check(s)) {
292
576
            parts[i] = PyUnicode_AsWideCharString(s, &cch);
293
576
            if (!parts[i]) {
294
0
                cchFinal = -1;
295
0
                break;
296
0
            }
297
576
            if (_Py_isabs(parts[i])) {
298
288
                first = i;
299
288
            }
300
576
        } else {
301
0
            PyErr_SetString(PyExc_TypeError, "all arguments to joinpath() must be str or None");
302
0
            cchFinal = -1;
303
0
            break;
304
0
        }
305
576
        cchFinal += cch + 1;
306
576
    }
307
308
288
    wchar_t *final = cchFinal > 0 ? (wchar_t *)PyMem_Malloc(cchFinal * sizeof(wchar_t)) : NULL;
309
288
    if (!final) {
310
0
        for (Py_ssize_t i = 0; i < n; ++i) {
311
0
            PyMem_Free(parts[i]);
312
0
        }
313
0
        PyMem_Free(parts);
314
0
        if (cchFinal) {
315
0
            PyErr_NoMemory();
316
0
            return NULL;
317
0
        }
318
0
        return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
319
0
    }
320
321
288
    final[0] = '\0';
322
    /* Now join all the paths. The final result should be shorter than the buffer */
323
864
    for (Py_ssize_t i = 0; i < n; ++i) {
324
576
        if (!parts[i]) {
325
0
            continue;
326
0
        }
327
576
        if (i >= first && final) {
328
576
            if (!final[0]) {
329
                /* final is definitely long enough to fit any individual part */
330
288
                wcscpy(final, parts[i]);
331
288
            } else if (_Py_add_relfile(final, parts[i], cchFinal) < 0) {
332
                /* if we fail, keep iterating to free memory, but stop adding parts */
333
0
                PyMem_Free(final);
334
0
                final = NULL;
335
0
            }
336
576
        }
337
576
        PyMem_Free(parts[i]);
338
576
    }
339
288
    PyMem_Free(parts);
340
288
    if (!final) {
341
0
        PyErr_SetString(PyExc_SystemError, "failed to join paths");
342
0
        return NULL;
343
0
    }
344
288
    PyObject *r = PyUnicode_FromWideChar(_Py_normpath(final, -1), -1);
345
288
    PyMem_Free(final);
346
288
    return r;
347
288
}
348
349
350
static PyObject *
351
getpath_readlines(PyObject *Py_UNUSED(self), PyObject *args)
352
180
{
353
180
    PyObject *pathobj;
354
180
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
355
0
        return NULL;
356
0
    }
357
180
    FILE *fp = Py_fopen(pathobj, "rb");
358
180
    if (!fp) {
359
180
        return NULL;
360
180
    }
361
362
0
    PyObject *r = PyList_New(0);
363
0
    if (!r) {
364
0
        fclose(fp);
365
0
        return NULL;
366
0
    }
367
0
    const size_t MAX_FILE = 32 * 1024;
368
0
    char *buffer = (char *)PyMem_Malloc(MAX_FILE);
369
0
    if (!buffer) {
370
0
        Py_DECREF(r);
371
0
        fclose(fp);
372
0
        PyErr_NoMemory();
373
0
        return NULL;
374
0
    }
375
376
0
    size_t cb = fread(buffer, 1, MAX_FILE, fp);
377
0
    fclose(fp);
378
0
    if (!cb) {
379
0
        return r;
380
0
    }
381
0
    if (cb >= MAX_FILE) {
382
0
        Py_DECREF(r);
383
0
        PyErr_SetString(PyExc_MemoryError,
384
0
            "cannot read file larger than 32KB during initialization");
385
0
        return NULL;
386
0
    }
387
0
    buffer[cb] = '\0';
388
389
0
    size_t len;
390
0
    wchar_t *wbuffer = _Py_DecodeUTF8_surrogateescape(buffer, cb, &len);
391
0
    PyMem_Free((void *)buffer);
392
0
    if (!wbuffer) {
393
0
        Py_DECREF(r);
394
0
        PyErr_NoMemory();
395
0
        return NULL;
396
0
    }
397
398
0
    wchar_t *p1 = wbuffer;
399
0
    wchar_t *p2 = p1;
400
0
    while ((p2 = wcschr(p1, L'\n')) != NULL) {
401
0
        Py_ssize_t cb = p2 - p1;
402
0
        while (cb >= 0 && (p1[cb] == L'\n' || p1[cb] == L'\r')) {
403
0
            --cb;
404
0
        }
405
0
        PyObject *u = PyUnicode_FromWideChar(p1, cb >= 0 ? cb + 1 : 0);
406
0
        if (!u || PyList_Append(r, u) < 0) {
407
0
            Py_XDECREF(u);
408
0
            Py_CLEAR(r);
409
0
            break;
410
0
        }
411
0
        Py_DECREF(u);
412
0
        p1 = p2 + 1;
413
0
    }
414
0
    if (r && p1 && *p1) {
415
0
        PyObject *u = PyUnicode_FromWideChar(p1, -1);
416
0
        if (!u || PyList_Append(r, u) < 0) {
417
0
            Py_CLEAR(r);
418
0
        }
419
0
        Py_XDECREF(u);
420
0
    }
421
0
    PyMem_RawFree(wbuffer);
422
0
    return r;
423
0
}
424
425
426
static PyObject *
427
getpath_realpath(PyObject *Py_UNUSED(self) , PyObject *args)
428
36
{
429
36
    PyObject *pathobj;
430
36
    if (!PyArg_ParseTuple(args, "U", &pathobj)) {
431
0
        return NULL;
432
0
    }
433
36
#if defined(HAVE_READLINK)
434
    /* This readlink calculation only resolves a symlinked file, and
435
       does not resolve any path segments. This is consistent with
436
       prior releases, however, the realpath implementation below is
437
       potentially correct in more cases. */
438
36
    PyObject *r = NULL;
439
36
    int nlink = 0;
440
36
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
441
36
    if (!path) {
442
0
        goto done;
443
0
    }
444
36
    wchar_t *path2 = _PyMem_RawWcsdup(path);
445
36
    PyMem_Free((void *)path);
446
36
    path = path2;
447
72
    while (path) {
448
72
        wchar_t resolved[MAXPATHLEN + 1];
449
72
        int linklen = _Py_wreadlink(path, resolved, Py_ARRAY_LENGTH(resolved));
450
72
        if (linklen == -1) {
451
36
            r = PyUnicode_FromWideChar(path, -1);
452
36
            break;
453
36
        }
454
36
        if (_Py_isabs(resolved)) {
455
36
            PyMem_RawFree((void *)path);
456
36
            path = _PyMem_RawWcsdup(resolved);
457
36
        } else {
458
0
            wchar_t *s = wcsrchr(path, SEP);
459
0
            if (s) {
460
0
                *s = L'\0';
461
0
            }
462
0
            path2 = _Py_join_relfile(path, resolved);
463
0
            if (path2) {
464
0
                path2 = _Py_normpath(path2, -1);
465
0
            }
466
0
            PyMem_RawFree((void *)path);
467
0
            path = path2;
468
0
        }
469
36
        nlink++;
470
        /* 40 is the Linux kernel 4.2 limit */
471
36
        if (nlink >= 40) {
472
0
            PyErr_SetString(PyExc_OSError, "maximum number of symbolic links reached");
473
0
            break;
474
0
        }
475
36
    }
476
36
    if (!path) {
477
0
        PyErr_NoMemory();
478
0
    }
479
36
done:
480
36
    PyMem_RawFree((void *)path);
481
36
    return r;
482
483
#elif defined(HAVE_REALPATH)
484
    PyObject *r = NULL;
485
    struct stat st;
486
    const char *narrow = NULL;
487
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
488
    if (!path) {
489
        goto done;
490
    }
491
    narrow = Py_EncodeLocale(path, NULL);
492
    if (!narrow) {
493
        PyErr_NoMemory();
494
        goto done;
495
    }
496
    if (lstat(narrow, &st)) {
497
        PyErr_SetFromErrno(PyExc_OSError);
498
        goto done;
499
    }
500
    if (!S_ISLNK(st.st_mode)) {
501
        r = Py_NewRef(pathobj);
502
        goto done;
503
    }
504
    wchar_t resolved[MAXPATHLEN+1];
505
    if (_Py_wrealpath(path, resolved, MAXPATHLEN) == NULL) {
506
        PyErr_SetFromErrno(PyExc_OSError);
507
    } else {
508
        r = PyUnicode_FromWideChar(resolved, -1);
509
    }
510
done:
511
    PyMem_Free((void *)path);
512
    PyMem_Free((void *)narrow);
513
    return r;
514
#elif defined(MS_WINDOWS)
515
    HANDLE hFile;
516
    wchar_t resolved[MAXPATHLEN+1];
517
    int len = 0, err;
518
    Py_ssize_t pathlen;
519
    PyObject *result;
520
521
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, &pathlen);
522
    if (!path) {
523
        return NULL;
524
    }
525
    if (wcslen(path) != pathlen) {
526
        PyErr_SetString(PyExc_ValueError, "path contains embedded nulls");
527
        return NULL;
528
    }
529
530
    Py_BEGIN_ALLOW_THREADS
531
    hFile = CreateFileW(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
532
    if (hFile != INVALID_HANDLE_VALUE) {
533
        len = GetFinalPathNameByHandleW(hFile, resolved, MAXPATHLEN, VOLUME_NAME_DOS);
534
        err = len ? 0 : GetLastError();
535
        CloseHandle(hFile);
536
    } else {
537
        err = GetLastError();
538
    }
539
    Py_END_ALLOW_THREADS
540
541
    if (err) {
542
        PyErr_SetFromWindowsErr(err);
543
        result = NULL;
544
    } else if (len <= MAXPATHLEN) {
545
        const wchar_t *p = resolved;
546
        if (0 == wcsncmp(p, L"\\\\?\\", 4)) {
547
            if (GetFileAttributesW(&p[4]) != INVALID_FILE_ATTRIBUTES) {
548
                p += 4;
549
                len -= 4;
550
            }
551
        }
552
        if (CompareStringOrdinal(path, (int)pathlen, p, len, TRUE) == CSTR_EQUAL) {
553
            result = Py_NewRef(pathobj);
554
        } else {
555
            result = PyUnicode_FromWideChar(p, len);
556
        }
557
    } else {
558
        result = Py_NewRef(pathobj);
559
    }
560
    PyMem_Free(path);
561
    return result;
562
#endif
563
564
0
    return Py_NewRef(pathobj);
565
36
}
566
567
568
static PyMethodDef getpath_methods[] = {
569
    {"abspath", getpath_abspath, METH_VARARGS, NULL},
570
    {"basename", getpath_basename, METH_VARARGS, NULL},
571
    {"dirname", getpath_dirname, METH_VARARGS, NULL},
572
    {"hassuffix", getpath_hassuffix, METH_VARARGS, NULL},
573
    {"isabs", getpath_isabs, METH_VARARGS, NULL},
574
    {"isdir", getpath_isdir, METH_VARARGS, NULL},
575
    {"isfile", getpath_isfile, METH_VARARGS, NULL},
576
    {"isxfile", getpath_isxfile, METH_VARARGS, NULL},
577
    {"joinpath", getpath_joinpath, METH_VARARGS, NULL},
578
    {"readlines", getpath_readlines, METH_VARARGS, NULL},
579
    {"realpath", getpath_realpath, METH_VARARGS, NULL},
580
    {NULL, NULL, 0, NULL}
581
};
582
583
584
/* Two implementations of warn() to use depending on whether warnings
585
   are enabled or not. */
586
587
static PyObject *
588
getpath_warn(PyObject *Py_UNUSED(self), PyObject *args)
589
0
{
590
0
    PyObject *msgobj;
591
0
    if (!PyArg_ParseTuple(args, "U", &msgobj)) {
592
0
        return NULL;
593
0
    }
594
0
    fprintf(stderr, "%s\n", PyUnicode_AsUTF8(msgobj));
595
0
    Py_RETURN_NONE;
596
0
}
597
598
599
static PyObject *
600
getpath_nowarn(PyObject *Py_UNUSED(self), PyObject *args)
601
0
{
602
0
    Py_RETURN_NONE;
603
0
}
604
605
606
static PyMethodDef getpath_warn_method = {"warn", getpath_warn, METH_VARARGS, NULL};
607
static PyMethodDef getpath_nowarn_method = {"warn", getpath_nowarn, METH_VARARGS, NULL};
608
609
/* Add the helper functions to the dict */
610
static int
611
funcs_to_dict(PyObject *dict, int warnings)
612
36
{
613
432
    for (PyMethodDef *m = getpath_methods; m->ml_name; ++m) {
614
396
        PyObject *f = PyCFunction_NewEx(m, NULL, NULL);
615
396
        if (!f) {
616
0
            return 0;
617
0
        }
618
396
        if (PyDict_SetItemString(dict, m->ml_name, f) < 0) {
619
0
            Py_DECREF(f);
620
0
            return 0;
621
0
        }
622
396
        Py_DECREF(f);
623
396
    }
624
36
    PyMethodDef *m2 = warnings ? &getpath_warn_method : &getpath_nowarn_method;
625
36
    PyObject *f = PyCFunction_NewEx(m2, NULL, NULL);
626
36
    if (!f) {
627
0
        return 0;
628
0
    }
629
36
    if (PyDict_SetItemString(dict, m2->ml_name, f) < 0) {
630
0
        Py_DECREF(f);
631
0
        return 0;
632
0
    }
633
36
    Py_DECREF(f);
634
36
    return 1;
635
36
}
636
637
638
/* Add a wide-character string constant to the dict */
639
static int
640
wchar_to_dict(PyObject *dict, const char *key, const wchar_t *s)
641
108
{
642
108
    PyObject *u;
643
108
    int r;
644
108
    if (s && s[0]) {
645
0
        u = PyUnicode_FromWideChar(s, -1);
646
0
        if (!u) {
647
0
            return 0;
648
0
        }
649
108
    } else {
650
108
        u = Py_NewRef(Py_None);
651
108
    }
652
108
    r = PyDict_SetItemString(dict, key, u) == 0;
653
108
    Py_DECREF(u);
654
108
    return r;
655
108
}
656
657
658
/* Add a narrow string constant to the dict, using default locale decoding */
659
static int
660
decode_to_dict(PyObject *dict, const char *key, const char *s)
661
324
{
662
324
    PyObject *u = NULL;
663
324
    int r;
664
324
    if (s && s[0]) {
665
144
        size_t len;
666
144
        const wchar_t *w = Py_DecodeLocale(s, &len);
667
144
        if (w) {
668
144
            u = PyUnicode_FromWideChar(w, len);
669
144
            PyMem_RawFree((void *)w);
670
144
        }
671
144
        if (!u) {
672
0
            return 0;
673
0
        }
674
180
    } else {
675
180
        u = Py_NewRef(Py_None);
676
180
    }
677
324
    r = PyDict_SetItemString(dict, key, u) == 0;
678
324
    Py_DECREF(u);
679
324
    return r;
680
324
}
681
682
/* Add an environment variable to the dict, optionally clearing it afterwards */
683
static int
684
env_to_dict(PyObject *dict, const char *key, int and_clear)
685
144
{
686
144
    PyObject *u = NULL;
687
144
    int r = 0;
688
144
    assert(strncmp(key, "ENV_", 4) == 0);
689
144
    assert(strlen(key) < 64);
690
#ifdef MS_WINDOWS
691
    wchar_t wkey[64];
692
    // Quick convert to wchar_t, since we know key is ASCII
693
    wchar_t *wp = wkey;
694
    for (const char *p = &key[4]; *p; ++p) {
695
        assert(!(*p & 0x80));
696
        *wp++ = *p;
697
    }
698
    *wp = L'\0';
699
    const wchar_t *v = _wgetenv(wkey);
700
    if (v) {
701
        u = PyUnicode_FromWideChar(v, -1);
702
        if (!u) {
703
            PyErr_Clear();
704
        }
705
    }
706
#else
707
144
    const char *v = getenv(&key[4]);
708
144
    if (v) {
709
72
        size_t len;
710
72
        const wchar_t *w = Py_DecodeLocale(v, &len);
711
72
        if (w) {
712
72
            u = PyUnicode_FromWideChar(w, len);
713
72
            if (!u) {
714
0
                PyErr_Clear();
715
0
            }
716
72
            PyMem_RawFree((void *)w);
717
72
        }
718
72
    }
719
144
#endif
720
144
    if (u) {
721
72
        r = PyDict_SetItemString(dict, key, u) == 0;
722
72
        Py_DECREF(u);
723
72
    } else {
724
72
        r = PyDict_SetItemString(dict, key, Py_None) == 0;
725
72
    }
726
144
    if (r && and_clear) {
727
#ifdef MS_WINDOWS
728
        _wputenv_s(wkey, L"");
729
#else
730
36
        unsetenv(&key[4]);
731
36
#endif
732
36
    }
733
144
    return r;
734
144
}
735
736
737
/* Add an integer constant to the dict */
738
static int
739
int_to_dict(PyObject *dict, const char *key, int v)
740
108
{
741
108
    PyObject *o;
742
108
    int r;
743
108
    o = PyLong_FromLong(v);
744
108
    if (!o) {
745
0
        return 0;
746
0
    }
747
108
    r = PyDict_SetItemString(dict, key, o) == 0;
748
108
    Py_DECREF(o);
749
108
    return r;
750
108
}
751
752
753
#ifdef MS_WINDOWS
754
static int
755
winmodule_to_dict(PyObject *dict, const char *key, HMODULE mod)
756
{
757
    wchar_t *buffer = NULL;
758
    for (DWORD cch = 256; buffer == NULL && cch < (1024 * 1024); cch *= 2) {
759
        buffer = (wchar_t*)PyMem_RawMalloc(cch * sizeof(wchar_t));
760
        if (buffer) {
761
            if (GetModuleFileNameW(mod, buffer, cch) == cch) {
762
                PyMem_RawFree(buffer);
763
                buffer = NULL;
764
            }
765
        }
766
    }
767
    int r = wchar_to_dict(dict, key, buffer);
768
    PyMem_RawFree(buffer);
769
    return r;
770
}
771
#endif
772
773
774
/* Add the current executable's path to the dict */
775
static int
776
progname_to_dict(PyObject *dict, const char *key)
777
36
{
778
#ifdef MS_WINDOWS
779
    return winmodule_to_dict(dict, key, NULL);
780
#elif defined(__APPLE__)
781
    char *path;
782
    uint32_t pathLen = 256;
783
    while (pathLen) {
784
        path = PyMem_RawMalloc((pathLen + 1) * sizeof(char));
785
        if (!path) {
786
            return 0;
787
        }
788
        if (_NSGetExecutablePath(path, &pathLen) != 0) {
789
            PyMem_RawFree(path);
790
            continue;
791
        }
792
        // Only keep if the path is absolute
793
        if (path[0] == SEP) {
794
            int r = decode_to_dict(dict, key, path);
795
            PyMem_RawFree(path);
796
            return r;
797
        }
798
        // Fall back and store None
799
        PyMem_RawFree(path);
800
        break;
801
    }
802
#endif
803
36
    return PyDict_SetItemString(dict, key, Py_None) == 0;
804
36
}
805
806
807
/* Add the runtime library's path to the dict */
808
static int
809
library_to_dict(PyObject *dict, const char *key)
810
36
{
811
/* macOS framework builds do not link against a libpython dynamic library, but
812
   instead link against a macOS Framework. */
813
#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK)
814
815
#ifdef MS_WINDOWS
816
    extern HMODULE PyWin_DLLhModule;
817
    if (PyWin_DLLhModule) {
818
        return winmodule_to_dict(dict, key, PyWin_DLLhModule);
819
    }
820
#endif
821
822
#if HAVE_DLADDR
823
    Dl_info libpython_info;
824
    if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) {
825
        return decode_to_dict(dict, key, libpython_info.dli_fname);
826
    }
827
#endif
828
#endif
829
830
36
    return PyDict_SetItemString(dict, key, Py_None) == 0;
831
36
}
832
833
834
PyObject *
835
_Py_Get_Getpath_CodeObject(void)
836
36
{
837
36
    return PyMarshal_ReadObjectFromString(
838
36
        (const char*)_Py_M__getpath, sizeof(_Py_M__getpath));
839
36
}
840
841
842
/* Perform the actual path calculation.
843
844
   When compute_path_config is 0, this only reads any initialised path
845
   config values into the PyConfig struct. For example, Py_SetHome() or
846
   Py_SetPath(). The only error should be due to failed memory allocation.
847
848
   When compute_path_config is 1, full path calculation is performed.
849
   The GIL must be held, and there may be filesystem access, side
850
   effects, and potential unraisable errors that are reported directly
851
   to stderr.
852
853
   Calling this function multiple times on the same PyConfig is only
854
   safe because already-configured values are not recalculated. To
855
   actually recalculate paths, you need a clean PyConfig.
856
*/
857
PyStatus
858
_PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
859
72
{
860
72
    PyStatus status = _PyPathConfig_ReadGlobal(config);
861
862
72
    if (_PyStatus_EXCEPTION(status) || !compute_path_config) {
863
36
        return status;
864
36
    }
865
866
36
    if (!_PyThreadState_GET()) {
867
0
        return PyStatus_Error("cannot calculate path configuration without GIL");
868
0
    }
869
870
36
    PyObject *configDict = _PyConfig_AsDict(config);
871
36
    if (!configDict) {
872
0
        PyErr_Clear();
873
0
        return PyStatus_NoMemory();
874
0
    }
875
876
36
    PyObject *dict = PyDict_New();
877
36
    if (!dict) {
878
0
        PyErr_Clear();
879
0
        Py_DECREF(configDict);
880
0
        return PyStatus_NoMemory();
881
0
    }
882
883
36
    if (PyDict_SetItemString(dict, "config", configDict) < 0) {
884
0
        PyErr_Clear();
885
0
        Py_DECREF(configDict);
886
0
        Py_DECREF(dict);
887
0
        return PyStatus_NoMemory();
888
0
    }
889
    /* reference now held by dict */
890
36
    Py_DECREF(configDict);
891
892
36
    PyObject *co = _Py_Get_Getpath_CodeObject();
893
36
    if (!co || !PyCode_Check(co)) {
894
0
        PyErr_Clear();
895
0
        Py_XDECREF(co);
896
0
        Py_DECREF(dict);
897
0
        return PyStatus_Error("error reading frozen getpath.py");
898
0
    }
899
900
#ifdef MS_WINDOWS
901
    PyObject *winreg = PyImport_ImportModule("winreg");
902
    if (!winreg || PyDict_SetItemString(dict, "winreg", winreg) < 0) {
903
        PyErr_Clear();
904
        Py_XDECREF(winreg);
905
        if (PyDict_SetItemString(dict, "winreg", Py_None) < 0) {
906
            PyErr_Clear();
907
            Py_DECREF(co);
908
            Py_DECREF(dict);
909
            return PyStatus_Error("error importing winreg module");
910
        }
911
    } else {
912
        Py_DECREF(winreg);
913
    }
914
#endif
915
916
36
    if (
917
#ifdef MS_WINDOWS
918
        !decode_to_dict(dict, "os_name", "nt") ||
919
#elif defined(__APPLE__)
920
        !decode_to_dict(dict, "os_name", "darwin") ||
921
#else
922
36
        !decode_to_dict(dict, "os_name", "posix") ||
923
36
#endif
924
#ifdef WITH_NEXT_FRAMEWORK
925
        !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 1) ||
926
#else
927
36
        !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 0) ||
928
36
#endif
929
36
        !decode_to_dict(dict, "PREFIX", PREFIX) ||
930
36
        !decode_to_dict(dict, "EXEC_PREFIX", EXEC_PREFIX) ||
931
36
        !decode_to_dict(dict, "PYTHONPATH", PYTHONPATH) ||
932
36
        !decode_to_dict(dict, "VPATH", VPATH) ||
933
36
        !decode_to_dict(dict, "PLATLIBDIR", PLATLIBDIR) ||
934
36
        !decode_to_dict(dict, "PYDEBUGEXT", PYDEBUGEXT) ||
935
36
        !int_to_dict(dict, "VERSION_MAJOR", PY_MAJOR_VERSION) ||
936
36
        !int_to_dict(dict, "VERSION_MINOR", PY_MINOR_VERSION) ||
937
36
        !decode_to_dict(dict, "PYWINVER", PYWINVER) ||
938
36
        !wchar_to_dict(dict, "EXE_SUFFIX", EXE_SUFFIX) ||
939
36
        !env_to_dict(dict, "ENV_PATH", 0) ||
940
36
        !env_to_dict(dict, "ENV_PYTHONHOME", 0) ||
941
36
        !env_to_dict(dict, "ENV_PYTHONEXECUTABLE", 0) ||
942
36
        !env_to_dict(dict, "ENV___PYVENV_LAUNCHER__", 1) ||
943
36
        !progname_to_dict(dict, "real_executable") ||
944
36
        !library_to_dict(dict, "library") ||
945
36
        !wchar_to_dict(dict, "executable_dir", NULL) ||
946
36
        !wchar_to_dict(dict, "py_setpath", _PyPathConfig_GetGlobalModuleSearchPath()) ||
947
36
        !funcs_to_dict(dict, config->pathconfig_warnings) ||
948
#ifdef Py_GIL_DISABLED
949
        !decode_to_dict(dict, "ABI_THREAD", "t") ||
950
#else
951
36
        !decode_to_dict(dict, "ABI_THREAD", "") ||
952
36
#endif
953
36
#ifndef MS_WINDOWS
954
36
        PyDict_SetItemString(dict, "winreg", Py_None) < 0 ||
955
36
#endif
956
36
        PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0
957
36
    ) {
958
0
        Py_DECREF(co);
959
0
        Py_DECREF(dict);
960
0
        PyErr_FormatUnraisable("Exception ignored while preparing getpath");
961
0
        return PyStatus_Error("error evaluating initial values");
962
0
    }
963
964
36
    PyObject *r = PyEval_EvalCode(co, dict, dict);
965
36
    Py_DECREF(co);
966
967
36
    if (!r) {
968
0
        Py_DECREF(dict);
969
0
        PyErr_FormatUnraisable("Exception ignored while running getpath");
970
0
        return PyStatus_Error("error evaluating path");
971
0
    }
972
36
    Py_DECREF(r);
973
974
36
    if (_PyConfig_FromDict(config, configDict) < 0) {
975
0
        PyErr_FormatUnraisable("Exception ignored while reading getpath results");
976
0
        Py_DECREF(dict);
977
0
        return PyStatus_Error("error getting getpath results");
978
0
    }
979
980
36
    Py_DECREF(dict);
981
982
36
    return _PyStatus_OK();
983
36
}