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