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 int
_PyPathConfig_ComputeArgv0(int argc,wchar_t ** argv,PyObject ** argv0_p)282 _PyPathConfig_ComputeArgv0(int argc, wchar_t **argv, PyObject **argv0_p)
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     assert(*argv0_p == NULL);
301 
302     argv0 = argv[0];
303     if (argc > 0 && argv0 != NULL) {
304         have_module_arg = (wcscmp(argv0, L"-m") == 0);
305         have_script_arg = !have_module_arg && (wcscmp(argv0, L"-c") != 0);
306     }
307 
308     if (have_module_arg) {
309         #if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
310             if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) {
311                 return 0;
312             }
313             argv0 = fullpath;
314             n = wcslen(argv0);
315         #else
316             argv0 = L".";
317             n = 1;
318         #endif
319     }
320 
321 #ifdef HAVE_READLINK
322     if (have_script_arg)
323         nr = _Py_wreadlink(argv0, link, MAXPATHLEN);
324     if (nr > 0) {
325         /* It's a symlink */
326         link[nr] = '\0';
327         if (link[0] == SEP)
328             argv0 = link; /* Link to absolute path */
329         else if (wcschr(link, SEP) == NULL)
330             ; /* Link without path */
331         else {
332             /* Must join(dirname(argv0), link) */
333             wchar_t *q = wcsrchr(argv0, SEP);
334             if (q == NULL)
335                 argv0 = link; /* argv0 without path */
336             else {
337                 /* Must make a copy, argv0copy has room for 2 * MAXPATHLEN */
338                 wcsncpy(argv0copy, argv0, MAXPATHLEN);
339                 q = wcsrchr(argv0copy, SEP);
340                 wcsncpy(q+1, link, MAXPATHLEN);
341                 q[MAXPATHLEN + 1] = L'\0';
342                 argv0 = argv0copy;
343             }
344         }
345     }
346 #endif /* HAVE_READLINK */
347 
348 #if SEP == '\\'
349     /* Special case for Microsoft filename syntax */
350     if (have_script_arg) {
351         wchar_t *q;
352 #if defined(MS_WINDOWS)
353         /* Replace the first element in argv with the full path. */
354         wchar_t *ptemp;
355         if (GetFullPathNameW(argv0,
356                            Py_ARRAY_LENGTH(fullpath),
357                            fullpath,
358                            &ptemp)) {
359             argv0 = fullpath;
360         }
361 #endif
362         p = wcsrchr(argv0, SEP);
363         /* Test for alternate separator */
364         q = wcsrchr(p ? p : argv0, '/');
365         if (q != NULL)
366             p = q;
367         if (p != NULL) {
368             n = p + 1 - argv0;
369             if (n > 1 && p[-1] != ':')
370                 n--; /* Drop trailing separator */
371         }
372     }
373 #else /* All other filename syntaxes */
374     if (have_script_arg) {
375 #if defined(HAVE_REALPATH)
376         if (_Py_wrealpath(argv0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
377             argv0 = fullpath;
378         }
379 #endif
380         p = wcsrchr(argv0, SEP);
381     }
382     if (p != NULL) {
383         n = p + 1 - argv0;
384 #if SEP == '/' /* Special case for Unix filename syntax */
385         if (n > 1)
386             n--; /* Drop trailing separator */
387 #endif /* Unix */
388     }
389 #endif /* All others */
390 
391     *argv0_p = PyUnicode_FromWideChar(argv0, n);
392     return 1;
393 }
394 
395 
396 /* Search for a prefix value in an environment file (pyvenv.cfg).
397    If found, copy it into the provided buffer. */
398 int
_Py_FindEnvConfigValue(FILE * env_file,const wchar_t * key,wchar_t * value,size_t value_size)399 _Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
400                        wchar_t *value, size_t value_size)
401 {
402     int result = 0; /* meaning not found */
403     char buffer[MAXPATHLEN*2+1];  /* allow extra for key, '=', etc. */
404 
405     fseek(env_file, 0, SEEK_SET);
406     while (!feof(env_file)) {
407         char * p = fgets(buffer, MAXPATHLEN*2, env_file);
408 
409         if (p == NULL) {
410             break;
411         }
412 
413         size_t n = strlen(p);
414         if (p[n - 1] != '\n') {
415             /* line has overflowed - bail */
416             break;
417         }
418         if (p[0] == '#') {
419             /* Comment - skip */
420             continue;
421         }
422 
423         wchar_t *tmpbuffer = _Py_DecodeUTF8_surrogateescape(buffer, n);
424         if (tmpbuffer) {
425             wchar_t * state;
426             wchar_t * tok = wcstok(tmpbuffer, L" \t\r\n", &state);
427             if ((tok != NULL) && !wcscmp(tok, key)) {
428                 tok = wcstok(NULL, L" \t", &state);
429                 if ((tok != NULL) && !wcscmp(tok, L"=")) {
430                     tok = wcstok(NULL, L"\r\n", &state);
431                     if (tok != NULL) {
432                         wcsncpy(value, tok, MAXPATHLEN);
433                         result = 1;
434                         PyMem_RawFree(tmpbuffer);
435                         break;
436                     }
437                 }
438             }
439             PyMem_RawFree(tmpbuffer);
440         }
441     }
442     return result;
443 }
444 
445 #ifdef __cplusplus
446 }
447 #endif
448