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