17db96d56Sopenharmony_ci/*
27db96d56Sopenharmony_ci * Copyright (C) 2011-2013 Vinay Sajip.
37db96d56Sopenharmony_ci * Licensed to PSF under a contributor agreement.
47db96d56Sopenharmony_ci *
57db96d56Sopenharmony_ci * Based on the work of:
67db96d56Sopenharmony_ci *
77db96d56Sopenharmony_ci * Mark Hammond (original author of Python version)
87db96d56Sopenharmony_ci * Curt Hagenlocher (job management)
97db96d56Sopenharmony_ci */
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci#include <windows.h>
127db96d56Sopenharmony_ci#include <shlobj.h>
137db96d56Sopenharmony_ci#include <stdio.h>
147db96d56Sopenharmony_ci#include <tchar.h>
157db96d56Sopenharmony_ci
167db96d56Sopenharmony_ci#define BUFSIZE 256
177db96d56Sopenharmony_ci#define MSGSIZE 1024
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ci/* Build options. */
207db96d56Sopenharmony_ci#define SKIP_PREFIX
217db96d56Sopenharmony_ci#define SEARCH_PATH
227db96d56Sopenharmony_ci
237db96d56Sopenharmony_ci/* Error codes */
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ci#define RC_NO_STD_HANDLES   100
267db96d56Sopenharmony_ci#define RC_CREATE_PROCESS   101
277db96d56Sopenharmony_ci#define RC_BAD_VIRTUAL_PATH 102
287db96d56Sopenharmony_ci#define RC_NO_PYTHON        103
297db96d56Sopenharmony_ci#define RC_NO_MEMORY        104
307db96d56Sopenharmony_ci/*
317db96d56Sopenharmony_ci * SCRIPT_WRAPPER is used to choose one of the variants of an executable built
327db96d56Sopenharmony_ci * from this source file. If not defined, the PEP 397 Python launcher is built;
337db96d56Sopenharmony_ci * if defined, a script launcher of the type used by setuptools is built, which
347db96d56Sopenharmony_ci * looks for a script name related to the executable name and runs that script
357db96d56Sopenharmony_ci * with the appropriate Python interpreter.
367db96d56Sopenharmony_ci *
377db96d56Sopenharmony_ci * SCRIPT_WRAPPER should be undefined in the source, and defined in a VS project
387db96d56Sopenharmony_ci * which builds the setuptools-style launcher.
397db96d56Sopenharmony_ci */
407db96d56Sopenharmony_ci#if defined(SCRIPT_WRAPPER)
417db96d56Sopenharmony_ci#define RC_NO_SCRIPT        105
427db96d56Sopenharmony_ci#endif
437db96d56Sopenharmony_ci/*
447db96d56Sopenharmony_ci * VENV_REDIRECT is used to choose the variant that looks for an adjacent or
457db96d56Sopenharmony_ci * one-level-higher pyvenv.cfg, and uses its "home" property to locate and
467db96d56Sopenharmony_ci * launch the original python.exe.
477db96d56Sopenharmony_ci */
487db96d56Sopenharmony_ci#if defined(VENV_REDIRECT)
497db96d56Sopenharmony_ci#define RC_NO_VENV_CFG      106
507db96d56Sopenharmony_ci#define RC_BAD_VENV_CFG     107
517db96d56Sopenharmony_ci#endif
527db96d56Sopenharmony_ci
537db96d56Sopenharmony_ci/* Just for now - static definition */
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_cistatic FILE * log_fp = NULL;
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_cistatic wchar_t *
587db96d56Sopenharmony_ciskip_whitespace(wchar_t * p)
597db96d56Sopenharmony_ci{
607db96d56Sopenharmony_ci    while (*p && isspace(*p))
617db96d56Sopenharmony_ci        ++p;
627db96d56Sopenharmony_ci    return p;
637db96d56Sopenharmony_ci}
647db96d56Sopenharmony_ci
657db96d56Sopenharmony_cistatic void
667db96d56Sopenharmony_cidebug(wchar_t * format, ...)
677db96d56Sopenharmony_ci{
687db96d56Sopenharmony_ci    va_list va;
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci    if (log_fp != NULL) {
717db96d56Sopenharmony_ci        va_start(va, format);
727db96d56Sopenharmony_ci        vfwprintf_s(log_fp, format, va);
737db96d56Sopenharmony_ci        va_end(va);
747db96d56Sopenharmony_ci    }
757db96d56Sopenharmony_ci}
767db96d56Sopenharmony_ci
777db96d56Sopenharmony_cistatic void
787db96d56Sopenharmony_ciwinerror(int rc, wchar_t * message, int size)
797db96d56Sopenharmony_ci{
807db96d56Sopenharmony_ci    FormatMessageW(
817db96d56Sopenharmony_ci        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
827db96d56Sopenharmony_ci        NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
837db96d56Sopenharmony_ci        message, size, NULL);
847db96d56Sopenharmony_ci}
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_cistatic void
877db96d56Sopenharmony_cierror(int rc, wchar_t * format, ... )
887db96d56Sopenharmony_ci{
897db96d56Sopenharmony_ci    va_list va;
907db96d56Sopenharmony_ci    wchar_t message[MSGSIZE];
917db96d56Sopenharmony_ci    wchar_t win_message[MSGSIZE];
927db96d56Sopenharmony_ci    int len;
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_ci    va_start(va, format);
957db96d56Sopenharmony_ci    len = _vsnwprintf_s(message, MSGSIZE, _TRUNCATE, format, va);
967db96d56Sopenharmony_ci    va_end(va);
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_ci    if (rc == 0) {  /* a Windows error */
997db96d56Sopenharmony_ci        winerror(GetLastError(), win_message, MSGSIZE);
1007db96d56Sopenharmony_ci        if (len >= 0) {
1017db96d56Sopenharmony_ci            _snwprintf_s(&message[len], MSGSIZE - len, _TRUNCATE, L": %ls",
1027db96d56Sopenharmony_ci                         win_message);
1037db96d56Sopenharmony_ci        }
1047db96d56Sopenharmony_ci    }
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_ci#if !defined(_WINDOWS)
1077db96d56Sopenharmony_ci    fwprintf(stderr, L"%ls\n", message);
1087db96d56Sopenharmony_ci#else
1097db96d56Sopenharmony_ci    MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...",
1107db96d56Sopenharmony_ci               MB_OK);
1117db96d56Sopenharmony_ci#endif
1127db96d56Sopenharmony_ci    exit(rc);
1137db96d56Sopenharmony_ci}
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci/*
1167db96d56Sopenharmony_ci * This function is here to simplify memory management
1177db96d56Sopenharmony_ci * and to treat blank values as if they are absent.
1187db96d56Sopenharmony_ci */
1197db96d56Sopenharmony_cistatic wchar_t * get_env(wchar_t * key)
1207db96d56Sopenharmony_ci{
1217db96d56Sopenharmony_ci    /* This is not thread-safe, just like getenv */
1227db96d56Sopenharmony_ci    static wchar_t buf[BUFSIZE];
1237db96d56Sopenharmony_ci    DWORD result = GetEnvironmentVariableW(key, buf, BUFSIZE);
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci    if (result >= BUFSIZE) {
1267db96d56Sopenharmony_ci        /* Large environment variable. Accept some leakage */
1277db96d56Sopenharmony_ci        wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1));
1287db96d56Sopenharmony_ci        if (buf2 == NULL) {
1297db96d56Sopenharmony_ci            error(RC_NO_MEMORY, L"Could not allocate environment buffer");
1307db96d56Sopenharmony_ci        }
1317db96d56Sopenharmony_ci        GetEnvironmentVariableW(key, buf2, result);
1327db96d56Sopenharmony_ci        return buf2;
1337db96d56Sopenharmony_ci    }
1347db96d56Sopenharmony_ci
1357db96d56Sopenharmony_ci    if (result == 0)
1367db96d56Sopenharmony_ci        /* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND,
1377db96d56Sopenharmony_ci           or an empty environment variable. */
1387db96d56Sopenharmony_ci        return NULL;
1397db96d56Sopenharmony_ci
1407db96d56Sopenharmony_ci    return buf;
1417db96d56Sopenharmony_ci}
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci#if defined(_DEBUG)
1447db96d56Sopenharmony_ci/* Do not define EXECUTABLEPATH_VALUE in debug builds as it'll
1457db96d56Sopenharmony_ci   never point to the debug build. */
1467db96d56Sopenharmony_ci#if defined(_WINDOWS)
1477db96d56Sopenharmony_ci
1487db96d56Sopenharmony_ci#define PYTHON_EXECUTABLE L"pythonw_d.exe"
1497db96d56Sopenharmony_ci
1507db96d56Sopenharmony_ci#else
1517db96d56Sopenharmony_ci
1527db96d56Sopenharmony_ci#define PYTHON_EXECUTABLE L"python_d.exe"
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci#endif
1557db96d56Sopenharmony_ci#else
1567db96d56Sopenharmony_ci#if defined(_WINDOWS)
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_ci#define PYTHON_EXECUTABLE L"pythonw.exe"
1597db96d56Sopenharmony_ci#define EXECUTABLEPATH_VALUE L"WindowedExecutablePath"
1607db96d56Sopenharmony_ci
1617db96d56Sopenharmony_ci#else
1627db96d56Sopenharmony_ci
1637db96d56Sopenharmony_ci#define PYTHON_EXECUTABLE L"python.exe"
1647db96d56Sopenharmony_ci#define EXECUTABLEPATH_VALUE L"ExecutablePath"
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ci#endif
1677db96d56Sopenharmony_ci#endif
1687db96d56Sopenharmony_ci
1697db96d56Sopenharmony_ci#define MAX_VERSION_SIZE    8
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_citypedef struct {
1727db96d56Sopenharmony_ci    wchar_t version[MAX_VERSION_SIZE]; /* m.n */
1737db96d56Sopenharmony_ci    int bits;   /* 32 or 64 */
1747db96d56Sopenharmony_ci    wchar_t executable[MAX_PATH];
1757db96d56Sopenharmony_ci    wchar_t exe_display[MAX_PATH];
1767db96d56Sopenharmony_ci} INSTALLED_PYTHON;
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ci/*
1797db96d56Sopenharmony_ci * To avoid messing about with heap allocations, just assume we can allocate
1807db96d56Sopenharmony_ci * statically and never have to deal with more versions than this.
1817db96d56Sopenharmony_ci */
1827db96d56Sopenharmony_ci#define MAX_INSTALLED_PYTHONS   100
1837db96d56Sopenharmony_ci
1847db96d56Sopenharmony_cistatic INSTALLED_PYTHON installed_pythons[MAX_INSTALLED_PYTHONS];
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_cistatic size_t num_installed_pythons = 0;
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_ci/*
1897db96d56Sopenharmony_ci * To hold SOFTWARE\Python\PythonCore\X.Y...\InstallPath
1907db96d56Sopenharmony_ci * The version name can be longer than MAX_VERSION_SIZE, but will be
1917db96d56Sopenharmony_ci * truncated to just X.Y for comparisons.
1927db96d56Sopenharmony_ci */
1937db96d56Sopenharmony_ci#define IP_BASE_SIZE 80
1947db96d56Sopenharmony_ci#define IP_VERSION_SIZE 8
1957db96d56Sopenharmony_ci#define IP_SIZE (IP_BASE_SIZE + IP_VERSION_SIZE)
1967db96d56Sopenharmony_ci#define CORE_PATH L"SOFTWARE\\Python\\PythonCore"
1977db96d56Sopenharmony_ci/*
1987db96d56Sopenharmony_ci * Installations from the Microsoft Store will set the same registry keys,
1997db96d56Sopenharmony_ci * but because of a limitation in Windows they cannot be enumerated normally
2007db96d56Sopenharmony_ci * (unless you have no other Python installations... which is probably false
2017db96d56Sopenharmony_ci * because that's the most likely way to get this launcher!)
2027db96d56Sopenharmony_ci * This key is under HKEY_LOCAL_MACHINE
2037db96d56Sopenharmony_ci */
2047db96d56Sopenharmony_ci#define LOOKASIDE_PATH L"SOFTWARE\\Microsoft\\AppModel\\Lookaside\\user\\Software\\Python\\PythonCore"
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_cistatic wchar_t * location_checks[] = {
2077db96d56Sopenharmony_ci    L"\\",
2087db96d56Sopenharmony_ci    L"\\PCbuild\\win32\\",
2097db96d56Sopenharmony_ci    L"\\PCbuild\\amd64\\",
2107db96d56Sopenharmony_ci    /* To support early 32bit versions of Python that stuck the build binaries
2117db96d56Sopenharmony_ci    * directly in PCbuild... */
2127db96d56Sopenharmony_ci    L"\\PCbuild\\",
2137db96d56Sopenharmony_ci    NULL
2147db96d56Sopenharmony_ci};
2157db96d56Sopenharmony_ci
2167db96d56Sopenharmony_cistatic INSTALLED_PYTHON *
2177db96d56Sopenharmony_cifind_existing_python(const wchar_t * path)
2187db96d56Sopenharmony_ci{
2197db96d56Sopenharmony_ci    INSTALLED_PYTHON * result = NULL;
2207db96d56Sopenharmony_ci    size_t i;
2217db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip;
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci    for (i = 0, ip = installed_pythons; i < num_installed_pythons; i++, ip++) {
2247db96d56Sopenharmony_ci        if (_wcsicmp(path, ip->executable) == 0) {
2257db96d56Sopenharmony_ci            result = ip;
2267db96d56Sopenharmony_ci            break;
2277db96d56Sopenharmony_ci        }
2287db96d56Sopenharmony_ci    }
2297db96d56Sopenharmony_ci    return result;
2307db96d56Sopenharmony_ci}
2317db96d56Sopenharmony_ci
2327db96d56Sopenharmony_cistatic INSTALLED_PYTHON *
2337db96d56Sopenharmony_cifind_existing_python2(int bits, const wchar_t * version)
2347db96d56Sopenharmony_ci{
2357db96d56Sopenharmony_ci    INSTALLED_PYTHON * result = NULL;
2367db96d56Sopenharmony_ci    size_t i;
2377db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip;
2387db96d56Sopenharmony_ci
2397db96d56Sopenharmony_ci    for (i = 0, ip = installed_pythons; i < num_installed_pythons; i++, ip++) {
2407db96d56Sopenharmony_ci        if (bits == ip->bits && _wcsicmp(version, ip->version) == 0) {
2417db96d56Sopenharmony_ci            result = ip;
2427db96d56Sopenharmony_ci            break;
2437db96d56Sopenharmony_ci        }
2447db96d56Sopenharmony_ci    }
2457db96d56Sopenharmony_ci    return result;
2467db96d56Sopenharmony_ci}
2477db96d56Sopenharmony_ci
2487db96d56Sopenharmony_cistatic void
2497db96d56Sopenharmony_ci_locate_pythons_for_key(HKEY root, LPCWSTR subkey, REGSAM flags, int bits,
2507db96d56Sopenharmony_ci                        int display_name_only)
2517db96d56Sopenharmony_ci{
2527db96d56Sopenharmony_ci    HKEY core_root, ip_key;
2537db96d56Sopenharmony_ci    LSTATUS status = RegOpenKeyExW(root, subkey, 0, flags, &core_root);
2547db96d56Sopenharmony_ci    wchar_t message[MSGSIZE];
2557db96d56Sopenharmony_ci    DWORD i;
2567db96d56Sopenharmony_ci    size_t n;
2577db96d56Sopenharmony_ci    BOOL ok, append_name;
2587db96d56Sopenharmony_ci    DWORD type, data_size, attrs;
2597db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip, * pip;
2607db96d56Sopenharmony_ci    wchar_t ip_version[IP_VERSION_SIZE];
2617db96d56Sopenharmony_ci    wchar_t ip_path[IP_SIZE];
2627db96d56Sopenharmony_ci    wchar_t * check;
2637db96d56Sopenharmony_ci    wchar_t ** checkp;
2647db96d56Sopenharmony_ci    wchar_t *key_name = (root == HKEY_LOCAL_MACHINE) ? L"HKLM" : L"HKCU";
2657db96d56Sopenharmony_ci
2667db96d56Sopenharmony_ci    if (status != ERROR_SUCCESS)
2677db96d56Sopenharmony_ci        debug(L"locate_pythons_for_key: unable to open PythonCore key in %ls\n",
2687db96d56Sopenharmony_ci              key_name);
2697db96d56Sopenharmony_ci    else {
2707db96d56Sopenharmony_ci        ip = &installed_pythons[num_installed_pythons];
2717db96d56Sopenharmony_ci        for (i = 0; num_installed_pythons < MAX_INSTALLED_PYTHONS; i++) {
2727db96d56Sopenharmony_ci            status = RegEnumKeyW(core_root, i, ip_version, IP_VERSION_SIZE);
2737db96d56Sopenharmony_ci            if (status != ERROR_SUCCESS) {
2747db96d56Sopenharmony_ci                if (status != ERROR_NO_MORE_ITEMS) {
2757db96d56Sopenharmony_ci                    /* unexpected error */
2767db96d56Sopenharmony_ci                    winerror(status, message, MSGSIZE);
2777db96d56Sopenharmony_ci                    debug(L"Can't enumerate registry key for version %ls: %ls\n",
2787db96d56Sopenharmony_ci                          ip_version, message);
2797db96d56Sopenharmony_ci                }
2807db96d56Sopenharmony_ci                break;
2817db96d56Sopenharmony_ci            }
2827db96d56Sopenharmony_ci            else {
2837db96d56Sopenharmony_ci                wcsncpy_s(ip->version, MAX_VERSION_SIZE, ip_version,
2847db96d56Sopenharmony_ci                          MAX_VERSION_SIZE-1);
2857db96d56Sopenharmony_ci                /* Still treating version as "x.y" rather than sys.winver
2867db96d56Sopenharmony_ci                 * When PEP 514 tags are properly used, we shouldn't need
2877db96d56Sopenharmony_ci                 * to strip this off here.
2887db96d56Sopenharmony_ci                 */
2897db96d56Sopenharmony_ci                check = wcsrchr(ip->version, L'-');
2907db96d56Sopenharmony_ci                if (check && !wcscmp(check, L"-32")) {
2917db96d56Sopenharmony_ci                    *check = L'\0';
2927db96d56Sopenharmony_ci                }
2937db96d56Sopenharmony_ci                _snwprintf_s(ip_path, IP_SIZE, _TRUNCATE,
2947db96d56Sopenharmony_ci                             L"%ls\\%ls\\InstallPath", subkey, ip_version);
2957db96d56Sopenharmony_ci                status = RegOpenKeyExW(root, ip_path, 0, flags, &ip_key);
2967db96d56Sopenharmony_ci                if (status != ERROR_SUCCESS) {
2977db96d56Sopenharmony_ci                    winerror(status, message, MSGSIZE);
2987db96d56Sopenharmony_ci                    /* Note: 'message' already has a trailing \n*/
2997db96d56Sopenharmony_ci                    debug(L"%ls\\%ls: %ls", key_name, ip_path, message);
3007db96d56Sopenharmony_ci                    continue;
3017db96d56Sopenharmony_ci                }
3027db96d56Sopenharmony_ci                data_size = sizeof(ip->executable) - 1;
3037db96d56Sopenharmony_ci                append_name = FALSE;
3047db96d56Sopenharmony_ci#ifdef EXECUTABLEPATH_VALUE
3057db96d56Sopenharmony_ci                status = RegQueryValueExW(ip_key, EXECUTABLEPATH_VALUE, NULL, &type,
3067db96d56Sopenharmony_ci                                          (LPBYTE)ip->executable, &data_size);
3077db96d56Sopenharmony_ci#else
3087db96d56Sopenharmony_ci                status = ERROR_FILE_NOT_FOUND; /* actual error doesn't matter */
3097db96d56Sopenharmony_ci#endif
3107db96d56Sopenharmony_ci                if (status != ERROR_SUCCESS || type != REG_SZ || !data_size) {
3117db96d56Sopenharmony_ci                    append_name = TRUE;
3127db96d56Sopenharmony_ci                    data_size = sizeof(ip->executable) - 1;
3137db96d56Sopenharmony_ci                    status = RegQueryValueExW(ip_key, NULL, NULL, &type,
3147db96d56Sopenharmony_ci                                              (LPBYTE)ip->executable, &data_size);
3157db96d56Sopenharmony_ci                    if (status != ERROR_SUCCESS) {
3167db96d56Sopenharmony_ci                        winerror(status, message, MSGSIZE);
3177db96d56Sopenharmony_ci                        debug(L"%ls\\%ls: %ls\n", key_name, ip_path, message);
3187db96d56Sopenharmony_ci                        RegCloseKey(ip_key);
3197db96d56Sopenharmony_ci                        continue;
3207db96d56Sopenharmony_ci                    }
3217db96d56Sopenharmony_ci                }
3227db96d56Sopenharmony_ci                RegCloseKey(ip_key);
3237db96d56Sopenharmony_ci                if (type != REG_SZ) {
3247db96d56Sopenharmony_ci                    continue;
3257db96d56Sopenharmony_ci                }
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ci                data_size = data_size / sizeof(wchar_t) - 1;  /* for NUL */
3287db96d56Sopenharmony_ci                if (ip->executable[data_size - 1] == L'\\')
3297db96d56Sopenharmony_ci                    --data_size; /* reg value ended in a backslash */
3307db96d56Sopenharmony_ci                /* ip->executable is data_size long */
3317db96d56Sopenharmony_ci                for (checkp = location_checks; *checkp; ++checkp) {
3327db96d56Sopenharmony_ci                    check = *checkp;
3337db96d56Sopenharmony_ci                    if (append_name) {
3347db96d56Sopenharmony_ci                        _snwprintf_s(&ip->executable[data_size],
3357db96d56Sopenharmony_ci                                     MAX_PATH - data_size,
3367db96d56Sopenharmony_ci                                     MAX_PATH - data_size,
3377db96d56Sopenharmony_ci                                     L"%ls%ls", check, PYTHON_EXECUTABLE);
3387db96d56Sopenharmony_ci                    }
3397db96d56Sopenharmony_ci                    attrs = GetFileAttributesW(ip->executable);
3407db96d56Sopenharmony_ci                    if (attrs == INVALID_FILE_ATTRIBUTES) {
3417db96d56Sopenharmony_ci                        winerror(GetLastError(), message, MSGSIZE);
3427db96d56Sopenharmony_ci                        debug(L"locate_pythons_for_key: %ls: %ls",
3437db96d56Sopenharmony_ci                              ip->executable, message);
3447db96d56Sopenharmony_ci                    }
3457db96d56Sopenharmony_ci                    else if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
3467db96d56Sopenharmony_ci                        debug(L"locate_pythons_for_key: '%ls' is a directory\n",
3477db96d56Sopenharmony_ci                              ip->executable);
3487db96d56Sopenharmony_ci                    }
3497db96d56Sopenharmony_ci                    else if (find_existing_python(ip->executable)) {
3507db96d56Sopenharmony_ci                        debug(L"locate_pythons_for_key: %ls: already found\n",
3517db96d56Sopenharmony_ci                              ip->executable);
3527db96d56Sopenharmony_ci                    }
3537db96d56Sopenharmony_ci                    else {
3547db96d56Sopenharmony_ci                        /* check the executable type. */
3557db96d56Sopenharmony_ci                        if (bits) {
3567db96d56Sopenharmony_ci                            ip->bits = bits;
3577db96d56Sopenharmony_ci                        } else {
3587db96d56Sopenharmony_ci                            ok = GetBinaryTypeW(ip->executable, &attrs);
3597db96d56Sopenharmony_ci                            if (!ok) {
3607db96d56Sopenharmony_ci                                debug(L"Failure getting binary type: %ls\n",
3617db96d56Sopenharmony_ci                                      ip->executable);
3627db96d56Sopenharmony_ci                            }
3637db96d56Sopenharmony_ci                            else {
3647db96d56Sopenharmony_ci                                if (attrs == SCS_64BIT_BINARY)
3657db96d56Sopenharmony_ci                                    ip->bits = 64;
3667db96d56Sopenharmony_ci                                else if (attrs == SCS_32BIT_BINARY)
3677db96d56Sopenharmony_ci                                    ip->bits = 32;
3687db96d56Sopenharmony_ci                                else
3697db96d56Sopenharmony_ci                                    ip->bits = 0;
3707db96d56Sopenharmony_ci                            }
3717db96d56Sopenharmony_ci                        }
3727db96d56Sopenharmony_ci                        if (ip->bits == 0) {
3737db96d56Sopenharmony_ci                            debug(L"locate_pythons_for_key: %ls: \
3747db96d56Sopenharmony_ciinvalid binary type: %X\n",
3757db96d56Sopenharmony_ci                                  ip->executable, attrs);
3767db96d56Sopenharmony_ci                        }
3777db96d56Sopenharmony_ci                        else {
3787db96d56Sopenharmony_ci                            if (display_name_only) {
3797db96d56Sopenharmony_ci                                /* display just the executable name. This is
3807db96d56Sopenharmony_ci                                 * primarily for the Store installs */
3817db96d56Sopenharmony_ci                                const wchar_t *name = wcsrchr(ip->executable, L'\\');
3827db96d56Sopenharmony_ci                                if (name) {
3837db96d56Sopenharmony_ci                                    wcscpy_s(ip->exe_display, MAX_PATH, name+1);
3847db96d56Sopenharmony_ci                                }
3857db96d56Sopenharmony_ci                            }
3867db96d56Sopenharmony_ci                            if (wcschr(ip->executable, L' ') != NULL) {
3877db96d56Sopenharmony_ci                                /* has spaces, so quote, and set original as
3887db96d56Sopenharmony_ci                                 * the display name */
3897db96d56Sopenharmony_ci                                if (!ip->exe_display[0]) {
3907db96d56Sopenharmony_ci                                    wcscpy_s(ip->exe_display, MAX_PATH, ip->executable);
3917db96d56Sopenharmony_ci                                }
3927db96d56Sopenharmony_ci                                n = wcslen(ip->executable);
3937db96d56Sopenharmony_ci                                memmove(&ip->executable[1],
3947db96d56Sopenharmony_ci                                        ip->executable, n * sizeof(wchar_t));
3957db96d56Sopenharmony_ci                                ip->executable[0] = L'\"';
3967db96d56Sopenharmony_ci                                ip->executable[n + 1] = L'\"';
3977db96d56Sopenharmony_ci                                ip->executable[n + 2] = L'\0';
3987db96d56Sopenharmony_ci                            }
3997db96d56Sopenharmony_ci                            debug(L"locate_pythons_for_key: %ls \
4007db96d56Sopenharmony_ciis a %dbit executable\n",
4017db96d56Sopenharmony_ci                                ip->executable, ip->bits);
4027db96d56Sopenharmony_ci                            if (find_existing_python2(ip->bits, ip->version)) {
4037db96d56Sopenharmony_ci                                debug(L"locate_pythons_for_key: %ls-%i: already \
4047db96d56Sopenharmony_cifound\n", ip->version, ip->bits);
4057db96d56Sopenharmony_ci                            }
4067db96d56Sopenharmony_ci                            else {
4077db96d56Sopenharmony_ci                                ++num_installed_pythons;
4087db96d56Sopenharmony_ci                                pip = ip++;
4097db96d56Sopenharmony_ci                                if (num_installed_pythons >=
4107db96d56Sopenharmony_ci                                    MAX_INSTALLED_PYTHONS)
4117db96d56Sopenharmony_ci                                    break;
4127db96d56Sopenharmony_ci                            }
4137db96d56Sopenharmony_ci                        }
4147db96d56Sopenharmony_ci                    }
4157db96d56Sopenharmony_ci                }
4167db96d56Sopenharmony_ci            }
4177db96d56Sopenharmony_ci        }
4187db96d56Sopenharmony_ci        RegCloseKey(core_root);
4197db96d56Sopenharmony_ci    }
4207db96d56Sopenharmony_ci}
4217db96d56Sopenharmony_ci
4227db96d56Sopenharmony_cistatic int
4237db96d56Sopenharmony_cicompare_pythons(const void * p1, const void * p2)
4247db96d56Sopenharmony_ci{
4257db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip1 = (INSTALLED_PYTHON *) p1;
4267db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip2 = (INSTALLED_PYTHON *) p2;
4277db96d56Sopenharmony_ci    /* note reverse sorting on version */
4287db96d56Sopenharmony_ci    int result = CompareStringW(LOCALE_INVARIANT, SORT_DIGITSASNUMBERS,
4297db96d56Sopenharmony_ci                                ip2->version, -1, ip1->version, -1);
4307db96d56Sopenharmony_ci    switch (result) {
4317db96d56Sopenharmony_ci    case 0:
4327db96d56Sopenharmony_ci        error(0, L"CompareStringW failed");
4337db96d56Sopenharmony_ci        return 0;
4347db96d56Sopenharmony_ci    case CSTR_LESS_THAN:
4357db96d56Sopenharmony_ci        return -1;
4367db96d56Sopenharmony_ci    case CSTR_EQUAL:
4377db96d56Sopenharmony_ci        return ip2->bits - ip1->bits; /* 64 before 32 */
4387db96d56Sopenharmony_ci    case CSTR_GREATER_THAN:
4397db96d56Sopenharmony_ci        return 1;
4407db96d56Sopenharmony_ci    default:
4417db96d56Sopenharmony_ci        return 0; // This should never be reached.
4427db96d56Sopenharmony_ci    }
4437db96d56Sopenharmony_ci}
4447db96d56Sopenharmony_ci
4457db96d56Sopenharmony_cistatic void
4467db96d56Sopenharmony_cilocate_pythons_for_key(HKEY root, REGSAM flags)
4477db96d56Sopenharmony_ci{
4487db96d56Sopenharmony_ci    _locate_pythons_for_key(root, CORE_PATH, flags, 0, FALSE);
4497db96d56Sopenharmony_ci}
4507db96d56Sopenharmony_ci
4517db96d56Sopenharmony_cistatic void
4527db96d56Sopenharmony_cilocate_store_pythons()
4537db96d56Sopenharmony_ci{
4547db96d56Sopenharmony_ci#if defined(_M_X64)
4557db96d56Sopenharmony_ci    /* 64bit process, so look in native registry */
4567db96d56Sopenharmony_ci    _locate_pythons_for_key(HKEY_LOCAL_MACHINE, LOOKASIDE_PATH,
4577db96d56Sopenharmony_ci                            KEY_READ, 64, TRUE);
4587db96d56Sopenharmony_ci#else
4597db96d56Sopenharmony_ci    /* 32bit process, so check that we're on 64bit OS */
4607db96d56Sopenharmony_ci    BOOL f64 = FALSE;
4617db96d56Sopenharmony_ci    if (IsWow64Process(GetCurrentProcess(), &f64) && f64) {
4627db96d56Sopenharmony_ci        _locate_pythons_for_key(HKEY_LOCAL_MACHINE, LOOKASIDE_PATH,
4637db96d56Sopenharmony_ci                                KEY_READ | KEY_WOW64_64KEY, 64, TRUE);
4647db96d56Sopenharmony_ci    }
4657db96d56Sopenharmony_ci#endif
4667db96d56Sopenharmony_ci}
4677db96d56Sopenharmony_ci
4687db96d56Sopenharmony_cistatic void
4697db96d56Sopenharmony_cilocate_venv_python()
4707db96d56Sopenharmony_ci{
4717db96d56Sopenharmony_ci    static wchar_t venv_python[MAX_PATH];
4727db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip;
4737db96d56Sopenharmony_ci    wchar_t *virtual_env = get_env(L"VIRTUAL_ENV");
4747db96d56Sopenharmony_ci    DWORD attrs;
4757db96d56Sopenharmony_ci
4767db96d56Sopenharmony_ci    /* Check for VIRTUAL_ENV environment variable */
4777db96d56Sopenharmony_ci    if (virtual_env == NULL || virtual_env[0] == L'\0') {
4787db96d56Sopenharmony_ci        return;
4797db96d56Sopenharmony_ci    }
4807db96d56Sopenharmony_ci
4817db96d56Sopenharmony_ci    /* Check for a python executable in the venv */
4827db96d56Sopenharmony_ci    debug(L"Checking for Python executable in virtual env '%ls'\n", virtual_env);
4837db96d56Sopenharmony_ci    _snwprintf_s(venv_python, MAX_PATH, _TRUNCATE,
4847db96d56Sopenharmony_ci            L"%ls\\Scripts\\%ls", virtual_env, PYTHON_EXECUTABLE);
4857db96d56Sopenharmony_ci    attrs = GetFileAttributesW(venv_python);
4867db96d56Sopenharmony_ci    if (attrs == INVALID_FILE_ATTRIBUTES) {
4877db96d56Sopenharmony_ci        debug(L"Python executable %ls missing from virtual env\n", venv_python);
4887db96d56Sopenharmony_ci        return;
4897db96d56Sopenharmony_ci    }
4907db96d56Sopenharmony_ci
4917db96d56Sopenharmony_ci    ip = &installed_pythons[num_installed_pythons++];
4927db96d56Sopenharmony_ci    wcscpy_s(ip->executable, MAX_PATH, venv_python);
4937db96d56Sopenharmony_ci    ip->bits = 0;
4947db96d56Sopenharmony_ci    wcscpy_s(ip->version, MAX_VERSION_SIZE, L"venv");
4957db96d56Sopenharmony_ci}
4967db96d56Sopenharmony_ci
4977db96d56Sopenharmony_cistatic void
4987db96d56Sopenharmony_cilocate_all_pythons()
4997db96d56Sopenharmony_ci{
5007db96d56Sopenharmony_ci    /* venv Python is highest priority */
5017db96d56Sopenharmony_ci    locate_venv_python();
5027db96d56Sopenharmony_ci#if defined(_M_X64)
5037db96d56Sopenharmony_ci    /* If we are a 64bit process, first hit the 32bit keys. */
5047db96d56Sopenharmony_ci    debug(L"locating Pythons in 32bit registry\n");
5057db96d56Sopenharmony_ci    locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_32KEY);
5067db96d56Sopenharmony_ci    locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_32KEY);
5077db96d56Sopenharmony_ci#else
5087db96d56Sopenharmony_ci    /* If we are a 32bit process on a 64bit Windows, first hit the 64bit keys.*/
5097db96d56Sopenharmony_ci    BOOL f64 = FALSE;
5107db96d56Sopenharmony_ci    if (IsWow64Process(GetCurrentProcess(), &f64) && f64) {
5117db96d56Sopenharmony_ci        debug(L"locating Pythons in 64bit registry\n");
5127db96d56Sopenharmony_ci        locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_64KEY);
5137db96d56Sopenharmony_ci        locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_64KEY);
5147db96d56Sopenharmony_ci    }
5157db96d56Sopenharmony_ci#endif
5167db96d56Sopenharmony_ci    /* now hit the "native" key for this process bittedness. */
5177db96d56Sopenharmony_ci    debug(L"locating Pythons in native registry\n");
5187db96d56Sopenharmony_ci    locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ);
5197db96d56Sopenharmony_ci    locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ);
5207db96d56Sopenharmony_ci    /* Store-installed Python is lowest priority */
5217db96d56Sopenharmony_ci    locate_store_pythons();
5227db96d56Sopenharmony_ci    qsort(installed_pythons, num_installed_pythons, sizeof(INSTALLED_PYTHON),
5237db96d56Sopenharmony_ci          compare_pythons);
5247db96d56Sopenharmony_ci}
5257db96d56Sopenharmony_ci
5267db96d56Sopenharmony_cistatic INSTALLED_PYTHON *
5277db96d56Sopenharmony_cifind_python_by_version(wchar_t const * wanted_ver)
5287db96d56Sopenharmony_ci{
5297db96d56Sopenharmony_ci    INSTALLED_PYTHON * result = NULL;
5307db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip = installed_pythons;
5317db96d56Sopenharmony_ci    size_t i, n;
5327db96d56Sopenharmony_ci    size_t wlen = wcslen(wanted_ver);
5337db96d56Sopenharmony_ci    int bits = 0;
5347db96d56Sopenharmony_ci
5357db96d56Sopenharmony_ci    if (wcsstr(wanted_ver, L"-32")) {
5367db96d56Sopenharmony_ci        bits = 32;
5377db96d56Sopenharmony_ci        wlen -= wcslen(L"-32");
5387db96d56Sopenharmony_ci    }
5397db96d56Sopenharmony_ci    else if (wcsstr(wanted_ver, L"-64")) { /* Added option to select 64 bit explicitly */
5407db96d56Sopenharmony_ci        bits = 64;
5417db96d56Sopenharmony_ci        wlen -= wcslen(L"-64");
5427db96d56Sopenharmony_ci    }
5437db96d56Sopenharmony_ci    for (i = 0; i < num_installed_pythons; i++, ip++) {
5447db96d56Sopenharmony_ci        n = wcslen(ip->version);
5457db96d56Sopenharmony_ci        /*
5467db96d56Sopenharmony_ci         * If wlen is greater than 1, we're probably trying to find a specific
5477db96d56Sopenharmony_ci         * version and thus want an exact match: 3.1 != 3.10.  Otherwise, we
5487db96d56Sopenharmony_ci         * just want a prefix match.
5497db96d56Sopenharmony_ci         */
5507db96d56Sopenharmony_ci        if ((wlen > 1) && (n != wlen)) {
5517db96d56Sopenharmony_ci            continue;
5527db96d56Sopenharmony_ci        }
5537db96d56Sopenharmony_ci        if (n > wlen) {
5547db96d56Sopenharmony_ci            n = wlen;
5557db96d56Sopenharmony_ci        }
5567db96d56Sopenharmony_ci        if ((wcsncmp(ip->version, wanted_ver, n) == 0) &&
5577db96d56Sopenharmony_ci            /* bits == 0 => don't care */
5587db96d56Sopenharmony_ci            ((bits == 0) || (ip->bits == bits))) {
5597db96d56Sopenharmony_ci            result = ip;
5607db96d56Sopenharmony_ci            break;
5617db96d56Sopenharmony_ci        }
5627db96d56Sopenharmony_ci    }
5637db96d56Sopenharmony_ci    return result;
5647db96d56Sopenharmony_ci}
5657db96d56Sopenharmony_ci
5667db96d56Sopenharmony_ci
5677db96d56Sopenharmony_cistatic wchar_t appdata_ini_path[MAX_PATH];
5687db96d56Sopenharmony_cistatic wchar_t launcher_ini_path[MAX_PATH];
5697db96d56Sopenharmony_ci
5707db96d56Sopenharmony_ci/*
5717db96d56Sopenharmony_ci * Get a value either from the environment or a configuration file.
5727db96d56Sopenharmony_ci * The key passed in will either be "python", "python2" or "python3".
5737db96d56Sopenharmony_ci */
5747db96d56Sopenharmony_cistatic wchar_t *
5757db96d56Sopenharmony_ciget_configured_value(wchar_t * key)
5767db96d56Sopenharmony_ci{
5777db96d56Sopenharmony_ci/*
5787db96d56Sopenharmony_ci * Note: this static value is used to return a configured value
5797db96d56Sopenharmony_ci * obtained either from the environment or configuration file.
5807db96d56Sopenharmony_ci * This should be OK since there wouldn't be any concurrent calls.
5817db96d56Sopenharmony_ci */
5827db96d56Sopenharmony_ci    static wchar_t configured_value[MSGSIZE];
5837db96d56Sopenharmony_ci    wchar_t * result = NULL;
5847db96d56Sopenharmony_ci    wchar_t * found_in = L"environment";
5857db96d56Sopenharmony_ci    DWORD size;
5867db96d56Sopenharmony_ci
5877db96d56Sopenharmony_ci    /* First, search the environment. */
5887db96d56Sopenharmony_ci    _snwprintf_s(configured_value, MSGSIZE, _TRUNCATE, L"py_%ls", key);
5897db96d56Sopenharmony_ci    result = get_env(configured_value);
5907db96d56Sopenharmony_ci    if (result == NULL && appdata_ini_path[0]) {
5917db96d56Sopenharmony_ci        /* Not in environment: check local configuration. */
5927db96d56Sopenharmony_ci        size = GetPrivateProfileStringW(L"defaults", key, NULL,
5937db96d56Sopenharmony_ci                                        configured_value, MSGSIZE,
5947db96d56Sopenharmony_ci                                        appdata_ini_path);
5957db96d56Sopenharmony_ci        if (size > 0) {
5967db96d56Sopenharmony_ci            result = configured_value;
5977db96d56Sopenharmony_ci            found_in = appdata_ini_path;
5987db96d56Sopenharmony_ci        }
5997db96d56Sopenharmony_ci    }
6007db96d56Sopenharmony_ci    if (result == NULL && launcher_ini_path[0]) {
6017db96d56Sopenharmony_ci        /* Not in environment or local: check global configuration. */
6027db96d56Sopenharmony_ci        size = GetPrivateProfileStringW(L"defaults", key, NULL,
6037db96d56Sopenharmony_ci                                        configured_value, MSGSIZE,
6047db96d56Sopenharmony_ci                                        launcher_ini_path);
6057db96d56Sopenharmony_ci        if (size > 0) {
6067db96d56Sopenharmony_ci            result = configured_value;
6077db96d56Sopenharmony_ci            found_in = launcher_ini_path;
6087db96d56Sopenharmony_ci        }
6097db96d56Sopenharmony_ci    }
6107db96d56Sopenharmony_ci    if (result) {
6117db96d56Sopenharmony_ci        debug(L"found configured value '%ls=%ls' in %ls\n",
6127db96d56Sopenharmony_ci              key, result, found_in ? found_in : L"(unknown)");
6137db96d56Sopenharmony_ci    } else {
6147db96d56Sopenharmony_ci        debug(L"found no configured value for '%ls'\n", key);
6157db96d56Sopenharmony_ci    }
6167db96d56Sopenharmony_ci    return result;
6177db96d56Sopenharmony_ci}
6187db96d56Sopenharmony_ci
6197db96d56Sopenharmony_cistatic INSTALLED_PYTHON *
6207db96d56Sopenharmony_cilocate_python(wchar_t * wanted_ver, BOOL from_shebang)
6217db96d56Sopenharmony_ci{
6227db96d56Sopenharmony_ci    static wchar_t config_key [] = { L"pythonX" };
6237db96d56Sopenharmony_ci    static wchar_t * last_char = &config_key[sizeof(config_key) /
6247db96d56Sopenharmony_ci                                             sizeof(wchar_t) - 2];
6257db96d56Sopenharmony_ci    INSTALLED_PYTHON * result = NULL;
6267db96d56Sopenharmony_ci    size_t n = wcslen(wanted_ver);
6277db96d56Sopenharmony_ci    wchar_t * configured_value;
6287db96d56Sopenharmony_ci
6297db96d56Sopenharmony_ci    if (num_installed_pythons == 0)
6307db96d56Sopenharmony_ci        locate_all_pythons();
6317db96d56Sopenharmony_ci
6327db96d56Sopenharmony_ci    if (n == 1) {   /* just major version specified */
6337db96d56Sopenharmony_ci        *last_char = *wanted_ver;
6347db96d56Sopenharmony_ci        configured_value = get_configured_value(config_key);
6357db96d56Sopenharmony_ci        if (configured_value != NULL)
6367db96d56Sopenharmony_ci            wanted_ver = configured_value;
6377db96d56Sopenharmony_ci    }
6387db96d56Sopenharmony_ci    if (*wanted_ver) {
6397db96d56Sopenharmony_ci        result = find_python_by_version(wanted_ver);
6407db96d56Sopenharmony_ci        debug(L"search for Python version '%ls' found ", wanted_ver);
6417db96d56Sopenharmony_ci        if (result) {
6427db96d56Sopenharmony_ci            debug(L"'%ls'\n", result->executable);
6437db96d56Sopenharmony_ci        } else {
6447db96d56Sopenharmony_ci            debug(L"no interpreter\n");
6457db96d56Sopenharmony_ci        }
6467db96d56Sopenharmony_ci    }
6477db96d56Sopenharmony_ci    else {
6487db96d56Sopenharmony_ci        *last_char = L'\0'; /* look for an overall default */
6497db96d56Sopenharmony_ci        result = find_python_by_version(L"venv");
6507db96d56Sopenharmony_ci        if (result == NULL) {
6517db96d56Sopenharmony_ci            configured_value = get_configured_value(config_key);
6527db96d56Sopenharmony_ci            if (configured_value)
6537db96d56Sopenharmony_ci                result = find_python_by_version(configured_value);
6547db96d56Sopenharmony_ci        }
6557db96d56Sopenharmony_ci        /* Not found a value yet - try by major version.
6567db96d56Sopenharmony_ci         * If we're looking for an interpreter specified in a shebang line,
6577db96d56Sopenharmony_ci         * we want to try Python 2 first, then Python 3 (for Unix and backward
6587db96d56Sopenharmony_ci         * compatibility). If we're being called interactively, assume the user
6597db96d56Sopenharmony_ci         * wants the latest version available, so try Python 3 first, then
6607db96d56Sopenharmony_ci         * Python 2.
6617db96d56Sopenharmony_ci         */
6627db96d56Sopenharmony_ci        if (result == NULL)
6637db96d56Sopenharmony_ci            result = find_python_by_version(from_shebang ? L"2" : L"3");
6647db96d56Sopenharmony_ci        if (result == NULL)
6657db96d56Sopenharmony_ci            result = find_python_by_version(from_shebang ? L"3" : L"2");
6667db96d56Sopenharmony_ci        debug(L"search for default Python found ");
6677db96d56Sopenharmony_ci        if (result) {
6687db96d56Sopenharmony_ci            debug(L"version %ls at '%ls'\n",
6697db96d56Sopenharmony_ci                  result->version, result->executable);
6707db96d56Sopenharmony_ci        } else {
6717db96d56Sopenharmony_ci            debug(L"no interpreter\n");
6727db96d56Sopenharmony_ci        }
6737db96d56Sopenharmony_ci    }
6747db96d56Sopenharmony_ci    return result;
6757db96d56Sopenharmony_ci}
6767db96d56Sopenharmony_ci
6777db96d56Sopenharmony_ci#if defined(SCRIPT_WRAPPER)
6787db96d56Sopenharmony_ci/*
6797db96d56Sopenharmony_ci * Check for a script located alongside the executable
6807db96d56Sopenharmony_ci */
6817db96d56Sopenharmony_ci
6827db96d56Sopenharmony_ci#if defined(_WINDOWS)
6837db96d56Sopenharmony_ci#define SCRIPT_SUFFIX L"-script.pyw"
6847db96d56Sopenharmony_ci#else
6857db96d56Sopenharmony_ci#define SCRIPT_SUFFIX L"-script.py"
6867db96d56Sopenharmony_ci#endif
6877db96d56Sopenharmony_ci
6887db96d56Sopenharmony_cistatic wchar_t wrapped_script_path[MAX_PATH];
6897db96d56Sopenharmony_ci
6907db96d56Sopenharmony_ci/* Locate the script being wrapped.
6917db96d56Sopenharmony_ci *
6927db96d56Sopenharmony_ci * This code should store the name of the wrapped script in
6937db96d56Sopenharmony_ci * wrapped_script_path, or terminate the program with an error if there is no
6947db96d56Sopenharmony_ci * valid wrapped script file.
6957db96d56Sopenharmony_ci */
6967db96d56Sopenharmony_cistatic void
6977db96d56Sopenharmony_cilocate_wrapped_script()
6987db96d56Sopenharmony_ci{
6997db96d56Sopenharmony_ci    wchar_t * p;
7007db96d56Sopenharmony_ci    size_t plen;
7017db96d56Sopenharmony_ci    DWORD attrs;
7027db96d56Sopenharmony_ci
7037db96d56Sopenharmony_ci    plen = GetModuleFileNameW(NULL, wrapped_script_path, MAX_PATH);
7047db96d56Sopenharmony_ci    p = wcsrchr(wrapped_script_path, L'.');
7057db96d56Sopenharmony_ci    if (p == NULL) {
7067db96d56Sopenharmony_ci        debug(L"GetModuleFileNameW returned value has no extension: %ls\n",
7077db96d56Sopenharmony_ci              wrapped_script_path);
7087db96d56Sopenharmony_ci        error(RC_NO_SCRIPT, L"Wrapper name '%ls' is not valid.", wrapped_script_path);
7097db96d56Sopenharmony_ci    }
7107db96d56Sopenharmony_ci
7117db96d56Sopenharmony_ci    wcsncpy_s(p, MAX_PATH - (p - wrapped_script_path) + 1, SCRIPT_SUFFIX, _TRUNCATE);
7127db96d56Sopenharmony_ci    attrs = GetFileAttributesW(wrapped_script_path);
7137db96d56Sopenharmony_ci    if (attrs == INVALID_FILE_ATTRIBUTES) {
7147db96d56Sopenharmony_ci        debug(L"File '%ls' non-existent\n", wrapped_script_path);
7157db96d56Sopenharmony_ci        error(RC_NO_SCRIPT, L"Script file '%ls' is not present.", wrapped_script_path);
7167db96d56Sopenharmony_ci    }
7177db96d56Sopenharmony_ci
7187db96d56Sopenharmony_ci    debug(L"Using wrapped script file '%ls'\n", wrapped_script_path);
7197db96d56Sopenharmony_ci}
7207db96d56Sopenharmony_ci#endif
7217db96d56Sopenharmony_ci
7227db96d56Sopenharmony_ci/*
7237db96d56Sopenharmony_ci * Process creation code
7247db96d56Sopenharmony_ci */
7257db96d56Sopenharmony_ci
7267db96d56Sopenharmony_cistatic BOOL
7277db96d56Sopenharmony_cisafe_duplicate_handle(HANDLE in, HANDLE * pout)
7287db96d56Sopenharmony_ci{
7297db96d56Sopenharmony_ci    BOOL ok;
7307db96d56Sopenharmony_ci    HANDLE process = GetCurrentProcess();
7317db96d56Sopenharmony_ci    DWORD rc;
7327db96d56Sopenharmony_ci
7337db96d56Sopenharmony_ci    *pout = NULL;
7347db96d56Sopenharmony_ci    ok = DuplicateHandle(process, in, process, pout, 0, TRUE,
7357db96d56Sopenharmony_ci                         DUPLICATE_SAME_ACCESS);
7367db96d56Sopenharmony_ci    if (!ok) {
7377db96d56Sopenharmony_ci        rc = GetLastError();
7387db96d56Sopenharmony_ci        if (rc == ERROR_INVALID_HANDLE) {
7397db96d56Sopenharmony_ci            debug(L"DuplicateHandle returned ERROR_INVALID_HANDLE\n");
7407db96d56Sopenharmony_ci            ok = TRUE;
7417db96d56Sopenharmony_ci        }
7427db96d56Sopenharmony_ci        else {
7437db96d56Sopenharmony_ci            debug(L"DuplicateHandle returned %d\n", rc);
7447db96d56Sopenharmony_ci        }
7457db96d56Sopenharmony_ci    }
7467db96d56Sopenharmony_ci    return ok;
7477db96d56Sopenharmony_ci}
7487db96d56Sopenharmony_ci
7497db96d56Sopenharmony_cistatic BOOL WINAPI
7507db96d56Sopenharmony_cictrl_c_handler(DWORD code)
7517db96d56Sopenharmony_ci{
7527db96d56Sopenharmony_ci    return TRUE;    /* We just ignore all control events. */
7537db96d56Sopenharmony_ci}
7547db96d56Sopenharmony_ci
7557db96d56Sopenharmony_cistatic void
7567db96d56Sopenharmony_cirun_child(wchar_t * cmdline)
7577db96d56Sopenharmony_ci{
7587db96d56Sopenharmony_ci    HANDLE job;
7597db96d56Sopenharmony_ci    JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
7607db96d56Sopenharmony_ci    DWORD rc;
7617db96d56Sopenharmony_ci    BOOL ok;
7627db96d56Sopenharmony_ci    STARTUPINFOW si;
7637db96d56Sopenharmony_ci    PROCESS_INFORMATION pi;
7647db96d56Sopenharmony_ci
7657db96d56Sopenharmony_ci#if defined(_WINDOWS)
7667db96d56Sopenharmony_ci    /*
7677db96d56Sopenharmony_ci    When explorer launches a Windows (GUI) application, it displays
7687db96d56Sopenharmony_ci    the "app starting" (the "pointer + hourglass") cursor for a number
7697db96d56Sopenharmony_ci    of seconds, or until the app does something UI-ish (eg, creating a
7707db96d56Sopenharmony_ci    window, or fetching a message).  As this launcher doesn't do this
7717db96d56Sopenharmony_ci    directly, that cursor remains even after the child process does these
7727db96d56Sopenharmony_ci    things.  We avoid that by doing a simple post+get message.
7737db96d56Sopenharmony_ci    See http://bugs.python.org/issue17290 and
7747db96d56Sopenharmony_ci    https://bitbucket.org/vinay.sajip/pylauncher/issue/20/busy-cursor-for-a-long-time-when-running
7757db96d56Sopenharmony_ci    */
7767db96d56Sopenharmony_ci    MSG msg;
7777db96d56Sopenharmony_ci
7787db96d56Sopenharmony_ci    PostMessage(0, 0, 0, 0);
7797db96d56Sopenharmony_ci    GetMessage(&msg, 0, 0, 0);
7807db96d56Sopenharmony_ci#endif
7817db96d56Sopenharmony_ci
7827db96d56Sopenharmony_ci    debug(L"run_child: about to run '%ls'\n", cmdline);
7837db96d56Sopenharmony_ci    job = CreateJobObject(NULL, NULL);
7847db96d56Sopenharmony_ci    ok = QueryInformationJobObject(job, JobObjectExtendedLimitInformation,
7857db96d56Sopenharmony_ci                                  &info, sizeof(info), &rc);
7867db96d56Sopenharmony_ci    if (!ok || (rc != sizeof(info)) || !job)
7877db96d56Sopenharmony_ci        error(RC_CREATE_PROCESS, L"Job information querying failed");
7887db96d56Sopenharmony_ci    info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
7897db96d56Sopenharmony_ci                                             JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
7907db96d56Sopenharmony_ci    ok = SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info,
7917db96d56Sopenharmony_ci                                 sizeof(info));
7927db96d56Sopenharmony_ci    if (!ok)
7937db96d56Sopenharmony_ci        error(RC_CREATE_PROCESS, L"Job information setting failed");
7947db96d56Sopenharmony_ci    memset(&si, 0, sizeof(si));
7957db96d56Sopenharmony_ci    GetStartupInfoW(&si);
7967db96d56Sopenharmony_ci    ok = safe_duplicate_handle(GetStdHandle(STD_INPUT_HANDLE), &si.hStdInput);
7977db96d56Sopenharmony_ci    if (!ok)
7987db96d56Sopenharmony_ci        error(RC_NO_STD_HANDLES, L"stdin duplication failed");
7997db96d56Sopenharmony_ci    ok = safe_duplicate_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si.hStdOutput);
8007db96d56Sopenharmony_ci    if (!ok)
8017db96d56Sopenharmony_ci        error(RC_NO_STD_HANDLES, L"stdout duplication failed");
8027db96d56Sopenharmony_ci    ok = safe_duplicate_handle(GetStdHandle(STD_ERROR_HANDLE), &si.hStdError);
8037db96d56Sopenharmony_ci    if (!ok)
8047db96d56Sopenharmony_ci        error(RC_NO_STD_HANDLES, L"stderr duplication failed");
8057db96d56Sopenharmony_ci
8067db96d56Sopenharmony_ci    ok = SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
8077db96d56Sopenharmony_ci    if (!ok)
8087db96d56Sopenharmony_ci        error(RC_CREATE_PROCESS, L"control handler setting failed");
8097db96d56Sopenharmony_ci
8107db96d56Sopenharmony_ci    si.dwFlags = STARTF_USESTDHANDLES;
8117db96d56Sopenharmony_ci    ok = CreateProcessW(NULL, cmdline, NULL, NULL, TRUE,
8127db96d56Sopenharmony_ci                        0, NULL, NULL, &si, &pi);
8137db96d56Sopenharmony_ci    if (!ok)
8147db96d56Sopenharmony_ci        error(RC_CREATE_PROCESS, L"Unable to create process using '%ls'", cmdline);
8157db96d56Sopenharmony_ci    AssignProcessToJobObject(job, pi.hProcess);
8167db96d56Sopenharmony_ci    CloseHandle(pi.hThread);
8177db96d56Sopenharmony_ci    WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE);
8187db96d56Sopenharmony_ci    ok = GetExitCodeProcess(pi.hProcess, &rc);
8197db96d56Sopenharmony_ci    if (!ok)
8207db96d56Sopenharmony_ci        error(RC_CREATE_PROCESS, L"Failed to get exit code of process");
8217db96d56Sopenharmony_ci    debug(L"child process exit code: %d\n", rc);
8227db96d56Sopenharmony_ci    exit(rc);
8237db96d56Sopenharmony_ci}
8247db96d56Sopenharmony_ci
8257db96d56Sopenharmony_cistatic void
8267db96d56Sopenharmony_ciinvoke_child(wchar_t * executable, wchar_t * suffix, wchar_t * cmdline)
8277db96d56Sopenharmony_ci{
8287db96d56Sopenharmony_ci    wchar_t * child_command;
8297db96d56Sopenharmony_ci    size_t child_command_size;
8307db96d56Sopenharmony_ci    BOOL no_suffix = (suffix == NULL) || (*suffix == L'\0');
8317db96d56Sopenharmony_ci    BOOL no_cmdline = (*cmdline == L'\0');
8327db96d56Sopenharmony_ci
8337db96d56Sopenharmony_ci    if (no_suffix && no_cmdline)
8347db96d56Sopenharmony_ci        run_child(executable);
8357db96d56Sopenharmony_ci    else {
8367db96d56Sopenharmony_ci        if (no_suffix) {
8377db96d56Sopenharmony_ci            /* add 2 for space separator + terminating NUL. */
8387db96d56Sopenharmony_ci            child_command_size = wcslen(executable) + wcslen(cmdline) + 2;
8397db96d56Sopenharmony_ci        }
8407db96d56Sopenharmony_ci        else {
8417db96d56Sopenharmony_ci            /* add 3 for 2 space separators + terminating NUL. */
8427db96d56Sopenharmony_ci            child_command_size = wcslen(executable) + wcslen(suffix) +
8437db96d56Sopenharmony_ci                                    wcslen(cmdline) + 3;
8447db96d56Sopenharmony_ci        }
8457db96d56Sopenharmony_ci        child_command = calloc(child_command_size, sizeof(wchar_t));
8467db96d56Sopenharmony_ci        if (child_command == NULL)
8477db96d56Sopenharmony_ci            error(RC_CREATE_PROCESS, L"unable to allocate %zd bytes for child command.",
8487db96d56Sopenharmony_ci                  child_command_size);
8497db96d56Sopenharmony_ci        if (no_suffix)
8507db96d56Sopenharmony_ci            _snwprintf_s(child_command, child_command_size,
8517db96d56Sopenharmony_ci                         child_command_size - 1, L"%ls %ls",
8527db96d56Sopenharmony_ci                         executable, cmdline);
8537db96d56Sopenharmony_ci        else
8547db96d56Sopenharmony_ci            _snwprintf_s(child_command, child_command_size,
8557db96d56Sopenharmony_ci                         child_command_size - 1, L"%ls %ls %ls",
8567db96d56Sopenharmony_ci                         executable, suffix, cmdline);
8577db96d56Sopenharmony_ci        run_child(child_command);
8587db96d56Sopenharmony_ci        free(child_command);
8597db96d56Sopenharmony_ci    }
8607db96d56Sopenharmony_ci}
8617db96d56Sopenharmony_ci
8627db96d56Sopenharmony_citypedef struct {
8637db96d56Sopenharmony_ci    wchar_t *shebang;
8647db96d56Sopenharmony_ci    BOOL search;
8657db96d56Sopenharmony_ci} SHEBANG;
8667db96d56Sopenharmony_ci
8677db96d56Sopenharmony_cistatic SHEBANG builtin_virtual_paths [] = {
8687db96d56Sopenharmony_ci    { L"/usr/bin/env python", TRUE },
8697db96d56Sopenharmony_ci    { L"/usr/bin/python", FALSE },
8707db96d56Sopenharmony_ci    { L"/usr/local/bin/python", FALSE },
8717db96d56Sopenharmony_ci    { L"python", FALSE },
8727db96d56Sopenharmony_ci    { NULL, FALSE },
8737db96d56Sopenharmony_ci};
8747db96d56Sopenharmony_ci
8757db96d56Sopenharmony_ci/* For now, a static array of commands. */
8767db96d56Sopenharmony_ci
8777db96d56Sopenharmony_ci#define MAX_COMMANDS 100
8787db96d56Sopenharmony_ci
8797db96d56Sopenharmony_citypedef struct {
8807db96d56Sopenharmony_ci    wchar_t key[MAX_PATH];
8817db96d56Sopenharmony_ci    wchar_t value[MSGSIZE];
8827db96d56Sopenharmony_ci} COMMAND;
8837db96d56Sopenharmony_ci
8847db96d56Sopenharmony_cistatic COMMAND commands[MAX_COMMANDS];
8857db96d56Sopenharmony_cistatic int num_commands = 0;
8867db96d56Sopenharmony_ci
8877db96d56Sopenharmony_ci#if defined(SKIP_PREFIX)
8887db96d56Sopenharmony_ci
8897db96d56Sopenharmony_cistatic wchar_t * builtin_prefixes [] = {
8907db96d56Sopenharmony_ci    /* These must be in an order that the longest matches should be found,
8917db96d56Sopenharmony_ci     * i.e. if the prefix is "/usr/bin/env ", it should match that entry
8927db96d56Sopenharmony_ci     * *before* matching "/usr/bin/".
8937db96d56Sopenharmony_ci     */
8947db96d56Sopenharmony_ci    L"/usr/bin/env ",
8957db96d56Sopenharmony_ci    L"/usr/bin/",
8967db96d56Sopenharmony_ci    L"/usr/local/bin/",
8977db96d56Sopenharmony_ci    NULL
8987db96d56Sopenharmony_ci};
8997db96d56Sopenharmony_ci
9007db96d56Sopenharmony_cistatic wchar_t * skip_prefix(wchar_t * name)
9017db96d56Sopenharmony_ci{
9027db96d56Sopenharmony_ci    wchar_t ** pp = builtin_prefixes;
9037db96d56Sopenharmony_ci    wchar_t * result = name;
9047db96d56Sopenharmony_ci    wchar_t * p;
9057db96d56Sopenharmony_ci    size_t n;
9067db96d56Sopenharmony_ci
9077db96d56Sopenharmony_ci    for (; p = *pp; pp++) {
9087db96d56Sopenharmony_ci        n = wcslen(p);
9097db96d56Sopenharmony_ci        if (_wcsnicmp(p, name, n) == 0) {
9107db96d56Sopenharmony_ci            result += n;   /* skip the prefix */
9117db96d56Sopenharmony_ci            if (p[n - 1] == L' ') /* No empty strings in table, so n > 1 */
9127db96d56Sopenharmony_ci                result = skip_whitespace(result);
9137db96d56Sopenharmony_ci            break;
9147db96d56Sopenharmony_ci        }
9157db96d56Sopenharmony_ci    }
9167db96d56Sopenharmony_ci    return result;
9177db96d56Sopenharmony_ci}
9187db96d56Sopenharmony_ci
9197db96d56Sopenharmony_ci#endif
9207db96d56Sopenharmony_ci
9217db96d56Sopenharmony_ci#if defined(SEARCH_PATH)
9227db96d56Sopenharmony_ci
9237db96d56Sopenharmony_cistatic COMMAND path_command;
9247db96d56Sopenharmony_ci
9257db96d56Sopenharmony_cistatic COMMAND * find_on_path(wchar_t * name)
9267db96d56Sopenharmony_ci{
9277db96d56Sopenharmony_ci    wchar_t * pathext;
9287db96d56Sopenharmony_ci    size_t    varsize;
9297db96d56Sopenharmony_ci    wchar_t * context = NULL;
9307db96d56Sopenharmony_ci    wchar_t * extension;
9317db96d56Sopenharmony_ci    COMMAND * result = NULL;
9327db96d56Sopenharmony_ci    DWORD     len;
9337db96d56Sopenharmony_ci    errno_t   rc;
9347db96d56Sopenharmony_ci
9357db96d56Sopenharmony_ci    wcscpy_s(path_command.key, MAX_PATH, name);
9367db96d56Sopenharmony_ci    if (wcschr(name, L'.') != NULL) {
9377db96d56Sopenharmony_ci        /* assume it has an extension. */
9387db96d56Sopenharmony_ci        len = SearchPathW(NULL, name, NULL, MSGSIZE, path_command.value, NULL);
9397db96d56Sopenharmony_ci        if (len) {
9407db96d56Sopenharmony_ci            result = &path_command;
9417db96d56Sopenharmony_ci        }
9427db96d56Sopenharmony_ci    }
9437db96d56Sopenharmony_ci    else {
9447db96d56Sopenharmony_ci        /* No extension - search using registered extensions. */
9457db96d56Sopenharmony_ci        rc = _wdupenv_s(&pathext, &varsize, L"PATHEXT");
9467db96d56Sopenharmony_ci        if (rc == 0) {
9477db96d56Sopenharmony_ci            extension = wcstok_s(pathext, L";", &context);
9487db96d56Sopenharmony_ci            while (extension) {
9497db96d56Sopenharmony_ci                len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL);
9507db96d56Sopenharmony_ci                if (len) {
9517db96d56Sopenharmony_ci                    result = &path_command;
9527db96d56Sopenharmony_ci                    break;
9537db96d56Sopenharmony_ci                }
9547db96d56Sopenharmony_ci                extension = wcstok_s(NULL, L";", &context);
9557db96d56Sopenharmony_ci            }
9567db96d56Sopenharmony_ci            free(pathext);
9577db96d56Sopenharmony_ci        }
9587db96d56Sopenharmony_ci    }
9597db96d56Sopenharmony_ci    return result;
9607db96d56Sopenharmony_ci}
9617db96d56Sopenharmony_ci
9627db96d56Sopenharmony_ci#endif
9637db96d56Sopenharmony_ci
9647db96d56Sopenharmony_cistatic COMMAND * find_command(wchar_t * name)
9657db96d56Sopenharmony_ci{
9667db96d56Sopenharmony_ci    COMMAND * result = NULL;
9677db96d56Sopenharmony_ci    COMMAND * cp = commands;
9687db96d56Sopenharmony_ci    int i;
9697db96d56Sopenharmony_ci
9707db96d56Sopenharmony_ci    for (i = 0; i < num_commands; i++, cp++) {
9717db96d56Sopenharmony_ci        if (_wcsicmp(cp->key, name) == 0) {
9727db96d56Sopenharmony_ci            result = cp;
9737db96d56Sopenharmony_ci            break;
9747db96d56Sopenharmony_ci        }
9757db96d56Sopenharmony_ci    }
9767db96d56Sopenharmony_ci#if defined(SEARCH_PATH)
9777db96d56Sopenharmony_ci    if (result == NULL)
9787db96d56Sopenharmony_ci        result = find_on_path(name);
9797db96d56Sopenharmony_ci#endif
9807db96d56Sopenharmony_ci    return result;
9817db96d56Sopenharmony_ci}
9827db96d56Sopenharmony_ci
9837db96d56Sopenharmony_cistatic void
9847db96d56Sopenharmony_ciupdate_command(COMMAND * cp, wchar_t * name, wchar_t * cmdline)
9857db96d56Sopenharmony_ci{
9867db96d56Sopenharmony_ci    wcsncpy_s(cp->key, MAX_PATH, name, _TRUNCATE);
9877db96d56Sopenharmony_ci    wcsncpy_s(cp->value, MSGSIZE, cmdline, _TRUNCATE);
9887db96d56Sopenharmony_ci}
9897db96d56Sopenharmony_ci
9907db96d56Sopenharmony_cistatic void
9917db96d56Sopenharmony_ciadd_command(wchar_t * name, wchar_t * cmdline)
9927db96d56Sopenharmony_ci{
9937db96d56Sopenharmony_ci    if (num_commands >= MAX_COMMANDS) {
9947db96d56Sopenharmony_ci        debug(L"can't add %ls = '%ls': no room\n", name, cmdline);
9957db96d56Sopenharmony_ci    }
9967db96d56Sopenharmony_ci    else {
9977db96d56Sopenharmony_ci        COMMAND * cp = &commands[num_commands++];
9987db96d56Sopenharmony_ci
9997db96d56Sopenharmony_ci        update_command(cp, name, cmdline);
10007db96d56Sopenharmony_ci    }
10017db96d56Sopenharmony_ci}
10027db96d56Sopenharmony_ci
10037db96d56Sopenharmony_cistatic void
10047db96d56Sopenharmony_ciread_config_file(wchar_t * config_path)
10057db96d56Sopenharmony_ci{
10067db96d56Sopenharmony_ci    wchar_t keynames[MSGSIZE];
10077db96d56Sopenharmony_ci    wchar_t value[MSGSIZE];
10087db96d56Sopenharmony_ci    DWORD read;
10097db96d56Sopenharmony_ci    wchar_t * key;
10107db96d56Sopenharmony_ci    COMMAND * cp;
10117db96d56Sopenharmony_ci    wchar_t * cmdp;
10127db96d56Sopenharmony_ci
10137db96d56Sopenharmony_ci    read = GetPrivateProfileStringW(L"commands", NULL, NULL, keynames, MSGSIZE,
10147db96d56Sopenharmony_ci                                    config_path);
10157db96d56Sopenharmony_ci    if (read == MSGSIZE - 1) {
10167db96d56Sopenharmony_ci        debug(L"read_commands: %ls: not enough space for names\n", config_path);
10177db96d56Sopenharmony_ci    }
10187db96d56Sopenharmony_ci    key = keynames;
10197db96d56Sopenharmony_ci    while (*key) {
10207db96d56Sopenharmony_ci        read = GetPrivateProfileStringW(L"commands", key, NULL, value, MSGSIZE,
10217db96d56Sopenharmony_ci                                       config_path);
10227db96d56Sopenharmony_ci        if (read == MSGSIZE - 1) {
10237db96d56Sopenharmony_ci            debug(L"read_commands: %ls: not enough space for %ls\n",
10247db96d56Sopenharmony_ci                  config_path, key);
10257db96d56Sopenharmony_ci        }
10267db96d56Sopenharmony_ci        cmdp = skip_whitespace(value);
10277db96d56Sopenharmony_ci        if (*cmdp) {
10287db96d56Sopenharmony_ci            cp = find_command(key);
10297db96d56Sopenharmony_ci            if (cp == NULL)
10307db96d56Sopenharmony_ci                add_command(key, value);
10317db96d56Sopenharmony_ci            else
10327db96d56Sopenharmony_ci                update_command(cp, key, value);
10337db96d56Sopenharmony_ci        }
10347db96d56Sopenharmony_ci        key += wcslen(key) + 1;
10357db96d56Sopenharmony_ci    }
10367db96d56Sopenharmony_ci}
10377db96d56Sopenharmony_ci
10387db96d56Sopenharmony_cistatic void read_commands()
10397db96d56Sopenharmony_ci{
10407db96d56Sopenharmony_ci    if (launcher_ini_path[0])
10417db96d56Sopenharmony_ci        read_config_file(launcher_ini_path);
10427db96d56Sopenharmony_ci    if (appdata_ini_path[0])
10437db96d56Sopenharmony_ci        read_config_file(appdata_ini_path);
10447db96d56Sopenharmony_ci}
10457db96d56Sopenharmony_ci
10467db96d56Sopenharmony_cistatic BOOL
10477db96d56Sopenharmony_ciparse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command,
10487db96d56Sopenharmony_ci              wchar_t ** suffix, BOOL *search)
10497db96d56Sopenharmony_ci{
10507db96d56Sopenharmony_ci    BOOL rc = FALSE;
10517db96d56Sopenharmony_ci    SHEBANG * vpp;
10527db96d56Sopenharmony_ci    size_t plen;
10537db96d56Sopenharmony_ci    wchar_t * p;
10547db96d56Sopenharmony_ci    wchar_t zapped;
10557db96d56Sopenharmony_ci    wchar_t * endp = shebang_line + nchars - 1;
10567db96d56Sopenharmony_ci    COMMAND * cp;
10577db96d56Sopenharmony_ci    wchar_t * skipped;
10587db96d56Sopenharmony_ci
10597db96d56Sopenharmony_ci    *command = NULL;    /* failure return */
10607db96d56Sopenharmony_ci    *suffix = NULL;
10617db96d56Sopenharmony_ci    *search = FALSE;
10627db96d56Sopenharmony_ci
10637db96d56Sopenharmony_ci    if ((*shebang_line++ == L'#') && (*shebang_line++ == L'!')) {
10647db96d56Sopenharmony_ci        shebang_line = skip_whitespace(shebang_line);
10657db96d56Sopenharmony_ci        if (*shebang_line) {
10667db96d56Sopenharmony_ci            *command = shebang_line;
10677db96d56Sopenharmony_ci            for (vpp = builtin_virtual_paths; vpp->shebang; ++vpp) {
10687db96d56Sopenharmony_ci                plen = wcslen(vpp->shebang);
10697db96d56Sopenharmony_ci                if (wcsncmp(shebang_line, vpp->shebang, plen) == 0) {
10707db96d56Sopenharmony_ci                    rc = TRUE;
10717db96d56Sopenharmony_ci                    *search = vpp->search;
10727db96d56Sopenharmony_ci                    /* We can do this because all builtin commands contain
10737db96d56Sopenharmony_ci                     * "python".
10747db96d56Sopenharmony_ci                     */
10757db96d56Sopenharmony_ci                    *command = wcsstr(shebang_line, L"python");
10767db96d56Sopenharmony_ci                    break;
10777db96d56Sopenharmony_ci                }
10787db96d56Sopenharmony_ci            }
10797db96d56Sopenharmony_ci            if (vpp->shebang == NULL) {
10807db96d56Sopenharmony_ci                /*
10817db96d56Sopenharmony_ci                 * Not found in builtins - look in customized commands.
10827db96d56Sopenharmony_ci                 *
10837db96d56Sopenharmony_ci                 * We can't permanently modify the shebang line in case
10847db96d56Sopenharmony_ci                 * it's not a customized command, but we can temporarily
10857db96d56Sopenharmony_ci                 * stick a NUL after the command while searching for it,
10867db96d56Sopenharmony_ci                 * then put back the char we zapped.
10877db96d56Sopenharmony_ci                 */
10887db96d56Sopenharmony_ci#if defined(SKIP_PREFIX)
10897db96d56Sopenharmony_ci                skipped = skip_prefix(shebang_line);
10907db96d56Sopenharmony_ci#else
10917db96d56Sopenharmony_ci                skipped = shebang_line;
10927db96d56Sopenharmony_ci#endif
10937db96d56Sopenharmony_ci                p = wcspbrk(skipped, L" \t\r\n");
10947db96d56Sopenharmony_ci                if (p != NULL) {
10957db96d56Sopenharmony_ci                    zapped = *p;
10967db96d56Sopenharmony_ci                    *p = L'\0';
10977db96d56Sopenharmony_ci                }
10987db96d56Sopenharmony_ci                cp = find_command(skipped);
10997db96d56Sopenharmony_ci                if (p != NULL)
11007db96d56Sopenharmony_ci                    *p = zapped;
11017db96d56Sopenharmony_ci                if (cp != NULL) {
11027db96d56Sopenharmony_ci                    *command = cp->value;
11037db96d56Sopenharmony_ci                    if (p != NULL)
11047db96d56Sopenharmony_ci                        *suffix = skip_whitespace(p);
11057db96d56Sopenharmony_ci                }
11067db96d56Sopenharmony_ci            }
11077db96d56Sopenharmony_ci            /* remove trailing whitespace */
11087db96d56Sopenharmony_ci            while ((endp > shebang_line) && isspace(*endp))
11097db96d56Sopenharmony_ci                --endp;
11107db96d56Sopenharmony_ci            if (endp > shebang_line)
11117db96d56Sopenharmony_ci                endp[1] = L'\0';
11127db96d56Sopenharmony_ci        }
11137db96d56Sopenharmony_ci    }
11147db96d56Sopenharmony_ci    return rc;
11157db96d56Sopenharmony_ci}
11167db96d56Sopenharmony_ci
11177db96d56Sopenharmony_ci/* #define CP_UTF8             65001 defined in winnls.h */
11187db96d56Sopenharmony_ci#define CP_UTF16LE          1200
11197db96d56Sopenharmony_ci#define CP_UTF16BE          1201
11207db96d56Sopenharmony_ci#define CP_UTF32LE          12000
11217db96d56Sopenharmony_ci#define CP_UTF32BE          12001
11227db96d56Sopenharmony_ci
11237db96d56Sopenharmony_citypedef struct {
11247db96d56Sopenharmony_ci    int length;
11257db96d56Sopenharmony_ci    char sequence[4];
11267db96d56Sopenharmony_ci    UINT code_page;
11277db96d56Sopenharmony_ci} BOM;
11287db96d56Sopenharmony_ci
11297db96d56Sopenharmony_ci/*
11307db96d56Sopenharmony_ci * Strictly, we don't need to handle UTF-16 and UTF-32, since Python itself
11317db96d56Sopenharmony_ci * doesn't. Never mind, one day it might - there's no harm leaving it in.
11327db96d56Sopenharmony_ci */
11337db96d56Sopenharmony_cistatic BOM BOMs[] = {
11347db96d56Sopenharmony_ci    { 3, { 0xEF, 0xBB, 0xBF }, CP_UTF8 },           /* UTF-8 - keep first */
11357db96d56Sopenharmony_ci    /* Test UTF-32LE before UTF-16LE since UTF-16LE BOM is a prefix
11367db96d56Sopenharmony_ci     * of UTF-32LE BOM. */
11377db96d56Sopenharmony_ci    { 4, { 0xFF, 0xFE, 0x00, 0x00 }, CP_UTF32LE },  /* UTF-32LE */
11387db96d56Sopenharmony_ci    { 4, { 0x00, 0x00, 0xFE, 0xFF }, CP_UTF32BE },  /* UTF-32BE */
11397db96d56Sopenharmony_ci    { 2, { 0xFF, 0xFE }, CP_UTF16LE },              /* UTF-16LE */
11407db96d56Sopenharmony_ci    { 2, { 0xFE, 0xFF }, CP_UTF16BE },              /* UTF-16BE */
11417db96d56Sopenharmony_ci    { 0 }                                           /* sentinel */
11427db96d56Sopenharmony_ci};
11437db96d56Sopenharmony_ci
11447db96d56Sopenharmony_cistatic BOM *
11457db96d56Sopenharmony_cifind_BOM(char * buffer)
11467db96d56Sopenharmony_ci{
11477db96d56Sopenharmony_ci/*
11487db96d56Sopenharmony_ci * Look for a BOM in the input and return a pointer to the
11497db96d56Sopenharmony_ci * corresponding structure, or NULL if not found.
11507db96d56Sopenharmony_ci */
11517db96d56Sopenharmony_ci    BOM * result = NULL;
11527db96d56Sopenharmony_ci    BOM *bom;
11537db96d56Sopenharmony_ci
11547db96d56Sopenharmony_ci    for (bom = BOMs; bom->length; bom++) {
11557db96d56Sopenharmony_ci        if (strncmp(bom->sequence, buffer, bom->length) == 0) {
11567db96d56Sopenharmony_ci            result = bom;
11577db96d56Sopenharmony_ci            break;
11587db96d56Sopenharmony_ci        }
11597db96d56Sopenharmony_ci    }
11607db96d56Sopenharmony_ci    return result;
11617db96d56Sopenharmony_ci}
11627db96d56Sopenharmony_ci
11637db96d56Sopenharmony_cistatic char *
11647db96d56Sopenharmony_cifind_terminator(char * buffer, int len, BOM *bom)
11657db96d56Sopenharmony_ci{
11667db96d56Sopenharmony_ci    char * result = NULL;
11677db96d56Sopenharmony_ci    char * end = buffer + len;
11687db96d56Sopenharmony_ci    char  * p;
11697db96d56Sopenharmony_ci    char c;
11707db96d56Sopenharmony_ci    int cp;
11717db96d56Sopenharmony_ci
11727db96d56Sopenharmony_ci    for (p = buffer; p < end; p++) {
11737db96d56Sopenharmony_ci        c = *p;
11747db96d56Sopenharmony_ci        if (c == '\r') {
11757db96d56Sopenharmony_ci            result = p;
11767db96d56Sopenharmony_ci            break;
11777db96d56Sopenharmony_ci        }
11787db96d56Sopenharmony_ci        if (c == '\n') {
11797db96d56Sopenharmony_ci            result = p;
11807db96d56Sopenharmony_ci            break;
11817db96d56Sopenharmony_ci        }
11827db96d56Sopenharmony_ci    }
11837db96d56Sopenharmony_ci    if (result != NULL) {
11847db96d56Sopenharmony_ci        cp = bom->code_page;
11857db96d56Sopenharmony_ci
11867db96d56Sopenharmony_ci        /* adjustments to include all bytes of the char */
11877db96d56Sopenharmony_ci        /* no adjustment needed for UTF-8 or big endian */
11887db96d56Sopenharmony_ci        if (cp == CP_UTF16LE)
11897db96d56Sopenharmony_ci            ++result;
11907db96d56Sopenharmony_ci        else if (cp == CP_UTF32LE)
11917db96d56Sopenharmony_ci            result += 3;
11927db96d56Sopenharmony_ci        ++result; /* point just past terminator */
11937db96d56Sopenharmony_ci    }
11947db96d56Sopenharmony_ci    return result;
11957db96d56Sopenharmony_ci}
11967db96d56Sopenharmony_ci
11977db96d56Sopenharmony_cistatic BOOL
11987db96d56Sopenharmony_civalidate_version(wchar_t * p)
11997db96d56Sopenharmony_ci{
12007db96d56Sopenharmony_ci    /*
12017db96d56Sopenharmony_ci    Version information should start with the major version,
12027db96d56Sopenharmony_ci    Optionally followed by a period and a minor version,
12037db96d56Sopenharmony_ci    Optionally followed by a minus and one of 32 or 64.
12047db96d56Sopenharmony_ci    Valid examples:
12057db96d56Sopenharmony_ci      2
12067db96d56Sopenharmony_ci      3
12077db96d56Sopenharmony_ci      2.7
12087db96d56Sopenharmony_ci      3.6
12097db96d56Sopenharmony_ci      2.7-32
12107db96d56Sopenharmony_ci      The intent is to add to the valid patterns:
12117db96d56Sopenharmony_ci      3.10
12127db96d56Sopenharmony_ci      3-32
12137db96d56Sopenharmony_ci      3.6-64
12147db96d56Sopenharmony_ci      3-64
12157db96d56Sopenharmony_ci    */
12167db96d56Sopenharmony_ci    BOOL result = (p != NULL); /* Default to False if null pointer. */
12177db96d56Sopenharmony_ci
12187db96d56Sopenharmony_ci    result = result && iswdigit(*p);  /* Result = False if first string element is not a digit. */
12197db96d56Sopenharmony_ci
12207db96d56Sopenharmony_ci    while (result && iswdigit(*p))   /* Require a major version */
12217db96d56Sopenharmony_ci        ++p;  /* Skip all leading digit(s) */
12227db96d56Sopenharmony_ci    if (result && (*p == L'.'))     /* Allow . for major minor separator.*/
12237db96d56Sopenharmony_ci    {
12247db96d56Sopenharmony_ci        result = iswdigit(*++p);     /* Must be at least one digit */
12257db96d56Sopenharmony_ci        while (result && iswdigit(*++p)) ; /* Skip any more Digits */
12267db96d56Sopenharmony_ci    }
12277db96d56Sopenharmony_ci    if (result && (*p == L'-')) {   /* Allow - for Bits Separator */
12287db96d56Sopenharmony_ci        switch(*++p){
12297db96d56Sopenharmony_ci        case L'3':                            /* 3 is OK */
12307db96d56Sopenharmony_ci            result = (*++p == L'2') && !*++p; /* only if followed by 2 and ended.*/
12317db96d56Sopenharmony_ci            break;
12327db96d56Sopenharmony_ci        case L'6':                            /* 6 is OK */
12337db96d56Sopenharmony_ci            result = (*++p == L'4') && !*++p; /* only if followed by 4 and ended.*/
12347db96d56Sopenharmony_ci            break;
12357db96d56Sopenharmony_ci        default:
12367db96d56Sopenharmony_ci            result = FALSE;
12377db96d56Sopenharmony_ci            break;
12387db96d56Sopenharmony_ci        }
12397db96d56Sopenharmony_ci    }
12407db96d56Sopenharmony_ci    result = result && !*p; /* Must have reached EOS */
12417db96d56Sopenharmony_ci    return result;
12427db96d56Sopenharmony_ci
12437db96d56Sopenharmony_ci}
12447db96d56Sopenharmony_ci
12457db96d56Sopenharmony_citypedef struct {
12467db96d56Sopenharmony_ci    unsigned short min;
12477db96d56Sopenharmony_ci    unsigned short max;
12487db96d56Sopenharmony_ci    wchar_t version[MAX_VERSION_SIZE];
12497db96d56Sopenharmony_ci} PYC_MAGIC;
12507db96d56Sopenharmony_ci
12517db96d56Sopenharmony_cistatic PYC_MAGIC magic_values[] = {
12527db96d56Sopenharmony_ci    { 50823, 50823, L"2.0" },
12537db96d56Sopenharmony_ci    { 60202, 60202, L"2.1" },
12547db96d56Sopenharmony_ci    { 60717, 60717, L"2.2" },
12557db96d56Sopenharmony_ci    { 62011, 62021, L"2.3" },
12567db96d56Sopenharmony_ci    { 62041, 62061, L"2.4" },
12577db96d56Sopenharmony_ci    { 62071, 62131, L"2.5" },
12587db96d56Sopenharmony_ci    { 62151, 62161, L"2.6" },
12597db96d56Sopenharmony_ci    { 62171, 62211, L"2.7" },
12607db96d56Sopenharmony_ci    { 3000, 3131, L"3.0" },
12617db96d56Sopenharmony_ci    { 3141, 3151, L"3.1" },
12627db96d56Sopenharmony_ci    { 3160, 3180, L"3.2" },
12637db96d56Sopenharmony_ci    { 3190, 3230, L"3.3" },
12647db96d56Sopenharmony_ci    { 3250, 3310, L"3.4" },
12657db96d56Sopenharmony_ci    { 3320, 3351, L"3.5" },
12667db96d56Sopenharmony_ci    { 3360, 3379, L"3.6" },
12677db96d56Sopenharmony_ci    { 3390, 3399, L"3.7" },
12687db96d56Sopenharmony_ci    { 3400, 3419, L"3.8" },
12697db96d56Sopenharmony_ci    { 3420, 3429, L"3.9" },
12707db96d56Sopenharmony_ci    { 3430, 3449, L"3.10" },
12717db96d56Sopenharmony_ci    /* Allow 50 magic numbers per version from here on */
12727db96d56Sopenharmony_ci    { 3450, 3499, L"3.11" },
12737db96d56Sopenharmony_ci    { 3500, 3549, L"3.12" },
12747db96d56Sopenharmony_ci    { 0 }
12757db96d56Sopenharmony_ci};
12767db96d56Sopenharmony_ci
12777db96d56Sopenharmony_cistatic INSTALLED_PYTHON *
12787db96d56Sopenharmony_cifind_by_magic(unsigned short magic)
12797db96d56Sopenharmony_ci{
12807db96d56Sopenharmony_ci    INSTALLED_PYTHON * result = NULL;
12817db96d56Sopenharmony_ci    PYC_MAGIC * mp;
12827db96d56Sopenharmony_ci
12837db96d56Sopenharmony_ci    for (mp = magic_values; mp->min; mp++) {
12847db96d56Sopenharmony_ci        if ((magic >= mp->min) && (magic <= mp->max)) {
12857db96d56Sopenharmony_ci            result = locate_python(mp->version, FALSE);
12867db96d56Sopenharmony_ci            if (result != NULL)
12877db96d56Sopenharmony_ci                break;
12887db96d56Sopenharmony_ci        }
12897db96d56Sopenharmony_ci    }
12907db96d56Sopenharmony_ci    return result;
12917db96d56Sopenharmony_ci}
12927db96d56Sopenharmony_ci
12937db96d56Sopenharmony_cistatic void
12947db96d56Sopenharmony_cimaybe_handle_shebang(wchar_t ** argv, wchar_t * cmdline)
12957db96d56Sopenharmony_ci{
12967db96d56Sopenharmony_ci/*
12977db96d56Sopenharmony_ci * Look for a shebang line in the first argument.  If found
12987db96d56Sopenharmony_ci * and we spawn a child process, this never returns.  If it
12997db96d56Sopenharmony_ci * does return then we process the args "normally".
13007db96d56Sopenharmony_ci *
13017db96d56Sopenharmony_ci * argv[0] might be a filename with a shebang.
13027db96d56Sopenharmony_ci */
13037db96d56Sopenharmony_ci    FILE * fp;
13047db96d56Sopenharmony_ci    errno_t rc = _wfopen_s(&fp, *argv, L"rb");
13057db96d56Sopenharmony_ci    char buffer[BUFSIZE];
13067db96d56Sopenharmony_ci    wchar_t shebang_line[BUFSIZE + 1];
13077db96d56Sopenharmony_ci    size_t read;
13087db96d56Sopenharmony_ci    char *p;
13097db96d56Sopenharmony_ci    char * start;
13107db96d56Sopenharmony_ci    char * shebang_alias = (char *) shebang_line;
13117db96d56Sopenharmony_ci    BOM* bom;
13127db96d56Sopenharmony_ci    int i, j, nchars = 0;
13137db96d56Sopenharmony_ci    int header_len;
13147db96d56Sopenharmony_ci    BOOL is_virt;
13157db96d56Sopenharmony_ci    BOOL search;
13167db96d56Sopenharmony_ci    wchar_t * command;
13177db96d56Sopenharmony_ci    wchar_t * suffix;
13187db96d56Sopenharmony_ci    COMMAND *cmd = NULL;
13197db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip;
13207db96d56Sopenharmony_ci
13217db96d56Sopenharmony_ci    if (rc == 0) {
13227db96d56Sopenharmony_ci        read = fread(buffer, sizeof(char), BUFSIZE, fp);
13237db96d56Sopenharmony_ci        debug(L"maybe_handle_shebang: read %zd bytes\n", read);
13247db96d56Sopenharmony_ci        fclose(fp);
13257db96d56Sopenharmony_ci
13267db96d56Sopenharmony_ci        if ((read >= 4) && (buffer[3] == '\n') && (buffer[2] == '\r')) {
13277db96d56Sopenharmony_ci            ip = find_by_magic((((unsigned char)buffer[1]) << 8 |
13287db96d56Sopenharmony_ci                                (unsigned char)buffer[0]) & 0xFFFF);
13297db96d56Sopenharmony_ci            if (ip != NULL) {
13307db96d56Sopenharmony_ci                debug(L"script file is compiled against Python %ls\n",
13317db96d56Sopenharmony_ci                      ip->version);
13327db96d56Sopenharmony_ci                invoke_child(ip->executable, NULL, cmdline);
13337db96d56Sopenharmony_ci            }
13347db96d56Sopenharmony_ci        }
13357db96d56Sopenharmony_ci        /* Look for BOM */
13367db96d56Sopenharmony_ci        bom = find_BOM(buffer);
13377db96d56Sopenharmony_ci        if (bom == NULL) {
13387db96d56Sopenharmony_ci            start = buffer;
13397db96d56Sopenharmony_ci            debug(L"maybe_handle_shebang: BOM not found, using UTF-8\n");
13407db96d56Sopenharmony_ci            bom = BOMs; /* points to UTF-8 entry - the default */
13417db96d56Sopenharmony_ci        }
13427db96d56Sopenharmony_ci        else {
13437db96d56Sopenharmony_ci            debug(L"maybe_handle_shebang: BOM found, code page %u\n",
13447db96d56Sopenharmony_ci                  bom->code_page);
13457db96d56Sopenharmony_ci            start = &buffer[bom->length];
13467db96d56Sopenharmony_ci        }
13477db96d56Sopenharmony_ci        p = find_terminator(start, BUFSIZE, bom);
13487db96d56Sopenharmony_ci        /*
13497db96d56Sopenharmony_ci         * If no CR or LF was found in the heading,
13507db96d56Sopenharmony_ci         * we assume it's not a shebang file.
13517db96d56Sopenharmony_ci         */
13527db96d56Sopenharmony_ci        if (p == NULL) {
13537db96d56Sopenharmony_ci            debug(L"maybe_handle_shebang: No line terminator found\n");
13547db96d56Sopenharmony_ci        }
13557db96d56Sopenharmony_ci        else {
13567db96d56Sopenharmony_ci            /*
13577db96d56Sopenharmony_ci             * Found line terminator - parse the shebang.
13587db96d56Sopenharmony_ci             *
13597db96d56Sopenharmony_ci             * Strictly, we don't need to handle UTF-16 anf UTF-32,
13607db96d56Sopenharmony_ci             * since Python itself doesn't.
13617db96d56Sopenharmony_ci             * Never mind, one day it might.
13627db96d56Sopenharmony_ci             */
13637db96d56Sopenharmony_ci            header_len = (int) (p - start);
13647db96d56Sopenharmony_ci            switch(bom->code_page) {
13657db96d56Sopenharmony_ci            case CP_UTF8:
13667db96d56Sopenharmony_ci                nchars = MultiByteToWideChar(bom->code_page,
13677db96d56Sopenharmony_ci                                             0,
13687db96d56Sopenharmony_ci                                             start, header_len, shebang_line,
13697db96d56Sopenharmony_ci                                             BUFSIZE);
13707db96d56Sopenharmony_ci                break;
13717db96d56Sopenharmony_ci            case CP_UTF16BE:
13727db96d56Sopenharmony_ci                if (header_len % 2 != 0) {
13737db96d56Sopenharmony_ci                    debug(L"maybe_handle_shebang: UTF-16BE, but an odd number \
13747db96d56Sopenharmony_ciof bytes: %d\n", header_len);
13757db96d56Sopenharmony_ci                    /* nchars = 0; Not needed - initialised to 0. */
13767db96d56Sopenharmony_ci                }
13777db96d56Sopenharmony_ci                else {
13787db96d56Sopenharmony_ci                    for (i = header_len; i > 0; i -= 2) {
13797db96d56Sopenharmony_ci                        shebang_alias[i - 1] = start[i - 2];
13807db96d56Sopenharmony_ci                        shebang_alias[i - 2] = start[i - 1];
13817db96d56Sopenharmony_ci                    }
13827db96d56Sopenharmony_ci                    nchars = header_len / sizeof(wchar_t);
13837db96d56Sopenharmony_ci                }
13847db96d56Sopenharmony_ci                break;
13857db96d56Sopenharmony_ci            case CP_UTF16LE:
13867db96d56Sopenharmony_ci                if ((header_len % 2) != 0) {
13877db96d56Sopenharmony_ci                    debug(L"UTF-16LE, but an odd number of bytes: %d\n",
13887db96d56Sopenharmony_ci                          header_len);
13897db96d56Sopenharmony_ci                    /* nchars = 0; Not needed - initialised to 0. */
13907db96d56Sopenharmony_ci                }
13917db96d56Sopenharmony_ci                else {
13927db96d56Sopenharmony_ci                    /* no actual conversion needed. */
13937db96d56Sopenharmony_ci                    memcpy(shebang_line, start, header_len);
13947db96d56Sopenharmony_ci                    nchars = header_len / sizeof(wchar_t);
13957db96d56Sopenharmony_ci                }
13967db96d56Sopenharmony_ci                break;
13977db96d56Sopenharmony_ci            case CP_UTF32BE:
13987db96d56Sopenharmony_ci                if (header_len % 4 != 0) {
13997db96d56Sopenharmony_ci                    debug(L"UTF-32BE, but not divisible by 4: %d\n",
14007db96d56Sopenharmony_ci                          header_len);
14017db96d56Sopenharmony_ci                    /* nchars = 0; Not needed - initialised to 0. */
14027db96d56Sopenharmony_ci                }
14037db96d56Sopenharmony_ci                else {
14047db96d56Sopenharmony_ci                    for (i = header_len, j = header_len / 2; i > 0; i -= 4,
14057db96d56Sopenharmony_ci                                                                    j -= 2) {
14067db96d56Sopenharmony_ci                        shebang_alias[j - 1] = start[i - 2];
14077db96d56Sopenharmony_ci                        shebang_alias[j - 2] = start[i - 1];
14087db96d56Sopenharmony_ci                    }
14097db96d56Sopenharmony_ci                    nchars = header_len / sizeof(wchar_t);
14107db96d56Sopenharmony_ci                }
14117db96d56Sopenharmony_ci                break;
14127db96d56Sopenharmony_ci            case CP_UTF32LE:
14137db96d56Sopenharmony_ci                if (header_len % 4 != 0) {
14147db96d56Sopenharmony_ci                    debug(L"UTF-32LE, but not divisible by 4: %d\n",
14157db96d56Sopenharmony_ci                          header_len);
14167db96d56Sopenharmony_ci                    /* nchars = 0; Not needed - initialised to 0. */
14177db96d56Sopenharmony_ci                }
14187db96d56Sopenharmony_ci                else {
14197db96d56Sopenharmony_ci                    for (i = header_len, j = header_len / 2; i > 0; i -= 4,
14207db96d56Sopenharmony_ci                                                                    j -= 2) {
14217db96d56Sopenharmony_ci                        shebang_alias[j - 1] = start[i - 3];
14227db96d56Sopenharmony_ci                        shebang_alias[j - 2] = start[i - 4];
14237db96d56Sopenharmony_ci                    }
14247db96d56Sopenharmony_ci                    nchars = header_len / sizeof(wchar_t);
14257db96d56Sopenharmony_ci                }
14267db96d56Sopenharmony_ci                break;
14277db96d56Sopenharmony_ci            }
14287db96d56Sopenharmony_ci            if (nchars > 0) {
14297db96d56Sopenharmony_ci                shebang_line[--nchars] = L'\0';
14307db96d56Sopenharmony_ci                is_virt = parse_shebang(shebang_line, nchars, &command,
14317db96d56Sopenharmony_ci                                        &suffix, &search);
14327db96d56Sopenharmony_ci                if (command != NULL) {
14337db96d56Sopenharmony_ci                    debug(L"parse_shebang: found command: %ls\n", command);
14347db96d56Sopenharmony_ci                    if (!is_virt) {
14357db96d56Sopenharmony_ci                        invoke_child(command, suffix, cmdline);
14367db96d56Sopenharmony_ci                    }
14377db96d56Sopenharmony_ci                    else {
14387db96d56Sopenharmony_ci                        suffix = wcschr(command, L' ');
14397db96d56Sopenharmony_ci                        if (suffix != NULL) {
14407db96d56Sopenharmony_ci                            *suffix++ = L'\0';
14417db96d56Sopenharmony_ci                            suffix = skip_whitespace(suffix);
14427db96d56Sopenharmony_ci                        }
14437db96d56Sopenharmony_ci                        if (wcsncmp(command, L"python", 6))
14447db96d56Sopenharmony_ci                            error(RC_BAD_VIRTUAL_PATH, L"Unknown virtual \
14457db96d56Sopenharmony_cipath '%ls'", command);
14467db96d56Sopenharmony_ci                        command += 6;   /* skip past "python" */
14477db96d56Sopenharmony_ci                        if (search && ((*command == L'\0') || isspace(*command))) {
14487db96d56Sopenharmony_ci                            /* Command is eligible for path search, and there
14497db96d56Sopenharmony_ci                             * is no version specification.
14507db96d56Sopenharmony_ci                             */
14517db96d56Sopenharmony_ci                            debug(L"searching PATH for python executable\n");
14527db96d56Sopenharmony_ci                            cmd = find_on_path(PYTHON_EXECUTABLE);
14537db96d56Sopenharmony_ci                            debug(L"Python on path: %ls\n", cmd ? cmd->value : L"<not found>");
14547db96d56Sopenharmony_ci                            if (cmd) {
14557db96d56Sopenharmony_ci                                debug(L"located python on PATH: %ls\n", cmd->value);
14567db96d56Sopenharmony_ci                                invoke_child(cmd->value, suffix, cmdline);
14577db96d56Sopenharmony_ci                                /* Exit here, as we have found the command */
14587db96d56Sopenharmony_ci                                return;
14597db96d56Sopenharmony_ci                            }
14607db96d56Sopenharmony_ci                            /* FALL THROUGH: No python found on PATH, so fall
14617db96d56Sopenharmony_ci                             * back to locating the correct installed python.
14627db96d56Sopenharmony_ci                             */
14637db96d56Sopenharmony_ci                        }
14647db96d56Sopenharmony_ci                        if (*command && !validate_version(command))
14657db96d56Sopenharmony_ci                            error(RC_BAD_VIRTUAL_PATH, L"Invalid version \
14667db96d56Sopenharmony_cispecification: '%ls'.\nIn the first line of the script, 'python' needs to be \
14677db96d56Sopenharmony_cifollowed by a valid version specifier.\nPlease check the documentation.",
14687db96d56Sopenharmony_ci                                  command);
14697db96d56Sopenharmony_ci                        /* TODO could call validate_version(command) */
14707db96d56Sopenharmony_ci                        ip = locate_python(command, TRUE);
14717db96d56Sopenharmony_ci                        if (ip == NULL) {
14727db96d56Sopenharmony_ci                            error(RC_NO_PYTHON, L"Requested Python version \
14737db96d56Sopenharmony_ci(%ls) is not installed", command);
14747db96d56Sopenharmony_ci                        }
14757db96d56Sopenharmony_ci                        else {
14767db96d56Sopenharmony_ci                            invoke_child(ip->executable, suffix, cmdline);
14777db96d56Sopenharmony_ci                        }
14787db96d56Sopenharmony_ci                    }
14797db96d56Sopenharmony_ci                }
14807db96d56Sopenharmony_ci            }
14817db96d56Sopenharmony_ci        }
14827db96d56Sopenharmony_ci    }
14837db96d56Sopenharmony_ci}
14847db96d56Sopenharmony_ci
14857db96d56Sopenharmony_cistatic wchar_t *
14867db96d56Sopenharmony_ciskip_me(wchar_t * cmdline)
14877db96d56Sopenharmony_ci{
14887db96d56Sopenharmony_ci    BOOL quoted;
14897db96d56Sopenharmony_ci    wchar_t c;
14907db96d56Sopenharmony_ci    wchar_t * result = cmdline;
14917db96d56Sopenharmony_ci
14927db96d56Sopenharmony_ci    quoted = cmdline[0] == L'\"';
14937db96d56Sopenharmony_ci    if (!quoted)
14947db96d56Sopenharmony_ci        c = L' ';
14957db96d56Sopenharmony_ci    else {
14967db96d56Sopenharmony_ci        c = L'\"';
14977db96d56Sopenharmony_ci        ++result;
14987db96d56Sopenharmony_ci    }
14997db96d56Sopenharmony_ci    result = wcschr(result, c);
15007db96d56Sopenharmony_ci    if (result == NULL) /* when, for example, just exe name on command line */
15017db96d56Sopenharmony_ci        result = L"";
15027db96d56Sopenharmony_ci    else {
15037db96d56Sopenharmony_ci        ++result; /* skip past space or closing quote */
15047db96d56Sopenharmony_ci        result = skip_whitespace(result);
15057db96d56Sopenharmony_ci    }
15067db96d56Sopenharmony_ci    return result;
15077db96d56Sopenharmony_ci}
15087db96d56Sopenharmony_ci
15097db96d56Sopenharmony_cistatic DWORD version_high = 0;
15107db96d56Sopenharmony_cistatic DWORD version_low = 0;
15117db96d56Sopenharmony_ci
15127db96d56Sopenharmony_cistatic void
15137db96d56Sopenharmony_ciget_version_info(wchar_t * version_text, size_t size)
15147db96d56Sopenharmony_ci{
15157db96d56Sopenharmony_ci    WORD maj, min, rel, bld;
15167db96d56Sopenharmony_ci
15177db96d56Sopenharmony_ci    if (!version_high && !version_low)
15187db96d56Sopenharmony_ci        wcsncpy_s(version_text, size, L"0.1", _TRUNCATE);   /* fallback */
15197db96d56Sopenharmony_ci    else {
15207db96d56Sopenharmony_ci        maj = HIWORD(version_high);
15217db96d56Sopenharmony_ci        min = LOWORD(version_high);
15227db96d56Sopenharmony_ci        rel = HIWORD(version_low);
15237db96d56Sopenharmony_ci        bld = LOWORD(version_low);
15247db96d56Sopenharmony_ci        _snwprintf_s(version_text, size, _TRUNCATE, L"%d.%d.%d.%d", maj,
15257db96d56Sopenharmony_ci                     min, rel, bld);
15267db96d56Sopenharmony_ci    }
15277db96d56Sopenharmony_ci}
15287db96d56Sopenharmony_ci
15297db96d56Sopenharmony_cistatic void
15307db96d56Sopenharmony_cishow_help_text(wchar_t ** argv)
15317db96d56Sopenharmony_ci{
15327db96d56Sopenharmony_ci    wchar_t version_text [MAX_PATH];
15337db96d56Sopenharmony_ci#if defined(_M_X64)
15347db96d56Sopenharmony_ci    BOOL canDo64bit = TRUE;
15357db96d56Sopenharmony_ci#else
15367db96d56Sopenharmony_ci    /* If we are a 32bit process on a 64bit Windows, first hit the 64bit keys. */
15377db96d56Sopenharmony_ci    BOOL canDo64bit = FALSE;
15387db96d56Sopenharmony_ci    IsWow64Process(GetCurrentProcess(), &canDo64bit);
15397db96d56Sopenharmony_ci#endif
15407db96d56Sopenharmony_ci
15417db96d56Sopenharmony_ci    get_version_info(version_text, MAX_PATH);
15427db96d56Sopenharmony_ci    fwprintf(stdout, L"\
15437db96d56Sopenharmony_ciPython Launcher for Windows Version %ls\n\n", version_text);
15447db96d56Sopenharmony_ci    fwprintf(stdout, L"\
15457db96d56Sopenharmony_ciusage:\n\
15467db96d56Sopenharmony_ci%ls [launcher-args] [python-args] [script [script-args]]\n\n", argv[0]);
15477db96d56Sopenharmony_ci    fputws(L"\
15487db96d56Sopenharmony_ciLauncher arguments:\n\n\
15497db96d56Sopenharmony_ci-2     : Launch the latest Python 2.x version\n\
15507db96d56Sopenharmony_ci-3     : Launch the latest Python 3.x version\n\
15517db96d56Sopenharmony_ci-X.Y   : Launch the specified Python version\n", stdout);
15527db96d56Sopenharmony_ci    if (canDo64bit) {
15537db96d56Sopenharmony_ci        fputws(L"\
15547db96d56Sopenharmony_ci     The above all default to 64 bit if a matching 64 bit python is present.\n\
15557db96d56Sopenharmony_ci-X.Y-32: Launch the specified 32bit Python version\n\
15567db96d56Sopenharmony_ci-X-32  : Launch the latest 32bit Python X version\n\
15577db96d56Sopenharmony_ci-X.Y-64: Launch the specified 64bit Python version\n\
15587db96d56Sopenharmony_ci-X-64  : Launch the latest 64bit Python X version", stdout);
15597db96d56Sopenharmony_ci    }
15607db96d56Sopenharmony_ci    fputws(L"\n-0  --list       : List the available pythons", stdout);
15617db96d56Sopenharmony_ci    fputws(L"\n-0p --list-paths : List with paths", stdout);
15627db96d56Sopenharmony_ci    fputws(L"\n\n If no script is specified the specified interpreter is opened.", stdout);
15637db96d56Sopenharmony_ci    fputws(L"\nIf an exact version is not given, using the latest version can be overridden by", stdout);
15647db96d56Sopenharmony_ci    fputws(L"\nany of the following, (in priority order):", stdout);
15657db96d56Sopenharmony_ci    fputws(L"\n An active virtual environment", stdout);
15667db96d56Sopenharmony_ci    fputws(L"\n A shebang line in the script (if present)", stdout);
15677db96d56Sopenharmony_ci    fputws(L"\n With -2 or -3 flag a matching PY_PYTHON2 or PY_PYTHON3 Environment variable", stdout);
15687db96d56Sopenharmony_ci    fputws(L"\n A PY_PYTHON Environment variable", stdout);
15697db96d56Sopenharmony_ci    fputws(L"\n From [defaults] in py.ini in your %LOCALAPPDATA%\\py.ini", stdout);
15707db96d56Sopenharmony_ci    fputws(L"\n From [defaults] in py.ini beside py.exe (use `where py` to locate)", stdout);
15717db96d56Sopenharmony_ci    fputws(L"\n\nThe following help text is from Python:\n\n", stdout);
15727db96d56Sopenharmony_ci    fflush(stdout);
15737db96d56Sopenharmony_ci}
15747db96d56Sopenharmony_ci
15757db96d56Sopenharmony_cistatic BOOL
15767db96d56Sopenharmony_cishow_python_list(wchar_t ** argv)
15777db96d56Sopenharmony_ci{
15787db96d56Sopenharmony_ci    /*
15797db96d56Sopenharmony_ci     * Display options -0
15807db96d56Sopenharmony_ci     */
15817db96d56Sopenharmony_ci    INSTALLED_PYTHON * result = NULL;
15827db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip = installed_pythons; /* List of installed pythons */
15837db96d56Sopenharmony_ci    INSTALLED_PYTHON * defpy = locate_python(L"", FALSE);
15847db96d56Sopenharmony_ci    size_t i = 0;
15857db96d56Sopenharmony_ci    wchar_t *p = argv[1];
15867db96d56Sopenharmony_ci    wchar_t *ver_fmt = L"-%ls-%d";
15877db96d56Sopenharmony_ci    wchar_t *fmt = L"\n %ls";
15887db96d56Sopenharmony_ci    wchar_t *defind = L" *"; /* Default indicator */
15897db96d56Sopenharmony_ci
15907db96d56Sopenharmony_ci    /*
15917db96d56Sopenharmony_ci    * Output informational messages to stderr to keep output
15927db96d56Sopenharmony_ci    * clean for use in pipes, etc.
15937db96d56Sopenharmony_ci    */
15947db96d56Sopenharmony_ci    fwprintf(stderr,
15957db96d56Sopenharmony_ci             L"Installed Pythons found by %s Launcher for Windows", argv[0]);
15967db96d56Sopenharmony_ci    if (!_wcsicmp(p, L"-0p") || !_wcsicmp(p, L"--list-paths"))
15977db96d56Sopenharmony_ci        fmt = L"\n %-15ls%ls"; /* include path */
15987db96d56Sopenharmony_ci
15997db96d56Sopenharmony_ci    if (num_installed_pythons == 0) /* We have somehow got here without searching for pythons */
16007db96d56Sopenharmony_ci        locate_all_pythons(); /* Find them, Populates installed_pythons */
16017db96d56Sopenharmony_ci
16027db96d56Sopenharmony_ci    if (num_installed_pythons == 0) /* No pythons found */
16037db96d56Sopenharmony_ci        fwprintf(stderr, L"\nNo Installed Pythons Found!");
16047db96d56Sopenharmony_ci    else
16057db96d56Sopenharmony_ci    {
16067db96d56Sopenharmony_ci        for (i = 0; i < num_installed_pythons; i++, ip++) {
16077db96d56Sopenharmony_ci            wchar_t version[BUFSIZ];
16087db96d56Sopenharmony_ci            if (wcscmp(ip->version, L"venv") == 0) {
16097db96d56Sopenharmony_ci                wcscpy_s(version, BUFSIZ, L"(venv)");
16107db96d56Sopenharmony_ci            }
16117db96d56Sopenharmony_ci            else {
16127db96d56Sopenharmony_ci                swprintf_s(version, BUFSIZ, ver_fmt, ip->version, ip->bits);
16137db96d56Sopenharmony_ci            }
16147db96d56Sopenharmony_ci
16157db96d56Sopenharmony_ci            if (ip->exe_display[0]) {
16167db96d56Sopenharmony_ci                fwprintf(stdout, fmt, version, ip->exe_display);
16177db96d56Sopenharmony_ci            }
16187db96d56Sopenharmony_ci            else {
16197db96d56Sopenharmony_ci                fwprintf(stdout, fmt, version, ip->executable);
16207db96d56Sopenharmony_ci            }
16217db96d56Sopenharmony_ci            /* If there is a default indicate it */
16227db96d56Sopenharmony_ci            if (defpy == ip)
16237db96d56Sopenharmony_ci                fwprintf(stderr, defind);
16247db96d56Sopenharmony_ci        }
16257db96d56Sopenharmony_ci    }
16267db96d56Sopenharmony_ci
16277db96d56Sopenharmony_ci    if ((defpy == NULL) && (num_installed_pythons > 0))
16287db96d56Sopenharmony_ci        /* We have pythons but none is the default */
16297db96d56Sopenharmony_ci        fwprintf(stderr, L"\n\nCan't find a Default Python.\n\n");
16307db96d56Sopenharmony_ci    else
16317db96d56Sopenharmony_ci        fwprintf(stderr, L"\n\n"); /* End with a blank line */
16327db96d56Sopenharmony_ci    return FALSE; /* If this has been called we cannot continue */
16337db96d56Sopenharmony_ci}
16347db96d56Sopenharmony_ci
16357db96d56Sopenharmony_ci#if defined(VENV_REDIRECT)
16367db96d56Sopenharmony_ci
16377db96d56Sopenharmony_cistatic int
16387db96d56Sopenharmony_cifind_home_value(const char *buffer, const char **start, DWORD *length)
16397db96d56Sopenharmony_ci{
16407db96d56Sopenharmony_ci    for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) {
16417db96d56Sopenharmony_ci        if (*s == '\n') {
16427db96d56Sopenharmony_ci            ++s;
16437db96d56Sopenharmony_ci        }
16447db96d56Sopenharmony_ci        for (int i = 4; i > 0 && *s; --i, ++s);
16457db96d56Sopenharmony_ci
16467db96d56Sopenharmony_ci        while (*s && iswspace(*s)) {
16477db96d56Sopenharmony_ci            ++s;
16487db96d56Sopenharmony_ci        }
16497db96d56Sopenharmony_ci        if (*s != L'=') {
16507db96d56Sopenharmony_ci            continue;
16517db96d56Sopenharmony_ci        }
16527db96d56Sopenharmony_ci
16537db96d56Sopenharmony_ci        do {
16547db96d56Sopenharmony_ci            ++s;
16557db96d56Sopenharmony_ci        } while (*s && iswspace(*s));
16567db96d56Sopenharmony_ci
16577db96d56Sopenharmony_ci        *start = s;
16587db96d56Sopenharmony_ci        char *nl = strchr(s, '\n');
16597db96d56Sopenharmony_ci        if (nl) {
16607db96d56Sopenharmony_ci            *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s);
16617db96d56Sopenharmony_ci        } else {
16627db96d56Sopenharmony_ci            *length = (DWORD)strlen(s);
16637db96d56Sopenharmony_ci        }
16647db96d56Sopenharmony_ci        return 1;
16657db96d56Sopenharmony_ci    }
16667db96d56Sopenharmony_ci    return 0;
16677db96d56Sopenharmony_ci}
16687db96d56Sopenharmony_ci#endif
16697db96d56Sopenharmony_ci
16707db96d56Sopenharmony_cistatic wchar_t *
16717db96d56Sopenharmony_ciwcsdup_pad(const wchar_t *s, int padding, int *newlen)
16727db96d56Sopenharmony_ci{
16737db96d56Sopenharmony_ci    size_t len = wcslen(s);
16747db96d56Sopenharmony_ci    len += 1 + padding;
16757db96d56Sopenharmony_ci    wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t));
16767db96d56Sopenharmony_ci    if (!r) {
16777db96d56Sopenharmony_ci        return NULL;
16787db96d56Sopenharmony_ci    }
16797db96d56Sopenharmony_ci    if (wcscpy_s(r, len, s)) {
16807db96d56Sopenharmony_ci        free(r);
16817db96d56Sopenharmony_ci        return NULL;
16827db96d56Sopenharmony_ci    }
16837db96d56Sopenharmony_ci    *newlen = len < MAXINT ? (int)len : MAXINT;
16847db96d56Sopenharmony_ci    return r;
16857db96d56Sopenharmony_ci}
16867db96d56Sopenharmony_ci
16877db96d56Sopenharmony_cistatic wchar_t *
16887db96d56Sopenharmony_ciget_process_name()
16897db96d56Sopenharmony_ci{
16907db96d56Sopenharmony_ci    DWORD bufferLen = MAX_PATH;
16917db96d56Sopenharmony_ci    DWORD len = bufferLen;
16927db96d56Sopenharmony_ci    wchar_t *r = NULL;
16937db96d56Sopenharmony_ci
16947db96d56Sopenharmony_ci    while (!r) {
16957db96d56Sopenharmony_ci        r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t));
16967db96d56Sopenharmony_ci        if (!r) {
16977db96d56Sopenharmony_ci            error(RC_NO_MEMORY, L"out of memory");
16987db96d56Sopenharmony_ci            return NULL;
16997db96d56Sopenharmony_ci        }
17007db96d56Sopenharmony_ci        len = GetModuleFileNameW(NULL, r, bufferLen);
17017db96d56Sopenharmony_ci        if (len == 0) {
17027db96d56Sopenharmony_ci            free(r);
17037db96d56Sopenharmony_ci            error(0, L"Failed to get module name");
17047db96d56Sopenharmony_ci            return NULL;
17057db96d56Sopenharmony_ci        } else if (len == bufferLen &&
17067db96d56Sopenharmony_ci                   GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
17077db96d56Sopenharmony_ci            free(r);
17087db96d56Sopenharmony_ci            r = NULL;
17097db96d56Sopenharmony_ci            bufferLen *= 2;
17107db96d56Sopenharmony_ci        }
17117db96d56Sopenharmony_ci    }
17127db96d56Sopenharmony_ci
17137db96d56Sopenharmony_ci    return r;
17147db96d56Sopenharmony_ci}
17157db96d56Sopenharmony_ci
17167db96d56Sopenharmony_cistatic int
17177db96d56Sopenharmony_ciprocess(int argc, wchar_t ** argv)
17187db96d56Sopenharmony_ci{
17197db96d56Sopenharmony_ci    wchar_t * wp;
17207db96d56Sopenharmony_ci    wchar_t * command;
17217db96d56Sopenharmony_ci    wchar_t * executable;
17227db96d56Sopenharmony_ci    wchar_t * p;
17237db96d56Sopenharmony_ci    wchar_t * argv0;
17247db96d56Sopenharmony_ci    int rc = 0;
17257db96d56Sopenharmony_ci    INSTALLED_PYTHON * ip;
17267db96d56Sopenharmony_ci    BOOL valid;
17277db96d56Sopenharmony_ci    DWORD size, attrs;
17287db96d56Sopenharmony_ci    wchar_t message[MSGSIZE];
17297db96d56Sopenharmony_ci    void * version_data;
17307db96d56Sopenharmony_ci    VS_FIXEDFILEINFO * file_info;
17317db96d56Sopenharmony_ci    UINT block_size;
17327db96d56Sopenharmony_ci#if defined(VENV_REDIRECT)
17337db96d56Sopenharmony_ci    wchar_t * venv_cfg_path;
17347db96d56Sopenharmony_ci    int newlen;
17357db96d56Sopenharmony_ci#elif defined(SCRIPT_WRAPPER)
17367db96d56Sopenharmony_ci    wchar_t * newcommand;
17377db96d56Sopenharmony_ci    wchar_t * av[2];
17387db96d56Sopenharmony_ci    int newlen;
17397db96d56Sopenharmony_ci    HRESULT hr;
17407db96d56Sopenharmony_ci    int index;
17417db96d56Sopenharmony_ci#else
17427db96d56Sopenharmony_ci    HRESULT hr;
17437db96d56Sopenharmony_ci    int index;
17447db96d56Sopenharmony_ci#endif
17457db96d56Sopenharmony_ci
17467db96d56Sopenharmony_ci    setvbuf(stderr, (char *)NULL, _IONBF, 0);
17477db96d56Sopenharmony_ci    wp = get_env(L"PYLAUNCH_DEBUG");
17487db96d56Sopenharmony_ci    if ((wp != NULL) && (*wp != L'\0'))
17497db96d56Sopenharmony_ci        log_fp = stderr;
17507db96d56Sopenharmony_ci
17517db96d56Sopenharmony_ci#if defined(_M_X64)
17527db96d56Sopenharmony_ci    debug(L"launcher build: 64bit\n");
17537db96d56Sopenharmony_ci#else
17547db96d56Sopenharmony_ci    debug(L"launcher build: 32bit\n");
17557db96d56Sopenharmony_ci#endif
17567db96d56Sopenharmony_ci#if defined(_WINDOWS)
17577db96d56Sopenharmony_ci    debug(L"launcher executable: Windows\n");
17587db96d56Sopenharmony_ci#else
17597db96d56Sopenharmony_ci    debug(L"launcher executable: Console\n");
17607db96d56Sopenharmony_ci#endif
17617db96d56Sopenharmony_ci#if !defined(VENV_REDIRECT)
17627db96d56Sopenharmony_ci    /* Get the local appdata folder (non-roaming) */
17637db96d56Sopenharmony_ci    hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA,
17647db96d56Sopenharmony_ci                          NULL, 0, appdata_ini_path);
17657db96d56Sopenharmony_ci    if (hr != S_OK) {
17667db96d56Sopenharmony_ci        debug(L"SHGetFolderPath failed: %X\n", hr);
17677db96d56Sopenharmony_ci        appdata_ini_path[0] = L'\0';
17687db96d56Sopenharmony_ci    }
17697db96d56Sopenharmony_ci    else {
17707db96d56Sopenharmony_ci        wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE);
17717db96d56Sopenharmony_ci        attrs = GetFileAttributesW(appdata_ini_path);
17727db96d56Sopenharmony_ci        if (attrs == INVALID_FILE_ATTRIBUTES) {
17737db96d56Sopenharmony_ci            debug(L"File '%ls' non-existent\n", appdata_ini_path);
17747db96d56Sopenharmony_ci            appdata_ini_path[0] = L'\0';
17757db96d56Sopenharmony_ci        } else {
17767db96d56Sopenharmony_ci            debug(L"Using local configuration file '%ls'\n", appdata_ini_path);
17777db96d56Sopenharmony_ci        }
17787db96d56Sopenharmony_ci    }
17797db96d56Sopenharmony_ci#endif
17807db96d56Sopenharmony_ci    argv0 = get_process_name();
17817db96d56Sopenharmony_ci    size = GetFileVersionInfoSizeW(argv0, &size);
17827db96d56Sopenharmony_ci    if (size == 0) {
17837db96d56Sopenharmony_ci        winerror(GetLastError(), message, MSGSIZE);
17847db96d56Sopenharmony_ci        debug(L"GetFileVersionInfoSize failed: %ls\n", message);
17857db96d56Sopenharmony_ci    }
17867db96d56Sopenharmony_ci    else {
17877db96d56Sopenharmony_ci        version_data = malloc(size);
17887db96d56Sopenharmony_ci        if (version_data) {
17897db96d56Sopenharmony_ci            valid = GetFileVersionInfoW(argv0, 0, size,
17907db96d56Sopenharmony_ci                                        version_data);
17917db96d56Sopenharmony_ci            if (!valid)
17927db96d56Sopenharmony_ci                debug(L"GetFileVersionInfo failed: %X\n", GetLastError());
17937db96d56Sopenharmony_ci            else {
17947db96d56Sopenharmony_ci                valid = VerQueryValueW(version_data, L"\\",
17957db96d56Sopenharmony_ci                                       (LPVOID *) &file_info, &block_size);
17967db96d56Sopenharmony_ci                if (!valid)
17977db96d56Sopenharmony_ci                    debug(L"VerQueryValue failed: %X\n", GetLastError());
17987db96d56Sopenharmony_ci                else {
17997db96d56Sopenharmony_ci                    version_high = file_info->dwFileVersionMS;
18007db96d56Sopenharmony_ci                    version_low = file_info->dwFileVersionLS;
18017db96d56Sopenharmony_ci                }
18027db96d56Sopenharmony_ci            }
18037db96d56Sopenharmony_ci            free(version_data);
18047db96d56Sopenharmony_ci        }
18057db96d56Sopenharmony_ci    }
18067db96d56Sopenharmony_ci
18077db96d56Sopenharmony_ci#if defined(VENV_REDIRECT)
18087db96d56Sopenharmony_ci    /* Allocate some extra space for new filenames */
18097db96d56Sopenharmony_ci    venv_cfg_path = wcsdup_pad(argv0, 32, &newlen);
18107db96d56Sopenharmony_ci    if (!venv_cfg_path) {
18117db96d56Sopenharmony_ci        error(RC_NO_MEMORY, L"Failed to copy module name");
18127db96d56Sopenharmony_ci    }
18137db96d56Sopenharmony_ci    p = wcsrchr(venv_cfg_path, L'\\');
18147db96d56Sopenharmony_ci
18157db96d56Sopenharmony_ci    if (p == NULL) {
18167db96d56Sopenharmony_ci        error(RC_NO_VENV_CFG, L"No pyvenv.cfg file");
18177db96d56Sopenharmony_ci    }
18187db96d56Sopenharmony_ci    p[0] = L'\0';
18197db96d56Sopenharmony_ci    wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg");
18207db96d56Sopenharmony_ci    attrs = GetFileAttributesW(venv_cfg_path);
18217db96d56Sopenharmony_ci    if (attrs == INVALID_FILE_ATTRIBUTES) {
18227db96d56Sopenharmony_ci        debug(L"File '%ls' non-existent\n", venv_cfg_path);
18237db96d56Sopenharmony_ci        p[0] = '\0';
18247db96d56Sopenharmony_ci        p = wcsrchr(venv_cfg_path, L'\\');
18257db96d56Sopenharmony_ci        if (p != NULL) {
18267db96d56Sopenharmony_ci            p[0] = '\0';
18277db96d56Sopenharmony_ci            wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg");
18287db96d56Sopenharmony_ci            attrs = GetFileAttributesW(venv_cfg_path);
18297db96d56Sopenharmony_ci            if (attrs == INVALID_FILE_ATTRIBUTES) {
18307db96d56Sopenharmony_ci                debug(L"File '%ls' non-existent\n", venv_cfg_path);
18317db96d56Sopenharmony_ci                error(RC_NO_VENV_CFG, L"No pyvenv.cfg file");
18327db96d56Sopenharmony_ci            }
18337db96d56Sopenharmony_ci        }
18347db96d56Sopenharmony_ci    }
18357db96d56Sopenharmony_ci    debug(L"Using venv configuration file '%ls'\n", venv_cfg_path);
18367db96d56Sopenharmony_ci#else
18377db96d56Sopenharmony_ci    /* Allocate some extra space for new filenames */
18387db96d56Sopenharmony_ci    if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) {
18397db96d56Sopenharmony_ci        error(RC_NO_MEMORY, L"Failed to copy module name");
18407db96d56Sopenharmony_ci    }
18417db96d56Sopenharmony_ci    p = wcsrchr(launcher_ini_path, L'\\');
18427db96d56Sopenharmony_ci
18437db96d56Sopenharmony_ci    if (p == NULL) {
18447db96d56Sopenharmony_ci        debug(L"GetModuleFileNameW returned value has no backslash: %ls\n",
18457db96d56Sopenharmony_ci              launcher_ini_path);
18467db96d56Sopenharmony_ci        launcher_ini_path[0] = L'\0';
18477db96d56Sopenharmony_ci    }
18487db96d56Sopenharmony_ci    else {
18497db96d56Sopenharmony_ci        p[0] = L'\0';
18507db96d56Sopenharmony_ci        wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini");
18517db96d56Sopenharmony_ci        attrs = GetFileAttributesW(launcher_ini_path);
18527db96d56Sopenharmony_ci        if (attrs == INVALID_FILE_ATTRIBUTES) {
18537db96d56Sopenharmony_ci            debug(L"File '%ls' non-existent\n", launcher_ini_path);
18547db96d56Sopenharmony_ci            launcher_ini_path[0] = L'\0';
18557db96d56Sopenharmony_ci        } else {
18567db96d56Sopenharmony_ci            debug(L"Using global configuration file '%ls'\n", launcher_ini_path);
18577db96d56Sopenharmony_ci        }
18587db96d56Sopenharmony_ci    }
18597db96d56Sopenharmony_ci#endif
18607db96d56Sopenharmony_ci
18617db96d56Sopenharmony_ci    command = skip_me(GetCommandLineW());
18627db96d56Sopenharmony_ci    debug(L"Called with command line: %ls\n", command);
18637db96d56Sopenharmony_ci
18647db96d56Sopenharmony_ci#if !defined(VENV_REDIRECT)
18657db96d56Sopenharmony_ci    /* bpo-35811: The __PYVENV_LAUNCHER__ variable is used to
18667db96d56Sopenharmony_ci     * override sys.executable and locate the original prefix path.
18677db96d56Sopenharmony_ci     * However, if it is silently inherited by a non-venv Python
18687db96d56Sopenharmony_ci     * process, that process will believe it is running in the venv
18697db96d56Sopenharmony_ci     * still. This is the only place where *we* can clear it (that is,
18707db96d56Sopenharmony_ci     * when py.exe is being used to launch Python), so we do.
18717db96d56Sopenharmony_ci     */
18727db96d56Sopenharmony_ci    SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", NULL);
18737db96d56Sopenharmony_ci#endif
18747db96d56Sopenharmony_ci
18757db96d56Sopenharmony_ci#if defined(SCRIPT_WRAPPER)
18767db96d56Sopenharmony_ci    /* The launcher is being used in "script wrapper" mode.
18777db96d56Sopenharmony_ci     * There should therefore be a Python script named <exename>-script.py in
18787db96d56Sopenharmony_ci     * the same directory as the launcher executable.
18797db96d56Sopenharmony_ci     * Put the script name into argv as the first (script name) argument.
18807db96d56Sopenharmony_ci     */
18817db96d56Sopenharmony_ci
18827db96d56Sopenharmony_ci    /* Get the wrapped script name - if the script is not present, this will
18837db96d56Sopenharmony_ci     * terminate the program with an error.
18847db96d56Sopenharmony_ci     */
18857db96d56Sopenharmony_ci    locate_wrapped_script();
18867db96d56Sopenharmony_ci
18877db96d56Sopenharmony_ci    /* Add the wrapped script to the start of command */
18887db96d56Sopenharmony_ci    newlen = wcslen(wrapped_script_path) + wcslen(command) + 2; /* ' ' + NUL */
18897db96d56Sopenharmony_ci    newcommand = malloc(sizeof(wchar_t) * newlen);
18907db96d56Sopenharmony_ci    if (!newcommand) {
18917db96d56Sopenharmony_ci        error(RC_NO_MEMORY, L"Could not allocate new command line");
18927db96d56Sopenharmony_ci    }
18937db96d56Sopenharmony_ci    else {
18947db96d56Sopenharmony_ci        wcscpy_s(newcommand, newlen, wrapped_script_path);
18957db96d56Sopenharmony_ci        wcscat_s(newcommand, newlen, L" ");
18967db96d56Sopenharmony_ci        wcscat_s(newcommand, newlen, command);
18977db96d56Sopenharmony_ci        debug(L"Running wrapped script with command line '%ls'\n", newcommand);
18987db96d56Sopenharmony_ci        read_commands();
18997db96d56Sopenharmony_ci        av[0] = wrapped_script_path;
19007db96d56Sopenharmony_ci        av[1] = NULL;
19017db96d56Sopenharmony_ci        maybe_handle_shebang(av, newcommand);
19027db96d56Sopenharmony_ci        /* Returns if no shebang line - pass to default processing */
19037db96d56Sopenharmony_ci        command = newcommand;
19047db96d56Sopenharmony_ci        valid = FALSE;
19057db96d56Sopenharmony_ci    }
19067db96d56Sopenharmony_ci#elif defined(VENV_REDIRECT)
19077db96d56Sopenharmony_ci    {
19087db96d56Sopenharmony_ci        FILE *f;
19097db96d56Sopenharmony_ci        char buffer[4096]; /* 4KB should be enough for anybody */
19107db96d56Sopenharmony_ci        char *start;
19117db96d56Sopenharmony_ci        DWORD len, cch, cch_actual;
19127db96d56Sopenharmony_ci        size_t cb;
19137db96d56Sopenharmony_ci        if (_wfopen_s(&f, venv_cfg_path, L"r")) {
19147db96d56Sopenharmony_ci            error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path);
19157db96d56Sopenharmony_ci        }
19167db96d56Sopenharmony_ci        cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]),
19177db96d56Sopenharmony_ci                     sizeof(buffer) / sizeof(buffer[0]), f);
19187db96d56Sopenharmony_ci        fclose(f);
19197db96d56Sopenharmony_ci
19207db96d56Sopenharmony_ci        if (!find_home_value(buffer, &start, &len)) {
19217db96d56Sopenharmony_ci            error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'",
19227db96d56Sopenharmony_ci                  venv_cfg_path);
19237db96d56Sopenharmony_ci        }
19247db96d56Sopenharmony_ci
19257db96d56Sopenharmony_ci        cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0);
19267db96d56Sopenharmony_ci        if (!cch) {
19277db96d56Sopenharmony_ci            error(0, L"Cannot determine memory for home path");
19287db96d56Sopenharmony_ci        }
19297db96d56Sopenharmony_ci        cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 4; /* include sep, null and quotes */
19307db96d56Sopenharmony_ci        executable = (wchar_t *)malloc(cch * sizeof(wchar_t));
19317db96d56Sopenharmony_ci        if (executable == NULL) {
19327db96d56Sopenharmony_ci            error(RC_NO_MEMORY, L"A memory allocation failed");
19337db96d56Sopenharmony_ci        }
19347db96d56Sopenharmony_ci        /* start with a quote - we'll skip this ahead, but want it for the final string */
19357db96d56Sopenharmony_ci        executable[0] = L'"';
19367db96d56Sopenharmony_ci        cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, &executable[1], cch - 1);
19377db96d56Sopenharmony_ci        if (!cch_actual) {
19387db96d56Sopenharmony_ci            error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'",
19397db96d56Sopenharmony_ci                  venv_cfg_path);
19407db96d56Sopenharmony_ci        }
19417db96d56Sopenharmony_ci        cch_actual += 1; /* account for the first quote */
19427db96d56Sopenharmony_ci        executable[cch_actual] = L'\0';
19437db96d56Sopenharmony_ci        if (executable[cch_actual - 1] != L'\\') {
19447db96d56Sopenharmony_ci            executable[cch_actual++] = L'\\';
19457db96d56Sopenharmony_ci            executable[cch_actual] = L'\0';
19467db96d56Sopenharmony_ci        }
19477db96d56Sopenharmony_ci        if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE)) {
19487db96d56Sopenharmony_ci            error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'",
19497db96d56Sopenharmony_ci                  venv_cfg_path);
19507db96d56Sopenharmony_ci        }
19517db96d56Sopenharmony_ci        /* there's no trailing quote, so we only have to skip one character for the test */
19527db96d56Sopenharmony_ci        if (GetFileAttributesW(&executable[1]) == INVALID_FILE_ATTRIBUTES) {
19537db96d56Sopenharmony_ci            error(RC_NO_PYTHON, L"No Python at '%ls'", executable);
19547db96d56Sopenharmony_ci        }
19557db96d56Sopenharmony_ci        /* now append the final quote */
19567db96d56Sopenharmony_ci        wcscat_s(executable, cch, L"\"");
19577db96d56Sopenharmony_ci        /* smuggle our original path through */
19587db96d56Sopenharmony_ci        if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) {
19597db96d56Sopenharmony_ci            error(0, L"Failed to set launcher environment");
19607db96d56Sopenharmony_ci        }
19617db96d56Sopenharmony_ci        valid = 1;
19627db96d56Sopenharmony_ci    }
19637db96d56Sopenharmony_ci#else
19647db96d56Sopenharmony_ci    if (argc <= 1) {
19657db96d56Sopenharmony_ci        valid = FALSE;
19667db96d56Sopenharmony_ci        p = NULL;
19677db96d56Sopenharmony_ci    }
19687db96d56Sopenharmony_ci    else {
19697db96d56Sopenharmony_ci        p = argv[1];
19707db96d56Sopenharmony_ci        if ((argc == 2) && // list version args
19717db96d56Sopenharmony_ci            (!wcsncmp(p, L"-0", wcslen(L"-0")) ||
19727db96d56Sopenharmony_ci            !wcsncmp(p, L"--list", wcslen(L"--list"))))
19737db96d56Sopenharmony_ci        {
19747db96d56Sopenharmony_ci            show_python_list(argv);
19757db96d56Sopenharmony_ci            return rc;
19767db96d56Sopenharmony_ci        }
19777db96d56Sopenharmony_ci        valid = valid && (*p == L'-') && validate_version(&p[1]);
19787db96d56Sopenharmony_ci        if (valid) {
19797db96d56Sopenharmony_ci            ip = locate_python(&p[1], FALSE);
19807db96d56Sopenharmony_ci            if (ip == NULL)
19817db96d56Sopenharmony_ci            {
19827db96d56Sopenharmony_ci                fwprintf(stdout, \
19837db96d56Sopenharmony_ci                         L"Python %ls not found!\n", &p[1]);
19847db96d56Sopenharmony_ci                valid = show_python_list(argv);
19857db96d56Sopenharmony_ci                error(RC_NO_PYTHON, L"Requested Python version (%ls) not \
19867db96d56Sopenharmony_ciinstalled, use -0 for available pythons", &p[1]);
19877db96d56Sopenharmony_ci            }
19887db96d56Sopenharmony_ci            executable = ip->executable;
19897db96d56Sopenharmony_ci            command += wcslen(p);
19907db96d56Sopenharmony_ci            command = skip_whitespace(command);
19917db96d56Sopenharmony_ci        }
19927db96d56Sopenharmony_ci        else {
19937db96d56Sopenharmony_ci            for (index = 1; index < argc; ++index) {
19947db96d56Sopenharmony_ci                if (*argv[index] != L'-')
19957db96d56Sopenharmony_ci                    break;
19967db96d56Sopenharmony_ci            }
19977db96d56Sopenharmony_ci            if (index < argc) {
19987db96d56Sopenharmony_ci                read_commands();
19997db96d56Sopenharmony_ci                maybe_handle_shebang(&argv[index], command);
20007db96d56Sopenharmony_ci            }
20017db96d56Sopenharmony_ci        }
20027db96d56Sopenharmony_ci    }
20037db96d56Sopenharmony_ci#endif
20047db96d56Sopenharmony_ci
20057db96d56Sopenharmony_ci    if (!valid) {
20067db96d56Sopenharmony_ci        if ((argc == 2) && (!_wcsicmp(p, L"-h") || !_wcsicmp(p, L"--help")))
20077db96d56Sopenharmony_ci            show_help_text(argv);
20087db96d56Sopenharmony_ci        if ((argc == 2) &&
20097db96d56Sopenharmony_ci            (!_wcsicmp(p, L"-0") || !_wcsicmp(p, L"--list") ||
20107db96d56Sopenharmony_ci            !_wcsicmp(p, L"-0p") || !_wcsicmp(p, L"--list-paths")))
20117db96d56Sopenharmony_ci        {
20127db96d56Sopenharmony_ci            executable = NULL; /* Info call only */
20137db96d56Sopenharmony_ci        }
20147db96d56Sopenharmony_ci        else {
20157db96d56Sopenharmony_ci            /* look for the default Python */
20167db96d56Sopenharmony_ci            ip = locate_python(L"", FALSE);
20177db96d56Sopenharmony_ci            if (ip == NULL)
20187db96d56Sopenharmony_ci                error(RC_NO_PYTHON, L"Can't find a default Python.");
20197db96d56Sopenharmony_ci            executable = ip->executable;
20207db96d56Sopenharmony_ci        }
20217db96d56Sopenharmony_ci    }
20227db96d56Sopenharmony_ci    if (executable != NULL)
20237db96d56Sopenharmony_ci        invoke_child(executable, NULL, command);
20247db96d56Sopenharmony_ci    else
20257db96d56Sopenharmony_ci        rc = RC_NO_PYTHON;
20267db96d56Sopenharmony_ci    return rc;
20277db96d56Sopenharmony_ci}
20287db96d56Sopenharmony_ci
20297db96d56Sopenharmony_ci#if defined(_WINDOWS)
20307db96d56Sopenharmony_ci
20317db96d56Sopenharmony_ciint WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
20327db96d56Sopenharmony_ci                   LPWSTR lpstrCmd, int nShow)
20337db96d56Sopenharmony_ci{
20347db96d56Sopenharmony_ci    return process(__argc, __wargv);
20357db96d56Sopenharmony_ci}
20367db96d56Sopenharmony_ci
20377db96d56Sopenharmony_ci#else
20387db96d56Sopenharmony_ci
20397db96d56Sopenharmony_ciint cdecl wmain(int argc, wchar_t ** argv)
20407db96d56Sopenharmony_ci{
20417db96d56Sopenharmony_ci    return process(argc, argv);
20427db96d56Sopenharmony_ci}
20437db96d56Sopenharmony_ci
20447db96d56Sopenharmony_ci#endif
2045