bpo-32030: Add _PyPathConfig_ComputeArgv0() (#4845)

Changes:

* Split _PySys_SetArgvWithError() into subfunctions for Py_Main():

  * Create the Python list object
  * Set sys.argv to the list
  * Compute argv0
  * Prepend argv0 to sys.path

* Add _PyPathConfig_ComputeArgv0()
* Remove _PySys_SetArgvWithError()
* Py_Main() now splits the code to compute sys.argv/path0 and the
  code to update the sys module: add pymain_compute_argv()
  subfunction.
diff --git a/Python/pathconfig.c b/Python/pathconfig.c
index 53ddfc9..b17ae82 100644
--- a/Python/pathconfig.c
+++ b/Python/pathconfig.c
@@ -261,6 +261,104 @@
 }
 
 
+#define _HAVE_SCRIPT_ARGUMENT(argc, argv) \
+  (argc > 0 && argv0 != NULL && \
+   wcscmp(argv0, L"-c") != 0 && wcscmp(argv0, L"-m") != 0)
+
+/* Compute argv[0] which will be prepended to sys.argv */
+PyObject*
+_PyPathConfig_ComputeArgv0(int argc, wchar_t **argv)
+{
+    wchar_t *argv0;
+    wchar_t *p = NULL;
+    Py_ssize_t n = 0;
+#ifdef HAVE_READLINK
+    wchar_t link[MAXPATHLEN+1];
+    wchar_t argv0copy[2*MAXPATHLEN+1];
+    int nr = 0;
+#endif
+#if defined(HAVE_REALPATH)
+    wchar_t fullpath[MAXPATHLEN];
+#elif defined(MS_WINDOWS)
+    wchar_t fullpath[MAX_PATH];
+#endif
+
+
+    argv0 = argv[0];
+
+#ifdef HAVE_READLINK
+    if (_HAVE_SCRIPT_ARGUMENT(argc, argv))
+        nr = _Py_wreadlink(argv0, link, MAXPATHLEN);
+    if (nr > 0) {
+        /* It's a symlink */
+        link[nr] = '\0';
+        if (link[0] == SEP)
+            argv0 = link; /* Link to absolute path */
+        else if (wcschr(link, SEP) == NULL)
+            ; /* Link without path */
+        else {
+            /* Must join(dirname(argv0), link) */
+            wchar_t *q = wcsrchr(argv0, SEP);
+            if (q == NULL)
+                argv0 = link; /* argv0 without path */
+            else {
+                /* Must make a copy, argv0copy has room for 2 * MAXPATHLEN */
+                wcsncpy(argv0copy, argv0, MAXPATHLEN);
+                q = wcsrchr(argv0copy, SEP);
+                wcsncpy(q+1, link, MAXPATHLEN);
+                q[MAXPATHLEN + 1] = L'\0';
+                argv0 = argv0copy;
+            }
+        }
+    }
+#endif /* HAVE_READLINK */
+
+#if SEP == '\\'
+    /* Special case for Microsoft filename syntax */
+    if (_HAVE_SCRIPT_ARGUMENT(argc, argv)) {
+        wchar_t *q;
+#if defined(MS_WINDOWS)
+        /* Replace the first element in argv with the full path. */
+        wchar_t *ptemp;
+        if (GetFullPathNameW(argv0,
+                           Py_ARRAY_LENGTH(fullpath),
+                           fullpath,
+                           &ptemp)) {
+            argv0 = fullpath;
+        }
+#endif
+        p = wcsrchr(argv0, SEP);
+        /* Test for alternate separator */
+        q = wcsrchr(p ? p : argv0, '/');
+        if (q != NULL)
+            p = q;
+        if (p != NULL) {
+            n = p + 1 - argv0;
+            if (n > 1 && p[-1] != ':')
+                n--; /* Drop trailing separator */
+        }
+    }
+#else /* All other filename syntaxes */
+    if (_HAVE_SCRIPT_ARGUMENT(argc, argv)) {
+#if defined(HAVE_REALPATH)
+        if (_Py_wrealpath(argv0, fullpath, Py_ARRAY_LENGTH(fullpath))) {
+            argv0 = fullpath;
+        }
+#endif
+        p = wcsrchr(argv0, SEP);
+    }
+    if (p != NULL) {
+        n = p + 1 - argv0;
+#if SEP == '/' /* Special case for Unix filename syntax */
+        if (n > 1)
+            n--; /* Drop trailing separator */
+#endif /* Unix */
+    }
+#endif /* All others */
+
+    return PyUnicode_FromWideChar(argv0, n);
+}
+
 #ifdef __cplusplus
 }
 #endif