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