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