Coverage Report

Created: 2025-10-13 06:15

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