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