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
15extern "C" {
16#endif
17
18
19_PyPathConfig _Py_path_config = _PyPathConfig_INIT;
20
21
22static int
23copy_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
39static void
40pathconfig_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
70static PyStatus
71pathconfig_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
100void
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
112static wchar_t*
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
143static PyStatus
144pathconfig_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
181no_memory:
182 status = _PyStatus_NO_MEMORY();
183
184done:
185 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
186 return status;
187}
188
189PyObject *
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
258fail:
259 Py_DECREF(dict);
260 return NULL;
261}
262
263
264PyStatus
265_PyConfig_WritePathConfig(const PyConfig *config)
266{
267 return pathconfig_set_from_config(&_Py_path_config, config);
268}
269
270
271static PyStatus
272config_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*/
334static PyStatus
335pathconfig_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
357done:
358 PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
359 return status;
360}
361
362
363static PyStatus
364config_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
420no_memory:
421 status = _PyStatus_NO_MEMORY();
422
423done:
424 pathconfig_clear(&pathconfig);
425 return status;
426}
427
428
429PyStatus
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
470static void _Py_NO_RETURN
471path_out_of_memory(const char *func)
472{
473 _Py_FatalErrorFunc(func, "out of memory");
474}
475
476void
477Py_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
506void
507Py_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
527void
528Py_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
547void
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
568wchar_t *
569Py_GetPath(void)
570{
571 return _Py_path_config.module_search_path;
572}
573
574
575wchar_t *
576Py_GetPrefix(void)
577{
578 return _Py_path_config.prefix;
579}
580
581
582wchar_t *
583Py_GetExecPrefix(void)
584{
585 return _Py_path_config.exec_prefix;
586}
587
588
589wchar_t *
590Py_GetProgramFullPath(void)
591{
592 return _Py_path_config.program_full_path;
593}
594
595
596wchar_t*
597Py_GetPythonHome(void)
598{
599 return _Py_path_config.home;
600}
601
602
603wchar_t *
604Py_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 */
620int
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*/
761PyStatus
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