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