Coverage Report

Created: 2026-10-17 06:15

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