1 /* Path configuration like module_search_path (sys.path) */
2 
3 #include "Python.h"
4 #include "osdefs.h"
5 #include "internal/pystate.h"
6 #include <wchar.h>
7 
8 #ifdef __cplusplus
9 extern "C" {
10 #endif
11 
12 
13 _PyPathConfig _Py_path_config = _PyPathConfig_INIT;
14 
15 
16 void
_PyPathConfig_Clear(_PyPathConfig * config)17 _PyPathConfig_Clear(_PyPathConfig *config)
18 {
19     /* _PyMem_SetDefaultAllocator() is needed to get a known memory allocator,
20        since Py_SetPath(), Py_SetPythonHome() and Py_SetProgramName() can be
21        called before Py_Initialize() which can changes the memory allocator. */
22     PyMemAllocatorEx old_alloc;
23     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
24 
25 #define CLEAR(ATTR) \
26     do { \
27         PyMem_RawFree(ATTR); \
28         ATTR = NULL; \
29     } while (0)
30 
31     CLEAR(config->prefix);
32     CLEAR(config->program_full_path);
33 #ifdef MS_WINDOWS
34     CLEAR(config->dll_path);
35 #else
36     CLEAR(config->exec_prefix);
37 #endif
38     CLEAR(config->module_search_path);
39     CLEAR(config->home);
40     CLEAR(config->program_name);
41 #undef CLEAR
42 
43     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
44 }
45 
46 
47 /* Initialize paths for Py_GetPath(), Py_GetPrefix(), Py_GetExecPrefix()
48    and Py_GetProgramFullPath() */
49 _PyInitError
_PyPathConfig_Init(const _PyCoreConfig * core_config)50 _PyPathConfig_Init(const _PyCoreConfig *core_config)
51 {
52     if (_Py_path_config.module_search_path) {
53         /* Already initialized */
54         return _Py_INIT_OK();
55     }
56 
57     _PyInitError err;
58     _PyPathConfig new_config = _PyPathConfig_INIT;
59 
60     PyMemAllocatorEx old_alloc;
61     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
62 
63     /* Calculate program_full_path, prefix, exec_prefix (Unix)
64        or dll_path (Windows), and module_search_path */
65     err = _PyPathConfig_Calculate(&new_config, core_config);
66     if (_Py_INIT_FAILED(err)) {
67         _PyPathConfig_Clear(&new_config);
68         goto done;
69     }
70 
71     /* Copy home and program_name from core_config */
72     if (core_config->home != NULL) {
73         new_config.home = _PyMem_RawWcsdup(core_config->home);
74         if (new_config.home == NULL) {
75             err = _Py_INIT_NO_MEMORY();
76             goto done;
77         }
78     }
79     else {
80         new_config.home = NULL;
81     }
82 
83     new_config.program_name = _PyMem_RawWcsdup(core_config->program_name);
84     if (new_config.program_name == NULL) {
85         err = _Py_INIT_NO_MEMORY();
86         goto done;
87     }
88 
89     _PyPathConfig_Clear(&_Py_path_config);
90     _Py_path_config = new_config;
91 
92     err = _Py_INIT_OK();
93 
94 done:
95     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
96     return err;
97 }
98 
99 
100 static void
pathconfig_global_init(void)101 pathconfig_global_init(void)
102 {
103     if (_Py_path_config.module_search_path) {
104         /* Already initialized */
105         return;
106     }
107 
108     _PyInitError err;
109     _PyCoreConfig config = _PyCoreConfig_INIT;
110 
111     err = _PyCoreConfig_Read(&config);
112     if (_Py_INIT_FAILED(err)) {
113         goto error;
114     }
115 
116     err = _PyPathConfig_Init(&config);
117     if (_Py_INIT_FAILED(err)) {
118         goto error;
119     }
120 
121     _PyCoreConfig_Clear(&config);
122     return;
123 
124 error:
125     _PyCoreConfig_Clear(&config);
126     _Py_FatalInitError(err);
127 }
128 
129 
130 /* External interface */
131 
132 void
Py_SetPath(const wchar_t * path)133 Py_SetPath(const wchar_t *path)
134 {
135     if (path == NULL) {
136         _PyPathConfig_Clear(&_Py_path_config);
137         return;
138     }
139 
140     PyMemAllocatorEx old_alloc;
141     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
142 
143     _PyPathConfig new_config;
144     new_config.program_full_path = _PyMem_RawWcsdup(Py_GetProgramName());
145     new_config.prefix = _PyMem_RawWcsdup(L"");
146 #ifdef MS_WINDOWS
147     new_config.dll_path = _PyMem_RawWcsdup(L"");
148 #else
149     new_config.exec_prefix = _PyMem_RawWcsdup(L"");
150 #endif
151     new_config.module_search_path = _PyMem_RawWcsdup(path);
152 
153     /* steal the home and program_name values (to leave them unchanged) */
154     new_config.home = _Py_path_config.home;
155     _Py_path_config.home = NULL;
156     new_config.program_name = _Py_path_config.program_name;
157     _Py_path_config.program_name = NULL;
158 
159     _PyPathConfig_Clear(&_Py_path_config);
160     _Py_path_config = new_config;
161 
162     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
163 }
164 
165 
166 void
Py_SetPythonHome(const wchar_t * home)167 Py_SetPythonHome(const wchar_t *home)
168 {
169     if (home == NULL) {
170         return;
171     }
172 
173     PyMemAllocatorEx old_alloc;
174     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
175 
176     PyMem_RawFree(_Py_path_config.home);
177     _Py_path_config.home = _PyMem_RawWcsdup(home);
178 
179     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
180 
181     if (_Py_path_config.home == NULL) {
182         Py_FatalError("Py_SetPythonHome() failed: out of memory");
183     }
184 }
185 
186 
187 void
Py_SetProgramName(const wchar_t * program_name)188 Py_SetProgramName(const wchar_t *program_name)
189 {
190     if (program_name == NULL || program_name[0] == L'\0') {
191         return;
192     }
193 
194     PyMemAllocatorEx old_alloc;
195     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
196 
197     PyMem_RawFree(_Py_path_config.program_name);
198     _Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
199 
200     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
201 
202     if (_Py_path_config.program_name == NULL) {
203         Py_FatalError("Py_SetProgramName() failed: out of memory");
204     }
205 }
206 
207 
208 void
_Py_SetProgramFullPath(const wchar_t * program_full_path)209 _Py_SetProgramFullPath(const wchar_t *program_full_path)
210 {
211     if (program_full_path == NULL || program_full_path[0] == L'\0') {
212         return;
213     }
214 
215     PyMemAllocatorEx old_alloc;
216     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
217 
218     PyMem_RawFree(_Py_path_config.program_full_path);
219     _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
220 
221     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
222 
223     if (_Py_path_config.program_full_path == NULL) {
224         Py_FatalError("Py_SetProgramFullPath() failed: out of memory");
225     }
226 }
227 
228 
229 wchar_t *
Py_GetPath(void)230 Py_GetPath(void)
231 {
232     pathconfig_global_init();
233     return _Py_path_config.module_search_path;
234 }
235 
236 
237 wchar_t *
Py_GetPrefix(void)238 Py_GetPrefix(void)
239 {
240     pathconfig_global_init();
241     return _Py_path_config.prefix;
242 }
243 
244 
245 wchar_t *
Py_GetExecPrefix(void)246 Py_GetExecPrefix(void)
247 {
248 #ifdef MS_WINDOWS
249     return Py_GetPrefix();
250 #else
251     pathconfig_global_init();
252     return _Py_path_config.exec_prefix;
253 #endif
254 }
255 
256 
257 wchar_t *
Py_GetProgramFullPath(void)258 Py_GetProgramFullPath(void)
259 {
260     pathconfig_global_init();
261     return _Py_path_config.program_full_path;
262 }
263 
264 
265 wchar_t*
Py_GetPythonHome(void)266 Py_GetPythonHome(void)
267 {
268     pathconfig_global_init();
269     return _Py_path_config.home;
270 }
271 
272 
273 wchar_t *
Py_GetProgramName(void)274 Py_GetProgramName(void)
275 {
276     pathconfig_global_init();
277     return _Py_path_config.program_name;
278 }
279 
280 /* Compute argv[0] which will be prepended to sys.argv */
281 PyObject*
_PyPathConfig_ComputeArgv0(int argc,wchar_t ** argv)282 _PyPathConfig_ComputeArgv0(int argc, wchar_t **argv)
283 {
284     wchar_t *argv0;
285     wchar_t *p = NULL;
286     Py_ssize_t n = 0;
287     int have_script_arg = 0;
288     int have_module_arg = 0;
289 #ifdef HAVE_READLINK
290     wchar_t link[MAXPATHLEN+1];
291     wchar_t argv0copy[2*MAXPATHLEN+1];
292     int nr = 0;
293 #endif
294 #if defined(HAVE_REALPATH)
295     wchar_t fullpath[MAXPATHLEN];
296 #elif defined(MS_WINDOWS)
297     wchar_t fullpath[MAX_PATH];
298 #endif
299 
300     argv0 = argv[0];
301     if (argc > 0 && argv0 != NULL) {
302         have_module_arg = (wcscmp(argv0, L"-m") == 0);
303         have_script_arg = !have_module_arg && (wcscmp(argv0, L"-c") != 0);
304     }
305 
306     if (have_module_arg) {
307         #if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
308             _Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath));
309             argv0 = fullpath;
310             n = wcslen(argv0);
311         #else
312             argv0 = L".";
313             n = 1;
314         #endif
315     }
316 
317 #ifdef HAVE_READLINK
318     if (have_script_arg)
319         nr = _Py_wreadlink(argv0, link, MAXPATHLEN);
320     if (nr > 0) {
321         /* It's a symlink */
322         link[nr] = '\0';
323         if (link[0] == SEP)
324             argv0 = link; /* Link to absolute path */
325         else if (wcschr(link, SEP) == NULL)
326             ; /* Link without path */
327         else {
328             /* Must join(dirname(argv0), link) */
329             wchar_t *q = wcsrchr(argv0, SEP);
330             if (q == NULL)
331                 argv0 = link; /* argv0 without path */
332             else {
333                 /* Must make a copy, argv0copy has room for 2 * MAXPATHLEN */
334                 wcsncpy(argv0copy, argv0, MAXPATHLEN);
335                 q = wcsrchr(argv0copy, SEP);
336                 wcsncpy(q+1, link, MAXPATHLEN);
337                 q[MAXPATHLEN + 1] = L'\0';
338                 argv0 = argv0copy;
339             }
340         }
341     }
342 #endif /* HAVE_READLINK */
343 
344 #if SEP == '\\'
345     /* Special case for Microsoft filename syntax */
346     if (have_script_arg) {
347         wchar_t *q;
348 #if defined(MS_WINDOWS)
349         /* Replace the first element in argv with the full path. */
350         wchar_t *ptemp;
351         if (GetFullPathNameW(argv0,
352                            Py_ARRAY_LENGTH(fullpath),
353                            fullpath,
354                            &ptemp)) {
355             argv0 = fullpath;
356         }
357 #endif
358         p = wcsrchr(argv0, SEP);
359         /* Test for alternate separator */
360         q = wcsrchr(p ? p : argv0, '/');
361         if (q != NULL)
362             p = q;
363         if (p != NULL) {
364             n = p + 1 - argv0;
365             if (n > 1 && p[-1] != ':')
366                 n--; /* Drop trailing separator */
367         }
368     }
369 #else /* All other filename syntaxes */
370     if (have_script_arg) {
371 #if defined(HAVE_REALPATH)
372         if (_Py_wrealpath(argv0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
373             argv0 = fullpath;
374         }
375 #endif
376         p = wcsrchr(argv0, SEP);
377     }
378     if (p != NULL) {
379         n = p + 1 - argv0;
380 #if SEP == '/' /* Special case for Unix filename syntax */
381         if (n > 1)
382             n--; /* Drop trailing separator */
383 #endif /* Unix */
384     }
385 #endif /* All others */
386 
387     return PyUnicode_FromWideChar(argv0, n);
388 }
389 
390 
391 /* Search for a prefix value in an environment file (pyvenv.cfg).
392    If found, copy it into the provided buffer. */
393 int
_Py_FindEnvConfigValue(FILE * env_file,const wchar_t * key,wchar_t * value,size_t value_size)394 _Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
395                        wchar_t *value, size_t value_size)
396 {
397     int result = 0; /* meaning not found */
398     char buffer[MAXPATHLEN*2+1];  /* allow extra for key, '=', etc. */
399 
400     fseek(env_file, 0, SEEK_SET);
401     while (!feof(env_file)) {
402         char * p = fgets(buffer, MAXPATHLEN*2, env_file);
403 
404         if (p == NULL) {
405             break;
406         }
407 
408         size_t n = strlen(p);
409         if (p[n - 1] != '\n') {
410             /* line has overflowed - bail */
411             break;
412         }
413         if (p[0] == '#') {
414             /* Comment - skip */
415             continue;
416         }
417 
418         wchar_t *tmpbuffer = _Py_DecodeUTF8_surrogateescape(buffer, n);
419         if (tmpbuffer) {
420             wchar_t * state;
421             wchar_t * tok = wcstok(tmpbuffer, L" \t\r\n", &state);
422             if ((tok != NULL) && !wcscmp(tok, key)) {
423                 tok = wcstok(NULL, L" \t", &state);
424                 if ((tok != NULL) && !wcscmp(tok, L"=")) {
425                     tok = wcstok(NULL, L"\r\n", &state);
426                     if (tok != NULL) {
427                         wcsncpy(value, tok, MAXPATHLEN);
428                         result = 1;
429                         PyMem_RawFree(tmpbuffer);
430                         break;
431                     }
432                 }
433             }
434             PyMem_RawFree(tmpbuffer);
435         }
436     }
437     return result;
438 }
439 
440 #ifdef __cplusplus
441 }
442 #endif
443