bpo-38234: Fix _PyConfig_InitPathConfig() (GH-16335) (GH-16336)

* _PyConfig_InitPathConfig() now starts by copying the global path
  configuration, and then override values set in PyConfig.
* _PyPathConfig_Calculate() implementations no longer override
  _PyPathConfig fields which are already computed. For example,
  if _PyPathConfig.prefix is not NULL, leave it unchanged.
* If Py_SetPath() has been called, _PyConfig_InitPathConfig() doesn't
  call _PyPathConfig_Calculate() anymore.
* _PyPathConfig_Calculate() no longer uses PyConfig,
  except to initialize PyCalculatePath structure.
* pathconfig_calculate(): remove useless temporary
  "_PyPathConfig new_config" variable.
* calculate_module_search_path(): remove hack to workaround memory
  allocation failure, call Py_FatalError() instead.
* Fix get_program_full_path(): handle memory allocation failure.

(cherry picked from commit 9c42f8cda552694f3b47d6388d4ae84d61731872)
diff --git a/PC/getpathp.c b/PC/getpathp.c
index 888bef1..5670fa2 100644
--- a/PC/getpathp.c
+++ b/PC/getpathp.c
@@ -130,6 +130,8 @@
     wchar_t zip_path[MAXPATHLEN+1];
 
     wchar_t *dll_path;
+
+    const wchar_t *pythonpath_env;
 } PyCalculatePath;
 
 
@@ -322,7 +324,7 @@
 
 
 /* assumes argv0_path is MAXPATHLEN+1 bytes long, already \0 term'd.
-   assumption provided by only caller, calculate_path_impl() */
+   assumption provided by only caller, calculate_path() */
 static int
 search_for_prefix(wchar_t *prefix, const wchar_t *argv0_path, const wchar_t *landmark)
 {
@@ -534,6 +536,7 @@
 static PyStatus
 get_program_full_path(_PyPathConfig *pathconfig)
 {
+    PyStatus status;
     const wchar_t *pyvenv_launcher;
     wchar_t program_full_path[MAXPATHLEN+1];
     memset(program_full_path, 0, sizeof(program_full_path));
@@ -548,12 +551,18 @@
     pyvenv_launcher = _wgetenv(L"__PYVENV_LAUNCHER__");
     if (pyvenv_launcher && pyvenv_launcher[0]) {
         /* If overridden, preserve the original full path */
-        pathconfig->base_executable = PyMem_RawMalloc(
-            sizeof(wchar_t) * (MAXPATHLEN + 1));
-        PyStatus status = canonicalize(pathconfig->base_executable,
-                                       program_full_path);
-        if (_PyStatus_EXCEPTION(status)) {
-            return status;
+        if (pathconfig->base_executable == NULL) {
+            pathconfig->base_executable = PyMem_RawMalloc(
+                sizeof(wchar_t) * (MAXPATHLEN + 1));
+            if (pathconfig->base_executable == NULL) {
+                return _PyStatus_NO_MEMORY();
+            }
+
+            status = canonicalize(pathconfig->base_executable,
+                                  program_full_path);
+            if (_PyStatus_EXCEPTION(status)) {
+                return status;
+            }
         }
 
         wcscpy_s(program_full_path, MAXPATHLEN+1, pyvenv_launcher);
@@ -562,11 +571,20 @@
         _wputenv_s(L"__PYVENV_LAUNCHER__", L"");
     }
 
-    pathconfig->program_full_path = PyMem_RawMalloc(
-        sizeof(wchar_t) * (MAXPATHLEN + 1));
+    if (pathconfig->program_full_path == NULL) {
+        pathconfig->program_full_path = PyMem_RawMalloc(
+            sizeof(wchar_t) * (MAXPATHLEN + 1));
+        if (pathconfig->program_full_path == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
 
-    return canonicalize(pathconfig->program_full_path,
-                        program_full_path);
+        status = canonicalize(pathconfig->program_full_path,
+                              program_full_path);
+        if (_PyStatus_EXCEPTION(status)) {
+            return status;
+        }
+    }
+    return _PyStatus_OK();
 }
 
 
@@ -657,7 +675,13 @@
     }
 
     fclose(sp_file);
-    pathconfig->module_search_path = buf;
+    if (pathconfig->module_search_path == NULL) {
+        pathconfig->module_search_path = _PyMem_RawWcsdup(buf);
+        if (pathconfig->module_search_path == NULL) {
+            Py_FatalError("out of memory");
+        }
+    }
+    PyMem_RawFree(buf);
     return 1;
 
 error:
@@ -668,8 +692,7 @@
 
 
 static PyStatus
-calculate_init(PyCalculatePath *calculate,
-               const PyConfig *config)
+calculate_init(PyCalculatePath *calculate, const PyConfig *config)
 {
     calculate->home = config->home;
     calculate->path_env = _wgetenv(L"PATH");
@@ -679,6 +702,8 @@
         return _PyStatus_NO_MEMORY();
     }
 
+    calculate->pythonpath_env = config->pythonpath_env;
+
     return _PyStatus_OK();
 }
 
@@ -785,8 +810,8 @@
 
 
 static PyStatus
-calculate_module_search_path(const PyConfig *config,
-                             PyCalculatePath *calculate, _PyPathConfig *pathconfig,
+calculate_module_search_path(PyCalculatePath *calculate,
+                             _PyPathConfig *pathconfig,
                              wchar_t *prefix)
 {
     int skiphome = calculate->home==NULL ? 0 : 1;
@@ -796,7 +821,7 @@
 #endif
     /* We only use the default relative PYTHONPATH if we haven't
        anything better to use! */
-    int skipdefault = (config->pythonpath_env != NULL ||
+    int skipdefault = (calculate->pythonpath_env != NULL ||
                        calculate->home != NULL ||
                        calculate->machine_path != NULL ||
                        calculate->user_path != NULL);
@@ -835,30 +860,20 @@
         bufsz += wcslen(calculate->machine_path) + 1;
     }
     bufsz += wcslen(calculate->zip_path) + 1;
-    if (config->pythonpath_env != NULL) {
-        bufsz += wcslen(config->pythonpath_env) + 1;
+    if (calculate->pythonpath_env != NULL) {
+        bufsz += wcslen(calculate->pythonpath_env) + 1;
     }
 
     wchar_t *buf, *start_buf;
     buf = PyMem_RawMalloc(bufsz * sizeof(wchar_t));
     if (buf == NULL) {
-        /* We can't exit, so print a warning and limp along */
-        fprintf(stderr, "Can't malloc dynamic PYTHONPATH.\n");
-        if (config->pythonpath_env) {
-            fprintf(stderr, "Using environment $PYTHONPATH.\n");
-            pathconfig->module_search_path = config->pythonpath_env;
-        }
-        else {
-            fprintf(stderr, "Using default static path.\n");
-            pathconfig->module_search_path = PYTHONPATH;
-        }
-        return _PyStatus_OK();
+        Py_FatalError("Can't malloc dynamic PYTHONPATH");
     }
     start_buf = buf;
 
-    if (config->pythonpath_env) {
+    if (calculate->pythonpath_env) {
         if (wcscpy_s(buf, bufsz - (buf - start_buf),
-                     config->pythonpath_env)) {
+                     calculate->pythonpath_env)) {
             return INIT_ERR_BUFFER_OVERFLOW();
         }
         buf = wcschr(buf, L'\0');
@@ -971,8 +986,7 @@
 
 
 static PyStatus
-calculate_path_impl(const PyConfig *config,
-                    PyCalculatePath *calculate, _PyPathConfig *pathconfig)
+calculate_path(PyCalculatePath *calculate, _PyPathConfig *pathconfig)
 {
     PyStatus status;
 
@@ -1003,21 +1017,24 @@
     calculate_home_prefix(calculate, prefix);
 
     if (pathconfig->module_search_path == NULL) {
-        status = calculate_module_search_path(config, calculate,
-                                              pathconfig, prefix);
+        status = calculate_module_search_path(calculate, pathconfig, prefix);
         if (_PyStatus_EXCEPTION(status)) {
             return status;
         }
     }
 
 done:
-    pathconfig->prefix = _PyMem_RawWcsdup(prefix);
     if (pathconfig->prefix == NULL) {
-        return _PyStatus_NO_MEMORY();
+        pathconfig->prefix = _PyMem_RawWcsdup(prefix);
+        if (pathconfig->prefix == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
     }
-    pathconfig->exec_prefix = _PyMem_RawWcsdup(prefix);
     if (pathconfig->exec_prefix == NULL) {
-        return _PyStatus_NO_MEMORY();
+        pathconfig->exec_prefix = _PyMem_RawWcsdup(prefix);
+        if (pathconfig->exec_prefix == NULL) {
+            return _PyStatus_NO_MEMORY();
+        }
     }
 
     return _PyStatus_OK();
@@ -1033,6 +1050,17 @@
 }
 
 
+/* Calculate 'pathconfig' attributes:
+
+   - base_executable
+   - program_full_path
+   - module_search_path
+   - prefix
+   - exec_prefix
+   - isolated
+   - site_import
+
+   If an attribute is already set (non NULL), it is left unchanged. */
 PyStatus
 _PyPathConfig_Calculate(_PyPathConfig *pathconfig, const PyConfig *config)
 {
@@ -1045,7 +1073,7 @@
         goto done;
     }
 
-    status = calculate_path_impl(config, &calculate, pathconfig);
+    status = calculate_path(&calculate, pathconfig);
 
 done:
     calculate_free(&calculate);