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 
190 PyStatus
_PyConfig_WritePathConfig(const PyConfig * config)191 _PyConfig_WritePathConfig(const PyConfig *config)
192 {
193     return pathconfig_set_from_config(&_Py_path_config, config);
194 }
195 
196 
197 static PyStatus
config_init_module_search_paths(PyConfig * config,_PyPathConfig * pathconfig)198 config_init_module_search_paths(PyConfig *config, _PyPathConfig *pathconfig)
199 {
200     assert(!config->module_search_paths_set);
201 
202     _PyWideStringList_Clear(&config->module_search_paths);
203 
204     const wchar_t *sys_path = pathconfig->module_search_path;
205     const wchar_t delim = DELIM;
206     while (1) {
207         const wchar_t *p = wcschr(sys_path, delim);
208         if (p == NULL) {
209             p = sys_path + wcslen(sys_path); /* End of string */
210         }
211 
212         size_t path_len = (p - sys_path);
213         wchar_t *path = PyMem_RawMalloc((path_len + 1) * sizeof(wchar_t));
214         if (path == NULL) {
215             return _PyStatus_NO_MEMORY();
216         }
217         memcpy(path, sys_path, path_len * sizeof(wchar_t));
218         path[path_len] = L'\0';
219 
220         PyStatus status = PyWideStringList_Append(&config->module_search_paths, path);
221         PyMem_RawFree(path);
222         if (_PyStatus_EXCEPTION(status)) {
223             return status;
224         }
225 
226         if (*p == '\0') {
227             break;
228         }
229         sys_path = p + 1;
230     }
231     config->module_search_paths_set = 1;
232     return _PyStatus_OK();
233 }
234 
235 
236 /* Calculate the path configuration:
237 
238    - exec_prefix
239    - module_search_path
240    - prefix
241    - program_full_path
242 
243    On Windows, more fields are calculated:
244 
245    - base_executable
246    - isolated
247    - site_import
248 
249    On other platforms, isolated and site_import are left unchanged, and
250    _PyConfig_InitPathConfig() copies executable to base_executable (if it's not
251    set).
252 
253    Priority, highest to lowest:
254 
255    - PyConfig
256    - _Py_path_config: set by Py_SetPath(), Py_SetPythonHome()
257      and Py_SetProgramName()
258    - _PyPathConfig_Calculate()
259 */
260 static PyStatus
pathconfig_calculate(_PyPathConfig * pathconfig,const PyConfig * config)261 pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config)
262 {
263     PyStatus status;
264 
265     PyMemAllocatorEx old_alloc;
266     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
267 
268     status = pathconfig_copy(pathconfig, &_Py_path_config);
269     if (_PyStatus_EXCEPTION(status)) {
270         goto done;
271     }
272 
273     status = pathconfig_set_from_config(pathconfig, config);
274     if (_PyStatus_EXCEPTION(status)) {
275         goto done;
276     }
277 
278     if (_Py_path_config.module_search_path == NULL) {
279         status = _PyPathConfig_Calculate(pathconfig, config);
280     }
281     else {
282         /* Py_SetPath() has been called: avoid _PyPathConfig_Calculate() */
283     }
284 
285 done:
286     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
287     return status;
288 }
289 
290 
291 static PyStatus
config_calculate_pathconfig(PyConfig * config)292 config_calculate_pathconfig(PyConfig *config)
293 {
294     _PyPathConfig pathconfig = _PyPathConfig_INIT;
295     PyStatus status;
296 
297     status = pathconfig_calculate(&pathconfig, config);
298     if (_PyStatus_EXCEPTION(status)) {
299         goto done;
300     }
301 
302     if (!config->module_search_paths_set) {
303         status = config_init_module_search_paths(config, &pathconfig);
304         if (_PyStatus_EXCEPTION(status)) {
305             goto done;
306         }
307     }
308 
309 #define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \
310         if (config->CONFIG_ATTR == NULL) { \
311             if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \
312                 goto no_memory; \
313             } \
314         }
315 
316 #ifdef MS_WINDOWS
317     if (config->executable != NULL && config->base_executable == NULL) {
318         /* If executable is set explicitly in the configuration,
319            ignore calculated base_executable: _PyConfig_InitPathConfig()
320            will copy executable to base_executable */
321     }
322     else {
323         COPY_ATTR(base_executable, base_executable);
324     }
325 #endif
326 
327     COPY_ATTR(program_full_path, executable);
328     COPY_ATTR(prefix, prefix);
329     COPY_ATTR(exec_prefix, exec_prefix);
330 
331 #undef COPY_ATTR
332 
333 #ifdef MS_WINDOWS
334     /* If a ._pth file is found: isolated and site_import are overriden */
335     if (pathconfig.isolated != -1) {
336         config->isolated = pathconfig.isolated;
337     }
338     if (pathconfig.site_import != -1) {
339         config->site_import = pathconfig.site_import;
340     }
341 #endif
342 
343     status = _PyStatus_OK();
344     goto done;
345 
346 no_memory:
347     status = _PyStatus_NO_MEMORY();
348 
349 done:
350     pathconfig_clear(&pathconfig);
351     return status;
352 }
353 
354 
355 PyStatus
_PyConfig_InitPathConfig(PyConfig * config)356 _PyConfig_InitPathConfig(PyConfig *config)
357 {
358     /* Do we need to calculate the path? */
359     if (!config->module_search_paths_set
360         || config->executable == NULL
361         || config->prefix == NULL
362         || config->exec_prefix == NULL)
363     {
364         PyStatus status = config_calculate_pathconfig(config);
365         if (_PyStatus_EXCEPTION(status)) {
366             return status;
367         }
368     }
369 
370     if (config->base_prefix == NULL) {
371         if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
372             return _PyStatus_NO_MEMORY();
373         }
374     }
375 
376     if (config->base_exec_prefix == NULL) {
377         if (copy_wstr(&config->base_exec_prefix,
378                       config->exec_prefix) < 0) {
379             return _PyStatus_NO_MEMORY();
380         }
381     }
382 
383     if (config->base_executable == NULL) {
384         if (copy_wstr(&config->base_executable,
385                       config->executable) < 0) {
386             return _PyStatus_NO_MEMORY();
387         }
388     }
389 
390     return _PyStatus_OK();
391 }
392 
393 
394 static PyStatus
pathconfig_global_read(_PyPathConfig * pathconfig)395 pathconfig_global_read(_PyPathConfig *pathconfig)
396 {
397     PyConfig config;
398     _PyConfig_InitCompatConfig(&config);
399 
400     /* Call _PyConfig_InitPathConfig() */
401     PyStatus status = PyConfig_Read(&config);
402     if (_PyStatus_EXCEPTION(status)) {
403         goto done;
404     }
405 
406     status = pathconfig_set_from_config(pathconfig, &config);
407 
408 done:
409     PyConfig_Clear(&config);
410     return status;
411 }
412 
413 
414 static void
pathconfig_global_init(void)415 pathconfig_global_init(void)
416 {
417     PyStatus status;
418 
419     if (_Py_path_config.module_search_path == NULL) {
420         status = pathconfig_global_read(&_Py_path_config);
421         if (_PyStatus_EXCEPTION(status)) {
422             Py_ExitStatusException(status);
423         }
424     }
425     else {
426         /* Global configuration already initialized */
427     }
428 
429     assert(_Py_path_config.program_full_path != NULL);
430     assert(_Py_path_config.prefix != NULL);
431     assert(_Py_path_config.exec_prefix != NULL);
432     assert(_Py_path_config.module_search_path != NULL);
433     assert(_Py_path_config.program_name != NULL);
434     /* home can be NULL */
435 #ifdef MS_WINDOWS
436     assert(_Py_path_config.base_executable != NULL);
437 #endif
438 }
439 
440 
441 /* External interface */
442 
443 static void _Py_NO_RETURN
path_out_of_memory(const char * func)444 path_out_of_memory(const char *func)
445 {
446     _Py_FatalErrorFunc(func, "out of memory");
447 }
448 
449 void
Py_SetPath(const wchar_t * path)450 Py_SetPath(const wchar_t *path)
451 {
452     if (path == NULL) {
453         pathconfig_clear(&_Py_path_config);
454         return;
455     }
456 
457     PyMemAllocatorEx old_alloc;
458     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
459 
460     /* Getting the program full path calls pathconfig_global_init() */
461     wchar_t *program_full_path = _PyMem_RawWcsdup(Py_GetProgramFullPath());
462 
463     PyMem_RawFree(_Py_path_config.program_full_path);
464     PyMem_RawFree(_Py_path_config.prefix);
465     PyMem_RawFree(_Py_path_config.exec_prefix);
466     PyMem_RawFree(_Py_path_config.module_search_path);
467 
468     _Py_path_config.program_full_path = program_full_path;
469     _Py_path_config.prefix = _PyMem_RawWcsdup(L"");
470     _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
471     _Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
472 
473     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
474 
475     if (_Py_path_config.program_full_path == NULL
476         || _Py_path_config.prefix == NULL
477         || _Py_path_config.exec_prefix == NULL
478         || _Py_path_config.module_search_path == NULL)
479     {
480         path_out_of_memory(__func__);
481     }
482 }
483 
484 
485 void
Py_SetPythonHome(const wchar_t * home)486 Py_SetPythonHome(const wchar_t *home)
487 {
488     if (home == NULL) {
489         return;
490     }
491 
492     PyMemAllocatorEx old_alloc;
493     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
494 
495     PyMem_RawFree(_Py_path_config.home);
496     _Py_path_config.home = _PyMem_RawWcsdup(home);
497 
498     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
499 
500     if (_Py_path_config.home == NULL) {
501         path_out_of_memory(__func__);
502     }
503 }
504 
505 
506 void
Py_SetProgramName(const wchar_t * program_name)507 Py_SetProgramName(const wchar_t *program_name)
508 {
509     if (program_name == NULL || program_name[0] == L'\0') {
510         return;
511     }
512 
513     PyMemAllocatorEx old_alloc;
514     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
515 
516     PyMem_RawFree(_Py_path_config.program_name);
517     _Py_path_config.program_name = _PyMem_RawWcsdup(program_name);
518 
519     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
520 
521     if (_Py_path_config.program_name == NULL) {
522         path_out_of_memory(__func__);
523     }
524 }
525 
526 void
_Py_SetProgramFullPath(const wchar_t * program_full_path)527 _Py_SetProgramFullPath(const wchar_t *program_full_path)
528 {
529     if (program_full_path == NULL || program_full_path[0] == L'\0') {
530         return;
531     }
532 
533     PyMemAllocatorEx old_alloc;
534     _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
535 
536     PyMem_RawFree(_Py_path_config.program_full_path);
537     _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path);
538 
539     PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
540 
541     if (_Py_path_config.program_full_path == NULL) {
542         path_out_of_memory(__func__);
543     }
544 }
545 
546 
547 wchar_t *
Py_GetPath(void)548 Py_GetPath(void)
549 {
550     pathconfig_global_init();
551     return _Py_path_config.module_search_path;
552 }
553 
554 
555 wchar_t *
Py_GetPrefix(void)556 Py_GetPrefix(void)
557 {
558     pathconfig_global_init();
559     return _Py_path_config.prefix;
560 }
561 
562 
563 wchar_t *
Py_GetExecPrefix(void)564 Py_GetExecPrefix(void)
565 {
566     pathconfig_global_init();
567     return _Py_path_config.exec_prefix;
568 }
569 
570 
571 wchar_t *
Py_GetProgramFullPath(void)572 Py_GetProgramFullPath(void)
573 {
574     pathconfig_global_init();
575     return _Py_path_config.program_full_path;
576 }
577 
578 
579 wchar_t*
Py_GetPythonHome(void)580 Py_GetPythonHome(void)
581 {
582     pathconfig_global_init();
583     return _Py_path_config.home;
584 }
585 
586 
587 wchar_t *
Py_GetProgramName(void)588 Py_GetProgramName(void)
589 {
590     pathconfig_global_init();
591     return _Py_path_config.program_name;
592 }
593 
594 /* Compute module search path from argv[0] or the current working
595    directory ("-m module" case) which will be prepended to sys.argv:
596    sys.path[0].
597 
598    Return 1 if the path is correctly resolved and written into *path0_p.
599 
600    Return 0 if it fails to resolve the full path. For example, return 0 if the
601    current working directory has been removed (bpo-36236) or if argv is empty.
602 
603    Raise an exception and return -1 on error.
604    */
605 int
_PyPathConfig_ComputeSysPath0(const PyWideStringList * argv,PyObject ** path0_p)606 _PyPathConfig_ComputeSysPath0(const PyWideStringList *argv, PyObject **path0_p)
607 {
608     assert(_PyWideStringList_CheckConsistency(argv));
609 
610     if (argv->length == 0) {
611         /* Leave sys.path unchanged if sys.argv is empty */
612         return 0;
613     }
614 
615     wchar_t *argv0 = argv->items[0];
616     int have_module_arg = (wcscmp(argv0, L"-m") == 0);
617     int have_script_arg = (!have_module_arg && (wcscmp(argv0, L"-c") != 0));
618 
619     wchar_t *path0 = argv0;
620     Py_ssize_t n = 0;
621 
622 #ifdef HAVE_REALPATH
623     wchar_t fullpath[MAXPATHLEN];
624 #elif defined(MS_WINDOWS)
625     wchar_t fullpath[MAX_PATH];
626 #endif
627 
628     if (have_module_arg) {
629 #if defined(HAVE_REALPATH) || defined(MS_WINDOWS)
630         if (!_Py_wgetcwd(fullpath, Py_ARRAY_LENGTH(fullpath))) {
631             return 0;
632         }
633         path0 = fullpath;
634 #else
635         path0 = L".";
636 #endif
637         n = wcslen(path0);
638     }
639 
640 #ifdef HAVE_READLINK
641     wchar_t link[MAXPATHLEN + 1];
642     int nr = 0;
643     wchar_t path0copy[2 * MAXPATHLEN + 1];
644 
645     if (have_script_arg) {
646         nr = _Py_wreadlink(path0, link, Py_ARRAY_LENGTH(link));
647     }
648     if (nr > 0) {
649         /* It's a symlink */
650         link[nr] = '\0';
651         if (link[0] == SEP) {
652             path0 = link; /* Link to absolute path */
653         }
654         else if (wcschr(link, SEP) == NULL) {
655             /* Link without path */
656         }
657         else {
658             /* Must join(dirname(path0), link) */
659             wchar_t *q = wcsrchr(path0, SEP);
660             if (q == NULL) {
661                 /* path0 without path */
662                 path0 = link;
663             }
664             else {
665                 /* Must make a copy, path0copy has room for 2 * MAXPATHLEN */
666                 wcsncpy(path0copy, path0, MAXPATHLEN);
667                 q = wcsrchr(path0copy, SEP);
668                 wcsncpy(q+1, link, MAXPATHLEN);
669                 q[MAXPATHLEN + 1] = L'\0';
670                 path0 = path0copy;
671             }
672         }
673     }
674 #endif /* HAVE_READLINK */
675 
676     wchar_t *p = NULL;
677 
678 #if SEP == '\\'
679     /* Special case for Microsoft filename syntax */
680     if (have_script_arg) {
681         wchar_t *q;
682 #if defined(MS_WINDOWS)
683         /* Replace the first element in argv with the full path. */
684         wchar_t *ptemp;
685         if (GetFullPathNameW(path0,
686                            Py_ARRAY_LENGTH(fullpath),
687                            fullpath,
688                            &ptemp)) {
689             path0 = fullpath;
690         }
691 #endif
692         p = wcsrchr(path0, SEP);
693         /* Test for alternate separator */
694         q = wcsrchr(p ? p : path0, '/');
695         if (q != NULL)
696             p = q;
697         if (p != NULL) {
698             n = p + 1 - path0;
699             if (n > 1 && p[-1] != ':')
700                 n--; /* Drop trailing separator */
701         }
702     }
703 #else
704     /* All other filename syntaxes */
705     if (have_script_arg) {
706 #if defined(HAVE_REALPATH)
707         if (_Py_wrealpath(path0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
708             path0 = fullpath;
709         }
710 #endif
711         p = wcsrchr(path0, SEP);
712     }
713     if (p != NULL) {
714         n = p + 1 - path0;
715 #if SEP == '/' /* Special case for Unix filename syntax */
716         if (n > 1) {
717             /* Drop trailing separator */
718             n--;
719         }
720 #endif /* Unix */
721     }
722 #endif /* All others */
723 
724     PyObject *path0_obj = PyUnicode_FromWideChar(path0, n);
725     if (path0_obj == NULL) {
726         return -1;
727     }
728 
729     *path0_p = path0_obj;
730     return 1;
731 }
732 
733 
734 #ifdef MS_WINDOWS
735 #define WCSTOK wcstok_s
736 #else
737 #define WCSTOK wcstok
738 #endif
739 
740 /* Search for a prefix value in an environment file (pyvenv.cfg).
741 
742    - If found, copy it into *value_p: string which must be freed by
743      PyMem_RawFree().
744    - If not found, *value_p is set to NULL.
745 */
746 PyStatus
_Py_FindEnvConfigValue(FILE * env_file,const wchar_t * key,wchar_t ** value_p)747 _Py_FindEnvConfigValue(FILE *env_file, const wchar_t *key,
748                        wchar_t **value_p)
749 {
750     *value_p = NULL;
751 
752     char buffer[MAXPATHLEN * 2 + 1];  /* allow extra for key, '=', etc. */
753     buffer[Py_ARRAY_LENGTH(buffer)-1] = '\0';
754 
755     while (!feof(env_file)) {
756         char * p = fgets(buffer, Py_ARRAY_LENGTH(buffer) - 1, env_file);
757 
758         if (p == NULL) {
759             break;
760         }
761 
762         size_t n = strlen(p);
763         if (p[n - 1] != '\n') {
764             /* line has overflowed - bail */
765             break;
766         }
767         if (p[0] == '#') {
768             /* Comment - skip */
769             continue;
770         }
771 
772         wchar_t *tmpbuffer = _Py_DecodeUTF8_surrogateescape(buffer, n, NULL);
773         if (tmpbuffer) {
774             wchar_t * state;
775             wchar_t * tok = WCSTOK(tmpbuffer, L" \t\r\n", &state);
776             if ((tok != NULL) && !wcscmp(tok, key)) {
777                 tok = WCSTOK(NULL, L" \t", &state);
778                 if ((tok != NULL) && !wcscmp(tok, L"=")) {
779                     tok = WCSTOK(NULL, L"\r\n", &state);
780                     if (tok != NULL) {
781                         *value_p = _PyMem_RawWcsdup(tok);
782                         PyMem_RawFree(tmpbuffer);
783 
784                         if (*value_p == NULL) {
785                             return _PyStatus_NO_MEMORY();
786                         }
787 
788                         /* found */
789                         return _PyStatus_OK();
790                     }
791                 }
792             }
793             PyMem_RawFree(tmpbuffer);
794         }
795     }
796 
797     /* not found */
798     return _PyStatus_OK();
799 }
800 
801 #ifdef __cplusplus
802 }
803 #endif
804