Coverage Report

Created: 2026-10-31 06:21

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
        PyMem_Free(buffer);
413
0
        return r;
414
0
    }
415
0
    if (cb >= MAX_FILE) {
416
0
        PyMem_Free(buffer);
417
0
        Py_DECREF(r);
418
0
        PyErr_SetString(PyExc_MemoryError,
419
0
            "cannot read file larger than 32KB during initialization");
420
0
        return NULL;
421
0
    }
422
0
    buffer[cb] = '\0';
423
424
0
    size_t len;
425
0
    wchar_t *wbuffer = _Py_DecodeUTF8_surrogateescape(buffer, cb, &len);
426
0
    PyMem_Free((void *)buffer);
427
0
    if (!wbuffer) {
428
0
        Py_DECREF(r);
429
0
        PyErr_NoMemory();
430
0
        return NULL;
431
0
    }
432
433
0
    wchar_t *p1 = wbuffer;
434
0
    wchar_t *p2 = p1;
435
0
    while ((p2 = wcschr(p1, L'\n')) != NULL) {
436
0
        Py_ssize_t cb = p2 - p1;
437
0
        while (cb >= 0 && (p1[cb] == L'\n' || p1[cb] == L'\r')) {
438
0
            --cb;
439
0
        }
440
0
        PyObject *u = PyUnicode_FromWideChar(p1, cb >= 0 ? cb + 1 : 0);
441
0
        if (!u || PyList_Append(r, u) < 0) {
442
0
            Py_XDECREF(u);
443
0
            Py_CLEAR(r);
444
0
            break;
445
0
        }
446
0
        Py_DECREF(u);
447
0
        p1 = p2 + 1;
448
0
    }
449
0
    if (r && p1 && *p1) {
450
0
        PyObject *u = PyUnicode_FromWideChar(p1, -1);
451
0
        if (!u || PyList_Append(r, u) < 0) {
452
0
            Py_CLEAR(r);
453
0
        }
454
0
        Py_XDECREF(u);
455
0
    }
456
0
    PyMem_RawFree(wbuffer);
457
0
    return r;
458
0
}
459
460
461
/*[clinic input]
462
getpath.realpath
463
464
    path as pathobj: unicode
465
    /
466
467
Resolve a symlinked file.
468
[clinic start generated code]*/
469
470
static PyObject *
471
getpath_realpath_impl(PyObject *module, PyObject *pathobj)
472
/*[clinic end generated code: output=8559260e3786874e input=d3c5eee0ddd2c3e2]*/
473
37
{
474
37
#if defined(HAVE_READLINK)
475
    /* This readlink calculation only resolves a symlinked file, and
476
       does not resolve any path segments. This is consistent with
477
       prior releases, however, the realpath implementation below is
478
       potentially correct in more cases. */
479
37
    PyObject *r = NULL;
480
37
    int nlink = 0;
481
37
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
482
37
    if (!path) {
483
0
        goto done;
484
0
    }
485
37
    wchar_t *path2 = _PyMem_RawWcsdup(path);
486
37
    PyMem_Free((void *)path);
487
37
    path = path2;
488
74
    while (path) {
489
74
        wchar_t resolved[MAXPATHLEN + 1];
490
74
        int linklen = _Py_wreadlink(path, resolved, Py_ARRAY_LENGTH(resolved));
491
74
        if (linklen == -1) {
492
37
            r = PyUnicode_FromWideChar(path, -1);
493
37
            break;
494
37
        }
495
37
        if (_Py_isabs(resolved)) {
496
37
            PyMem_RawFree((void *)path);
497
37
            path = _PyMem_RawWcsdup(resolved);
498
37
        } else {
499
0
            wchar_t *s = wcsrchr(path, SEP);
500
0
            if (s) {
501
0
                *s = L'\0';
502
0
            }
503
0
            path2 = _Py_join_relfile(path, resolved);
504
0
            if (path2) {
505
0
                path2 = _Py_normpath(path2, -1);
506
0
            }
507
0
            PyMem_RawFree((void *)path);
508
0
            path = path2;
509
0
        }
510
37
        nlink++;
511
        /* 40 is the Linux kernel 4.2 limit */
512
37
        if (nlink >= 40) {
513
0
            PyErr_SetString(PyExc_OSError, "maximum number of symbolic links reached");
514
0
            break;
515
0
        }
516
37
    }
517
37
    if (!path) {
518
0
        PyErr_NoMemory();
519
0
    }
520
37
done:
521
37
    PyMem_RawFree((void *)path);
522
37
    return r;
523
524
#elif defined(HAVE_REALPATH)
525
    PyObject *r = NULL;
526
    struct stat st;
527
    const char *narrow = NULL;
528
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, NULL);
529
    if (!path) {
530
        goto done;
531
    }
532
    narrow = Py_EncodeLocale(path, NULL);
533
    if (!narrow) {
534
        PyErr_NoMemory();
535
        goto done;
536
    }
537
    if (lstat(narrow, &st)) {
538
        PyErr_SetFromErrno(PyExc_OSError);
539
        goto done;
540
    }
541
    if (!S_ISLNK(st.st_mode)) {
542
        r = Py_NewRef(pathobj);
543
        goto done;
544
    }
545
    wchar_t resolved[MAXPATHLEN+1];
546
    if (_Py_wrealpath(path, resolved, MAXPATHLEN) == NULL) {
547
        PyErr_SetFromErrno(PyExc_OSError);
548
    } else {
549
        r = PyUnicode_FromWideChar(resolved, -1);
550
    }
551
done:
552
    PyMem_Free((void *)path);
553
    PyMem_Free((void *)narrow);
554
    return r;
555
#elif defined(MS_WINDOWS)
556
    HANDLE hFile;
557
    wchar_t resolved[MAXPATHLEN+1];
558
    int len = 0, err;
559
    Py_ssize_t pathlen;
560
    PyObject *result;
561
562
    wchar_t *path = PyUnicode_AsWideCharString(pathobj, &pathlen);
563
    if (!path) {
564
        return NULL;
565
    }
566
    if (wcslen(path) != pathlen) {
567
        PyErr_SetString(PyExc_ValueError, "path contains embedded nulls");
568
        return NULL;
569
    }
570
571
    Py_BEGIN_ALLOW_THREADS
572
    hFile = CreateFileW(path, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
573
    if (hFile != INVALID_HANDLE_VALUE) {
574
        len = GetFinalPathNameByHandleW(hFile, resolved, MAXPATHLEN, VOLUME_NAME_DOS);
575
        err = len ? 0 : GetLastError();
576
        CloseHandle(hFile);
577
    } else {
578
        err = GetLastError();
579
    }
580
    Py_END_ALLOW_THREADS
581
582
    if (err) {
583
        PyErr_SetFromWindowsErr(err);
584
        result = NULL;
585
    } else if (len <= MAXPATHLEN) {
586
        const wchar_t *p = resolved;
587
        if (0 == wcsncmp(p, L"\\\\?\\", 4)) {
588
            if (GetFileAttributesW(&p[4]) != INVALID_FILE_ATTRIBUTES) {
589
                p += 4;
590
                len -= 4;
591
            }
592
        }
593
        if (CompareStringOrdinal(path, (int)pathlen, p, len, TRUE) == CSTR_EQUAL) {
594
            result = Py_NewRef(pathobj);
595
        } else {
596
            result = PyUnicode_FromWideChar(p, len);
597
        }
598
    } else {
599
        result = Py_NewRef(pathobj);
600
    }
601
    PyMem_Free(path);
602
    return result;
603
#endif
604
605
0
    return Py_NewRef(pathobj);
606
37
}
607
608
609
static PyMethodDef getpath_methods[] = {
610
    GETPATH_ABSPATH_METHODDEF
611
    GETPATH_BASENAME_METHODDEF
612
    GETPATH_DIRNAME_METHODDEF
613
    GETPATH_HASSUFFIX_METHODDEF
614
    GETPATH_ISABS_METHODDEF
615
    GETPATH_ISDIR_METHODDEF
616
    GETPATH_ISFILE_METHODDEF
617
    GETPATH_ISXFILE_METHODDEF
618
    GETPATH_JOINPATH_METHODDEF
619
    GETPATH_READLINES_METHODDEF
620
    GETPATH_REALPATH_METHODDEF
621
    {NULL, NULL, 0, NULL}
622
};
623
624
625
/* Two implementations of warn() to use depending on whether warnings
626
   are enabled or not. */
627
628
static PyObject *
629
getpath_warn(PyObject *Py_UNUSED(self), PyObject *args)
630
0
{
631
0
    PyObject *msgobj;
632
0
    if (!PyArg_ParseTuple(args, "U", &msgobj)) {
633
0
        return NULL;
634
0
    }
635
0
    const char *msg = PyUnicode_AsUTF8(msgobj);
636
0
    if (msg == NULL) {
637
0
        return NULL;
638
0
    }
639
0
    fprintf(stderr, "%s\n", msg);
640
0
    Py_RETURN_NONE;
641
0
}
642
643
644
static PyObject *
645
getpath_nowarn(PyObject *Py_UNUSED(self), PyObject *args)
646
0
{
647
0
    Py_RETURN_NONE;
648
0
}
649
650
651
static PyMethodDef getpath_warn_method = {"warn", getpath_warn, METH_VARARGS, NULL};
652
static PyMethodDef getpath_nowarn_method = {"warn", getpath_nowarn, METH_VARARGS, NULL};
653
654
/* Add the helper functions to the dict */
655
static int
656
funcs_to_dict(PyObject *dict, int warnings)
657
37
{
658
444
    for (PyMethodDef *m = getpath_methods; m->ml_name; ++m) {
659
407
        PyObject *f = PyCFunction_NewEx(m, NULL, NULL);
660
407
        if (!f) {
661
0
            return 0;
662
0
        }
663
407
        if (PyDict_SetItemString(dict, m->ml_name, f) < 0) {
664
0
            Py_DECREF(f);
665
0
            return 0;
666
0
        }
667
407
        Py_DECREF(f);
668
407
    }
669
37
    PyMethodDef *m2 = warnings ? &getpath_warn_method : &getpath_nowarn_method;
670
37
    PyObject *f = PyCFunction_NewEx(m2, NULL, NULL);
671
37
    if (!f) {
672
0
        return 0;
673
0
    }
674
37
    if (PyDict_SetItemString(dict, m2->ml_name, f) < 0) {
675
0
        Py_DECREF(f);
676
0
        return 0;
677
0
    }
678
37
    Py_DECREF(f);
679
37
    return 1;
680
37
}
681
682
683
/* Add a wide-character string constant to the dict */
684
static int
685
wchar_to_dict(PyObject *dict, const char *key, const wchar_t *s)
686
111
{
687
111
    PyObject *u;
688
111
    int r;
689
111
    if (s && s[0]) {
690
0
        u = PyUnicode_FromWideChar(s, -1);
691
0
        if (!u) {
692
0
            return 0;
693
0
        }
694
111
    } else {
695
111
        u = Py_NewRef(Py_None);
696
111
    }
697
111
    r = PyDict_SetItemString(dict, key, u) == 0;
698
111
    Py_DECREF(u);
699
111
    return r;
700
111
}
701
702
703
/* Add a narrow string constant to the dict, using default locale decoding */
704
static int
705
decode_to_dict(PyObject *dict, const char *key, const char *s)
706
333
{
707
333
    PyObject *u = NULL;
708
333
    int r;
709
333
    if (s && s[0]) {
710
148
        size_t len;
711
148
        const wchar_t *w = Py_DecodeLocale(s, &len);
712
148
        if (w) {
713
148
            u = PyUnicode_FromWideChar(w, len);
714
148
            PyMem_RawFree((void *)w);
715
148
        }
716
148
        if (!u) {
717
0
            return 0;
718
0
        }
719
185
    } else {
720
185
        u = Py_NewRef(Py_None);
721
185
    }
722
333
    r = PyDict_SetItemString(dict, key, u) == 0;
723
333
    Py_DECREF(u);
724
333
    return r;
725
333
}
726
727
/* Add an environment variable to the dict, optionally clearing it afterwards */
728
static int
729
env_to_dict(PyObject *dict, const char *key, int and_clear)
730
148
{
731
148
    PyObject *u = NULL;
732
148
    int r = 0;
733
148
    assert(strncmp(key, "ENV_", 4) == 0);
734
148
    assert(strlen(key) < 64);
735
#ifdef MS_WINDOWS
736
    wchar_t wkey[64];
737
    // Quick convert to wchar_t, since we know key is ASCII
738
    wchar_t *wp = wkey;
739
    for (const char *p = &key[4]; *p; ++p) {
740
        assert(!(*p & 0x80));
741
        *wp++ = *p;
742
    }
743
    *wp = L'\0';
744
    const wchar_t *v = _wgetenv(wkey);
745
    if (v) {
746
        u = PyUnicode_FromWideChar(v, -1);
747
        if (!u) {
748
            PyErr_Clear();
749
        }
750
    }
751
#else
752
148
    const char *v = getenv(&key[4]);
753
148
    if (v) {
754
74
        size_t len;
755
74
        const wchar_t *w = Py_DecodeLocale(v, &len);
756
74
        if (w) {
757
74
            u = PyUnicode_FromWideChar(w, len);
758
74
            if (!u) {
759
0
                PyErr_Clear();
760
0
            }
761
74
            PyMem_RawFree((void *)w);
762
74
        }
763
74
    }
764
148
#endif
765
148
    if (u) {
766
74
        r = PyDict_SetItemString(dict, key, u) == 0;
767
74
        Py_DECREF(u);
768
74
    } else {
769
74
        r = PyDict_SetItemString(dict, key, Py_None) == 0;
770
74
    }
771
148
    if (r && and_clear) {
772
#ifdef MS_WINDOWS
773
        _wputenv_s(wkey, L"");
774
#else
775
37
        unsetenv(&key[4]);
776
37
#endif
777
37
    }
778
148
    return r;
779
148
}
780
781
782
/* Add an integer constant to the dict */
783
static int
784
int_to_dict(PyObject *dict, const char *key, int v)
785
111
{
786
111
    PyObject *o;
787
111
    int r;
788
111
    o = PyLong_FromLong(v);
789
111
    if (!o) {
790
0
        return 0;
791
0
    }
792
111
    r = PyDict_SetItemString(dict, key, o) == 0;
793
111
    Py_DECREF(o);
794
111
    return r;
795
111
}
796
797
798
#ifdef MS_WINDOWS
799
static int
800
winmodule_to_dict(PyObject *dict, const char *key, HMODULE mod)
801
{
802
    wchar_t *buffer = NULL;
803
    for (DWORD cch = 256; buffer == NULL && cch < (1024 * 1024); cch *= 2) {
804
        buffer = (wchar_t*)PyMem_RawMalloc(cch * sizeof(wchar_t));
805
        if (buffer) {
806
            if (GetModuleFileNameW(mod, buffer, cch) == cch) {
807
                PyMem_RawFree(buffer);
808
                buffer = NULL;
809
            }
810
        }
811
    }
812
    int r = wchar_to_dict(dict, key, buffer);
813
    PyMem_RawFree(buffer);
814
    return r;
815
}
816
#endif
817
818
819
/* Add the current executable's path to the dict */
820
static int
821
progname_to_dict(PyObject *dict, const char *key)
822
37
{
823
#ifdef MS_WINDOWS
824
    return winmodule_to_dict(dict, key, NULL);
825
#elif defined(__APPLE__)
826
    char *path;
827
    uint32_t pathLen = 256;
828
    while (pathLen) {
829
        path = PyMem_RawMalloc((pathLen + 1) * sizeof(char));
830
        if (!path) {
831
            return 0;
832
        }
833
        if (_NSGetExecutablePath(path, &pathLen) != 0) {
834
            PyMem_RawFree(path);
835
            continue;
836
        }
837
        // Only keep if the path is absolute
838
        if (path[0] == SEP) {
839
            int r = decode_to_dict(dict, key, path);
840
            PyMem_RawFree(path);
841
            return r;
842
        }
843
        // Fall back and store None
844
        PyMem_RawFree(path);
845
        break;
846
    }
847
#endif
848
37
    return PyDict_SetItemString(dict, key, Py_None) == 0;
849
37
}
850
851
852
/* Add the runtime library's path to the dict */
853
static int
854
library_to_dict(PyObject *dict, const char *key)
855
37
{
856
/* macOS framework builds do not link against a libpython dynamic library, but
857
   instead link against a macOS Framework. */
858
#if defined(Py_ENABLE_SHARED) || defined(WITH_NEXT_FRAMEWORK)
859
860
#ifdef MS_WINDOWS
861
    extern HMODULE PyWin_DLLhModule;
862
    if (PyWin_DLLhModule) {
863
        return winmodule_to_dict(dict, key, PyWin_DLLhModule);
864
    }
865
#endif
866
867
#if HAVE_DLADDR
868
    Dl_info libpython_info;
869
    if (dladdr(&Py_Initialize, &libpython_info) && libpython_info.dli_fname) {
870
        return decode_to_dict(dict, key, libpython_info.dli_fname);
871
    }
872
#endif
873
#endif
874
875
37
    return PyDict_SetItemString(dict, key, Py_None) == 0;
876
37
}
877
878
879
PyObject *
880
_Py_Get_Getpath_CodeObject(void)
881
37
{
882
37
    return PyMarshal_ReadObjectFromString(
883
37
        (const char*)_Py_M__getpath, sizeof(_Py_M__getpath));
884
37
}
885
886
887
/* Perform the actual path calculation.
888
889
   When compute_path_config is 0, this only reads any initialised path
890
   config values into the PyConfig struct. For example, Py_SetHome() or
891
   Py_SetPath(). The only error should be due to failed memory allocation.
892
893
   When compute_path_config is 1, full path calculation is performed.
894
   The GIL must be held, and there may be filesystem access, side
895
   effects, and potential unraisable errors that are reported directly
896
   to stderr.
897
898
   Calling this function multiple times on the same PyConfig is only
899
   safe because already-configured values are not recalculated. To
900
   actually recalculate paths, you need a clean PyConfig.
901
*/
902
PyStatus
903
_PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
904
74
{
905
74
    PyStatus status = _PyPathConfig_ReadGlobal(config);
906
907
74
    if (_PyStatus_EXCEPTION(status) || !compute_path_config) {
908
37
        return status;
909
37
    }
910
911
37
    if (!_PyThreadState_GET()) {
912
0
        return PyStatus_Error("cannot calculate path configuration without GIL");
913
0
    }
914
915
37
    PyObject *configDict = _PyConfig_AsDict(config);
916
37
    if (!configDict) {
917
0
        PyErr_Clear();
918
0
        return PyStatus_NoMemory();
919
0
    }
920
921
37
    PyObject *dict = PyDict_New();
922
37
    if (!dict) {
923
0
        PyErr_Clear();
924
0
        Py_DECREF(configDict);
925
0
        return PyStatus_NoMemory();
926
0
    }
927
928
37
    if (PyDict_SetItemString(dict, "config", configDict) < 0) {
929
0
        PyErr_Clear();
930
0
        Py_DECREF(configDict);
931
0
        Py_DECREF(dict);
932
0
        return PyStatus_NoMemory();
933
0
    }
934
    /* reference now held by dict */
935
37
    Py_DECREF(configDict);
936
937
37
    PyObject *co = _Py_Get_Getpath_CodeObject();
938
37
    if (!co || !PyCode_Check(co)) {
939
0
        PyErr_Clear();
940
0
        Py_XDECREF(co);
941
0
        Py_DECREF(dict);
942
0
        return PyStatus_Error("error reading frozen getpath.py");
943
0
    }
944
945
#ifdef MS_WINDOWS
946
    PyObject *winreg = PyImport_ImportModule("winreg");
947
    if (!winreg || PyDict_SetItemString(dict, "winreg", winreg) < 0) {
948
        PyErr_Clear();
949
        Py_XDECREF(winreg);
950
        if (PyDict_SetItemString(dict, "winreg", Py_None) < 0) {
951
            PyErr_Clear();
952
            Py_DECREF(co);
953
            Py_DECREF(dict);
954
            return PyStatus_Error("error importing winreg module");
955
        }
956
    } else {
957
        Py_DECREF(winreg);
958
    }
959
#endif
960
961
37
    if (
962
#ifdef MS_WINDOWS
963
        !decode_to_dict(dict, "os_name", "nt") ||
964
#elif defined(__APPLE__)
965
        !decode_to_dict(dict, "os_name", "darwin") ||
966
#else
967
37
        !decode_to_dict(dict, "os_name", "posix") ||
968
37
#endif
969
#ifdef WITH_NEXT_FRAMEWORK
970
        !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 1) ||
971
#else
972
37
        !int_to_dict(dict, "WITH_NEXT_FRAMEWORK", 0) ||
973
37
#endif
974
37
        !decode_to_dict(dict, "PREFIX", PREFIX) ||
975
37
        !decode_to_dict(dict, "EXEC_PREFIX", EXEC_PREFIX) ||
976
37
        !decode_to_dict(dict, "PYTHONPATH", PYTHONPATH) ||
977
37
        !decode_to_dict(dict, "VPATH", VPATH) ||
978
37
        !decode_to_dict(dict, "PLATLIBDIR", PLATLIBDIR) ||
979
37
        !decode_to_dict(dict, "PYDEBUGEXT", PYDEBUGEXT) ||
980
37
        !int_to_dict(dict, "VERSION_MAJOR", PY_MAJOR_VERSION) ||
981
37
        !int_to_dict(dict, "VERSION_MINOR", PY_MINOR_VERSION) ||
982
37
        !decode_to_dict(dict, "PYWINVER", PYWINVER) ||
983
37
        !wchar_to_dict(dict, "EXE_SUFFIX", EXE_SUFFIX) ||
984
37
        !env_to_dict(dict, "ENV_PATH", 0) ||
985
37
        !env_to_dict(dict, "ENV_PYTHONHOME", 0) ||
986
37
        !env_to_dict(dict, "ENV_PYTHONEXECUTABLE", 0) ||
987
37
        !env_to_dict(dict, "ENV___PYVENV_LAUNCHER__", 1) ||
988
37
        !progname_to_dict(dict, "real_executable") ||
989
37
        !library_to_dict(dict, "library") ||
990
37
        !wchar_to_dict(dict, "executable_dir", NULL) ||
991
37
        !wchar_to_dict(dict, "py_setpath", _PyPathConfig_GetGlobalModuleSearchPath()) ||
992
37
        !funcs_to_dict(dict, config->pathconfig_warnings) ||
993
#ifdef Py_GIL_DISABLED
994
        !decode_to_dict(dict, "ABI_THREAD", "t") ||
995
#else
996
37
        !decode_to_dict(dict, "ABI_THREAD", "") ||
997
37
#endif
998
37
#ifndef MS_WINDOWS
999
37
        PyDict_SetItemString(dict, "winreg", Py_None) < 0 ||
1000
37
#endif
1001
37
        PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0
1002
37
    ) {
1003
0
        Py_DECREF(co);
1004
0
        Py_DECREF(dict);
1005
0
        PyErr_FormatUnraisable("Exception ignored while preparing getpath");
1006
0
        return PyStatus_Error("error evaluating initial values");
1007
0
    }
1008
1009
37
    PyObject *r = PyEval_EvalCode(co, dict, dict);
1010
37
    Py_DECREF(co);
1011
1012
37
    if (!r) {
1013
0
        Py_DECREF(dict);
1014
0
        PyErr_FormatUnraisable("Exception ignored while running getpath");
1015
0
        return PyStatus_Error("error evaluating path");
1016
0
    }
1017
37
    Py_DECREF(r);
1018
1019
37
    if (_PyConfig_FromDict(config, configDict) < 0) {
1020
0
        PyErr_FormatUnraisable("Exception ignored while reading getpath results");
1021
0
        Py_DECREF(dict);
1022
0
        return PyStatus_Error("error getting getpath results");
1023
0
    }
1024
1025
37
    Py_DECREF(dict);
1026
1027
37
    return _PyStatus_OK();
1028
37
}