• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Path configuration like module_search_path (sys.path) */
2 
3 #include "Python.h"
4 #include "osdefs.h"
5 #include "pycore_initconfig.h"
6 #include "pycore_fileutils.h"
7 #include "pycore_pathconfig.h"
8 #include "pycore_pymem.h"
9 #include "pycore_pystate.h"
10 #include <wchar.h>
11 
12 #ifdef __cplusplus
13 extern "C" {
14 #endif
15 
16 
17 _PyPathConfig _Py_path_config = _PyPathConfig_INIT;
18 #ifdef MS_WINDOWS
19 wchar_t *_Py_dll_path = NULL;
20 #endif
21 
22 
23 static int
copy_wstr(wchar_t ** dst,const wchar_t * src)24 copy_wstr(wchar_t **dst, const wchar_t *src)
25 {
26     assert(*dst == NULL);
27     if (src != NULL) {
28         *dst = _PyMem_RawWcsdup(src);
29         if (*dst == NULL) {
30             return -1;
31         }
32     }
33     else {
34         *dst = NULL;
35     }
36     return 0;
37 }
38 
39 
40 static void
pathconfig_clear(_PyPathConfig * config)41 pathconfig_clear(_PyPathConfig *config)
42 {
43     /* _PyMem_SetDefaultAllocator() is needed to get a known memory allocator,
44        since Py_SetPath(), Py_SetPythonHome() and Py_SetProgramName() can be
45        called before Py_Initialize() which can changes the memory allocator. */
46     PyMemAllocatorEx old_alloc;
47     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
48 
49 #define CLEAR(ATTR) \
50     do { \
51         PyMem_RawFree(ATTR); \
52         ATTR = NULL; \
53     } while (0)
54 
55     CLEAR(config->program_full_path);
56     CLEAR(config->prefix);
57     CLEAR(config->exec_prefix);
58     CLEAR(config->module_search_path);
59     CLEAR(config->program_name);
60     CLEAR(config->home);
61 #ifdef MS_WINDOWS
62     CLEAR(config->base_executable);
63 #endif
64 
65 #undef CLEAR
66 
67     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
68 }
69 
70 
71 static PyStatus
pathconfig_copy(_PyPathConfig * config,const _PyPathConfig * config2)72 pathconfig_copy(_PyPathConfig *config, const _PyPathConfig *config2)
73 {
74     pathconfig_clear(config);
75 
76 #define COPY_ATTR(ATTR) \
77     do { \
78         if (copy_wstr(&config->ATTR, config2->ATTR) < 0) { \
79             return _PyStatus_NO_MEMORY(); \
80         } \
81     } while (0)
82 
83     COPY_ATTR(program_full_path);
84     COPY_ATTR(prefix);
85     COPY_ATTR(exec_prefix);
86     COPY_ATTR(module_search_path);
87     COPY_ATTR(program_name);
88     COPY_ATTR(home);
89 #ifdef MS_WINDOWS
90     config->isolated = config2->isolated;
91     config->site_import = config2->site_import;
92     COPY_ATTR(base_executable);
93 #endif
94 
95 #undef COPY_ATTR
96 
97     return _PyStatus_OK();
98 }
99 
100 
101 void
_PyPathConfig_ClearGlobal(void)102 _PyPathConfig_ClearGlobal(void)
103 {
104     PyMemAllocatorEx old_alloc;
105     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
106 
107     pathconfig_clear(&_Py_path_config);
108 #ifdef MS_WINDOWS
109     PyMem_RawFree(_Py_dll_path);
110     _Py_dll_path = NULL;
111 #endif
112 
113     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
114 }
115 
116 
117 static wchar_t*
_PyWideStringList_Join(const PyWideStringList * list,wchar_t sep)118 _PyWideStringList_Join(const PyWideStringList *list, wchar_t sep)
119 {
120     size_t len = 1;   /* NUL terminator */
121     for (Py_ssize_t i=0; i < list->length; i++) {
122         if (i != 0) {
123             len++;
124         }
125         len += wcslen(list->items[i]);
126     }
127 
128     wchar_t *text = PyMem_RawMalloc(len * sizeof(wchar_t));
129     if (text == NULL) {
130         return NULL;
131     }
132     wchar_t *str = text;
133     for (Py_ssize_t i=0; i < list->length; i++) {
134         wchar_t *path = list->items[i];
135         if (i != 0) {
136             *str++ = sep;
137         }
138         len = wcslen(path);
139         memcpy(str, path, len * sizeof(wchar_t));
140         str += len;
141     }
142     *str = L'\0';
143 
144     return text;
145 }
146 
147 
148 #ifdef MS_WINDOWS
149 /* Initialize _Py_dll_path on Windows. Do nothing on other platforms. */
150 static PyStatus
_PyPathConfig_InitDLLPath(void)151 _PyPathConfig_InitDLLPath(void)
152 {
153     if (_Py_dll_path == NULL) {
154         /* Already set: nothing to do */
155         return _PyStatus_OK();
156     }
157 
158     PyMemAllocatorEx old_alloc;
159     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
160 
161     _Py_dll_path = _Py_GetDLLPath();
162 
163     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
164 
165     if (_Py_dll_path == NULL) {
166         return _PyStatus_NO_MEMORY();
167     }
168     return _PyStatus_OK();
169 }
170 #endif
171 
172 
173 static PyStatus
pathconfig_set_from_config(_PyPathConfig * pathconfig,const PyConfig * config)174 pathconfig_set_from_config(_PyPathConfig *pathconfig, const PyConfig *config)
175 {
176     PyStatus status;
177     PyMemAllocatorEx old_alloc;
178     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
179 
180     if (config->module_search_paths_set) {
181         PyMem_RawFree(pathconfig->module_search_path);
182         pathconfig->module_search_path = _PyWideStringList_Join(&config->module_search_paths, DELIM);
183         if (pathconfig->module_search_path == NULL) {
184             goto no_memory;
185         }
186     }
187 
188 #define COPY_CONFIG(PATH_ATTR, CONFIG_ATTR) \
189         if (config->CONFIG_ATTR) { \
190             PyMem_RawFree(pathconfig->PATH_ATTR); \
191             pathconfig->PATH_ATTR = NULL; \
192             if (copy_wstr(&pathconfig->PATH_ATTR, config->CONFIG_ATTR) < 0) { \
193                 goto no_memory; \
194             } \
195         }
196 
197     COPY_CONFIG(program_full_path, executable);
198     COPY_CONFIG(prefix, prefix);
199     COPY_CONFIG(exec_prefix, exec_prefix);
200     COPY_CONFIG(program_name, program_name);
201     COPY_CONFIG(home, home);
202 #ifdef MS_WINDOWS
203     COPY_CONFIG(base_executable, base_executable);
204 #endif
205 
206 #undef COPY_CONFIG
207 
208     status = _PyStatus_OK();
209     goto done;
210 
211 no_memory:
212     status = _PyStatus_NO_MEMORY();
213 
214 done:
215     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
216     return status;
217 }
218 
219 
220 PyStatus
_PyConfig_WritePathConfig(const PyConfig * config)221 _PyConfig_WritePathConfig(const PyConfig *config)
222 {
223 #ifdef MS_WINDOWS
224     PyStatus status = _PyPathConfig_InitDLLPath();
225     if (_PyStatus_EXCEPTION(status)) {
226         return status;
227     }
228 #endif
229 
230     return pathconfig_set_from_config(&_Py_path_config, config);
231 }
232 
233 
234 static PyStatus
config_init_module_search_paths(PyConfig * config,_PyPathConfig * pathconfig)235 config_init_module_search_paths(PyConfig *config, _PyPathConfig *pathconfig)
236 {
237     assert(!config->module_search_paths_set);
238 
239     _PyWideStringList_Clear(&config->module_search_paths);
240 
241     const wchar_t *sys_path = pathconfig->module_search_path;
242     const wchar_t delim = DELIM;
243     const wchar_t *p = sys_path;
244     while (1) {
245         p = wcschr(sys_path, delim);
246         if (p == NULL) {
247             p = sys_path + wcslen(sys_path); /* End of string */
248         }
249 
250         size_t path_len = (p - sys_path);
251         wchar_t *path = PyMem_RawMalloc((path_len + 1) * sizeof(wchar_t));
252         if (path == NULL) {
253             return _PyStatus_NO_MEMORY();
254         }
255         memcpy(path, sys_path, path_len * sizeof(wchar_t));
256         path[path_len] = L'\0';
257 
258         PyStatus status = PyWideStringList_Append(&config->module_search_paths, path);
259         PyMem_RawFree(path);
260         if (_PyStatus_EXCEPTION(status)) {
261             return status;
262         }
263 
264         if (*p == '\0') {
265             break;
266         }
267         sys_path = p + 1;
268     }
269     config->module_search_paths_set = 1;
270     return _PyStatus_OK();
271 }
272 
273 
274 /* Calculate the path configuration:
275 
276    - exec_prefix
277    - module_search_path
278    - prefix
279    - program_full_path
280 
281    On Windows, more fields are calculated:
282 
283    - base_executable
284    - isolated
285    - site_import
286 
287    On other platforms, isolated and site_import are left unchanged, and
288    _PyConfig_InitPathConfig() copies executable to base_executable (if it's not
289    set).
290 
291    Priority, highest to lowest:
292 
293    - PyConfig
294    - _Py_path_config: set by Py_SetPath(), Py_SetPythonHome()
295      and Py_SetProgramName()
296    - _PyPathConfig_Calculate()
297 */
298 static PyStatus
pathconfig_calculate(_PyPathConfig * pathconfig,const PyConfig * config)299 pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config)
300 {
301     PyStatus status;
302 
303     PyMemAllocatorEx old_alloc;
304     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
305 
306     status = pathconfig_copy(pathconfig, &_Py_path_config);
307     if (_PyStatus_EXCEPTION(status)) {
308         goto done;
309     }
310 
311     status = pathconfig_set_from_config(pathconfig, config);
312     if (_PyStatus_EXCEPTION(status)) {
313         goto done;
314     }
315 
316     if (_Py_path_config.module_search_path == NULL) {
317         status = _PyPathConfig_Calculate(pathconfig, config);
318     }
319     else {
320         /* Py_SetPath() has been called: avoid _PyPathConfig_Calculate() */
321     }
322 
323 done:
324     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
325     return status;
326 }
327 
328 
329 static PyStatus
config_calculate_pathconfig(PyConfig * config)330 config_calculate_pathconfig(PyConfig *config)
331 {
332     _PyPathConfig pathconfig = _PyPathConfig_INIT;
333     PyStatus status;
334 
335     status = pathconfig_calculate(&pathconfig, config);
336     if (_PyStatus_EXCEPTION(status)) {
337         goto done;
338     }
339 
340     if (!config->module_search_paths_set) {
341         status = config_init_module_search_paths(config, &pathconfig);
342         if (_PyStatus_EXCEPTION(status)) {
343             goto done;
344         }
345     }
346 
347 #define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \
348         if (config->CONFIG_ATTR == NULL) { \
349             if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \
350                 goto no_memory; \
351             } \
352         }
353 
354 #ifdef MS_WINDOWS
355     if (config->executable != NULL && config->base_executable == NULL) {
356         /* If executable is set explicitly in the configuration,
357            ignore calculated base_executable: _PyConfig_InitPathConfig()
358            will copy executable to base_executable */
359     }
360     else {
361         COPY_ATTR(base_executable, base_executable);
362     }
363 #endif
364 
365     COPY_ATTR(program_full_path, executable);
366     COPY_ATTR(prefix, prefix);
367     COPY_ATTR(exec_prefix, exec_prefix);
368 
369 #undef COPY_ATTR
370 
371 #ifdef MS_WINDOWS
372     /* If a ._pth file is found: isolated and site_import are overriden */
373     if (pathconfig.isolated != -1) {
374         config->isolated = pathconfig.isolated;
375     }
376     if (pathconfig.site_import != -1) {
377         config->site_import = pathconfig.site_import;
378     }
379 #endif
380 
381     status = _PyStatus_OK();
382     goto done;
383 
384 no_memory:
385     status = _PyStatus_NO_MEMORY();
386 
387 done:
388     pathconfig_clear(&pathconfig);
389     return status;
390 }
391 
392 
393 PyStatus
_PyConfig_InitPathConfig(PyConfig * config)394 _PyConfig_InitPathConfig(PyConfig *config)
395 {
396     /* Do we need to calculate the path? */
397     if (!config->module_search_paths_set
398         || config->executable == NULL
399         || config->prefix == NULL
400         || config->exec_prefix == NULL)
401     {
402         PyStatus status = config_calculate_pathconfig(config);
403         if (_PyStatus_EXCEPTION(status)) {
404             return status;
405         }
406     }
407 
408     if (config->base_prefix == NULL) {
409         if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
410             return _PyStatus_NO_MEMORY();
411         }
412     }
413 
414     if (config->base_exec_prefix == NULL) {
415         if (copy_wstr(&config->base_exec_prefix,
416                       config->exec_prefix) < 0) {
417             return _PyStatus_NO_MEMORY();
418         }
419     }
420 
421     if (config->base_executable == NULL) {
422         if (copy_wstr(&config->base_executable,
423                       config->executable) < 0) {
424             return _PyStatus_NO_MEMORY();
425         }
426     }
427 
428     return _PyStatus_OK();
429 }
430 
431 
432 static PyStatus
pathconfig_global_read(_PyPathConfig * pathconfig)433 pathconfig_global_read(_PyPathConfig *pathconfig)
434 {
435     PyConfig config;
436     _PyConfig_InitCompatConfig(&config);
437 
438     /* Call _PyConfig_InitPathConfig() */
439     PyStatus status = PyConfig_Read(&config);
440     if (_PyStatus_EXCEPTION(status)) {
441         goto done;
442     }
443 
444     status = pathconfig_set_from_config(pathconfig, &config);
445 
446 done:
447     PyConfig_Clear(&config);
448     return status;
449 }
450 
451 
452 static void
pathconfig_global_init(void)453 pathconfig_global_init(void)
454 {
455     PyStatus status;
456 
457 #ifdef MS_WINDOWS
458     status = _PyPathConfig_InitDLLPath();
459     if (_PyStatus_EXCEPTION(status)) {
460         Py_ExitStatusException(status);
461     }
462 #endif
463 
464     if (_Py_path_config.module_search_path == NULL) {
465         status = pathconfig_global_read(&_Py_path_config);
466         if (_PyStatus_EXCEPTION(status)) {
467             Py_ExitStatusException(status);
468         }
469     }
470     else {
471         /* Global configuration already initialized */
472     }
473 
474     assert(_Py_path_config.program_full_path != NULL);
475     assert(_Py_path_config.prefix != NULL);
476     assert(_Py_path_config.exec_prefix != NULL);
477     assert(_Py_path_config.module_search_path != NULL);
478     assert(_Py_path_config.program_name != NULL);
479     /* home can be NULL */
480 #ifdef MS_WINDOWS
481     assert(_Py_path_config.base_executable != NULL);
482 #endif
483 }
484 
485 
486 /* External interface */
487 
488 void
Py_SetPath(const wchar_t * path)489 Py_SetPath(const wchar_t *path)
490 {
491     if (path == NULL) {
492         pathconfig_clear(&_Py_path_config);
493         return;
494     }
495 
496     PyMemAllocatorEx old_alloc;
497     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
498 
499     /* Getting the program full path calls pathconfig_global_init() */
500     wchar_t *program_full_path = _PyMem_RawWcsdup(Py_GetProgramFullPath());
501 
502     PyMem_RawFree(_Py_path_config.program_full_path);
503     PyMem_RawFree(_Py_path_config.prefix);
504     PyMem_RawFree(_Py_path_config.exec_prefix);
505     PyMem_RawFree(_Py_path_config.module_search_path);
506 
507     _Py_path_config.program_full_path = program_full_path;
508     _Py_path_config.prefix = _PyMem_RawWcsdup(L"");
509     _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
510     _Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
511 
512     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
513 
514     if (_Py_path_config.program_full_path == NULL
515         || _Py_path_config.prefix == NULL
516         || _Py_path_config.exec_prefix == NULL
517         || _Py_path_config.module_search_path == NULL)
518     {
519         Py_FatalError("Py_SetPath() failed: out of memory");
520     }
521 }
522 
523 
524 void
Py_SetPythonHome(const wchar_t * home)525 Py_SetPythonHome(const wchar_t *home)
526 {
527     if (home == NULL) {
528         return;
529     }
530 
531     PyMemAllocatorEx old_alloc;
532     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
533 
534     PyMem_RawFree(_Py_path_config.home);
535     _Py_path_config.home = _PyMem_RawWcsdup(home);
536 
537     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
538 
539     if (_Py_path_config.home == NULL) {
540         Py_FatalError("Py_SetPythonHome() failed: out of memory");
541     }
542 }
543 
544 
545 void
Py_SetProgramName(const wchar_t * program_name)546 Py_SetProgramName(const wchar_t *program_name)
547 {
548     if (program_name == NULL || program_name[0] == L'\0') {
549         return;
550     }
551 
552     PyMemAllocatorEx old_alloc;
553     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
554 
555     PyMem_RawFree(_Py_path_config.program_name);
556     _Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
557 
558     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
559 
560     if (_Py_path_config.program_name == NULL) {
561         Py_FatalError("Py_SetProgramName() failed: out of memory");
562     }
563 }
564 
565 void
_Py_SetProgramFullPath(const wchar_t * program_full_path)566 _Py_SetProgramFullPath(const wchar_t *program_full_path)
567 {
568     if (program_full_path == NULL || program_full_path[0] == L'\0') {
569         return;
570     }
571 
572     PyMemAllocatorEx old_alloc;
573     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
574 
575     PyMem_RawFree(_Py_path_config.program_full_path);
576     _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
577 
578     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
579 
580     if (_Py_path_config.program_full_path == NULL) {
581         Py_FatalError("_Py_SetProgramFullPath() failed: out of memory");
582     }
583 }
584 
585 
586 wchar_t *
Py_GetPath(void)587 Py_GetPath(void)
588 {
589     pathconfig_global_init();
590     return _Py_path_config.module_search_path;
591 }
592 
593 
594 wchar_t *
Py_GetPrefix(void)595 Py_GetPrefix(void)
596 {
597     pathconfig_global_init();
598     return _Py_path_config.prefix;
599 }
600 
601 
602 wchar_t *
Py_GetExecPrefix(void)603 Py_GetExecPrefix(void)
604 {
605     pathconfig_global_init();
606     return _Py_path_config.exec_prefix;
607 }
608 
609 
610 wchar_t *
Py_GetProgramFullPath(void)611 Py_GetProgramFullPath(void)
612 {
613     pathconfig_global_init();
614     return _Py_path_config.program_full_path;
615 }
616 
617 
618 wchar_t*
Py_GetPythonHome(void)619 Py_GetPythonHome(void)
620 {
621     pathconfig_global_init();
622     return _Py_path_config.home;
623 }
624 
625 
626 wchar_t *
Py_GetProgramName(void)627 Py_GetProgramName(void)
628 {
629     pathconfig_global_init();
630     return _Py_path_config.program_name;
631 }
632 
633 /* Compute module search path from argv[0] or the current working
634    directory ("-m module" case) which will be prepended to sys.argv:
635    sys.path[0].
636 
637    Return 1 if the path is correctly resolved and written into *path0_p.
638 
639    Return 0 if it fails to resolve the full path. For example, return 0 if the
640    current working directory has been removed (bpo-36236) or if argv is empty.
641 
642    Raise an exception and return -1 on error.
643    */
644 int
_PyPathConfig_ComputeSysPath0(const PyWideStringList * argv,PyObject ** path0_p)645 _PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p)
646 {
647     assert(_PyWideStringList_CheckConsistency(argv));
648 
649     if (argv->length == 0) {
650         /* Leave sys.path unchanged if sys.argv is empty */
651         return 0;
652     }
653 
654     wchar_t *argv0 = argv->items[0];
655     int have_module_arg = (wcscmp(argv0, L"-m") == 0);
656     int have_script_arg = (!have_module_arg && (wcscmp(argv0, L"-c") != 0));
657 
658     wchar_t *path0 = argv0;
659     Py_ssize_t n = 0;
660 
661 #ifdef HAVE_REALPATH
662     wchar_t fullpath[MAXPATHLEN];
663 #elif defined(MS_WINDOWS)
664     wchar_t fullpath[MAX_PATH];
665 #endif
666 
667     if (have_module_arg) {
668 #if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
669         if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) {
670             return 0;
671         }
672         path0 = fullpath;
673 #else
674         path0 = L".";
675 #endif
676         n = wcslen(path0);
677     }
678 
679 #ifdef HAVE_READLINK
680     wchar_t link[MAXPATHLEN + 1];
681     int nr = 0;
682 
683     if (have_script_arg) {
684         nr = _Py_wreadlink(path0, link, Py_ARRAY_LENGTH(link));
685     }
686     if (nr > 0) {
687         /* It's a symlink */
688         link[nr] = '\0';
689         if (link[0] == SEP) {
690             path0 = link; /* Link to absolute path */
691         }
692         else if (wcschr(link, SEP) == NULL) {
693             /* Link without path */
694         }
695         else {
696             /* Must join(dirname(path0), link) */
697             wchar_t *q = wcsrchr(path0, SEP);
698             if (q == NULL) {
699                 /* path0 without path */
700                 path0 = link;
701             }
702             else {
703                 /* Must make a copy, path0copy has room for 2 * MAXPATHLEN */
704                 wchar_t path0copy[2 * MAXPATHLEN + 1];
705                 wcsncpy(path0copy, path0, MAXPATHLEN);
706                 q = wcsrchr(path0copy, SEP);
707                 wcsncpy(q+1, link, MAXPATHLEN);
708                 q[MAXPATHLEN + 1] = L'\0';
709                 path0 = path0copy;
710             }
711         }
712     }
713 #endif /* HAVE_READLINK */
714 
715     wchar_t *p = NULL;
716 
717 #if SEP == '\\'
718     /* Special case for Microsoft filename syntax */
719     if (have_script_arg) {
720         wchar_t *q;
721 #if defined(MS_WINDOWS)
722         /* Replace the first element in argv with the full path. */
723         wchar_t *ptemp;
724         if (GetFullPathNameW(path0,
725                            Py_ARRAY_LENGTH(fullpath),
726                            fullpath,
727                            &ptemp)) {
728             path0 = fullpath;
729         }
730 #endif
731         p = wcsrchr(path0, SEP);
732         /* Test for alternate separator */
733         q = wcsrchr(p ? p : path0, '/');
734         if (q != NULL)
735             p = q;
736         if (p != NULL) {
737             n = p + 1 - path0;
738             if (n > 1 && p[-1] != ':')
739                 n--; /* Drop trailing separator */
740         }
741     }
742 #else
743     /* All other filename syntaxes */
744     if (have_script_arg) {
745 #if defined(HAVE_REALPATH)
746         if (_Py_wrealpath(path0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
747             path0 = fullpath;
748         }
749 #endif
750         p = wcsrchr(path0, SEP);
751     }
752     if (p != NULL) {
753         n = p + 1 - path0;
754 #if SEP == '/' /* Special case for Unix filename syntax */
755         if (n > 1) {
756             /* Drop trailing separator */
757             n--;
758         }
759 #endif /* Unix */
760     }
761 #endif /* All others */
762 
763     PyObject *path0_obj = PyUnicode_FromWideChar(path0, n);
764     if (path0_obj == NULL) {
765         return -1;
766     }
767 
768     *path0_p = path0_obj;
769     return 1;
770 }
771 
772 
773 #ifdef MS_WINDOWS
774 #define WCSTOK wcstok_s
775 #else
776 #define WCSTOK wcstok
777 #endif
778 
779 /* Search for a prefix value in an environment file (pyvenv.cfg).
780    If found, copy it into the provided buffer. */
781 int
_Py_FindEnvConfigValue(FILE * env_file,const wchar_t * key,wchar_t * value,size_t value_size)782 _Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
783                        wchar_t *value, size_t value_size)
784 {
785     int result = 0; /* meaning not found */
786     char buffer[MAXPATHLEN * 2 + 1];  /* allow extra for key, '=', etc. */
787     buffer[Py_ARRAY_LENGTH(buffer)-1] = '\0';
788 
789     fseek(env_file, 0, SEEK_SET);
790     while (!feof(env_file)) {
791         char * p = fgets(buffer, Py_ARRAY_LENGTH(buffer) - 1, env_file);
792 
793         if (p == NULL) {
794             break;
795         }
796 
797         size_t n = strlen(p);
798         if (p[n - 1] != '\n') {
799             /* line has overflowed - bail */
800             break;
801         }
802         if (p[0] == '#') {
803             /* Comment - skip */
804             continue;
805         }
806 
807         wchar_t *tmpbuffer = _Py_DecodeUTF8_surrogateescape(buffer, n, NULL);
808         if (tmpbuffer) {
809             wchar_t * state;
810             wchar_t * tok = WCSTOK(tmpbuffer, L" \t\r\n", &state);
811             if ((tok != NULL) && !wcscmp(tok, key)) {
812                 tok = WCSTOK(NULL, L" \t", &state);
813                 if ((tok != NULL) && !wcscmp(tok, L"=")) {
814                     tok = WCSTOK(NULL, L"\r\n", &state);
815                     if (tok != NULL) {
816                         wcsncpy(value, tok, value_size - 1);
817                         value[value_size - 1] = L'\0';
818                         result = 1;
819                         PyMem_RawFree(tmpbuffer);
820                         break;
821                     }
822                 }
823             }
824             PyMem_RawFree(tmpbuffer);
825         }
826     }
827     return result;
828 }
829 
830 #ifdef __cplusplus
831 }
832 #endif
833