17db96d56Sopenharmony_ci/*
27db96d56Sopenharmony_ci * Rewritten Python launcher for Windows
37db96d56Sopenharmony_ci *
47db96d56Sopenharmony_ci * This new rewrite properly handles PEP 514 and allows any registered Python
57db96d56Sopenharmony_ci * runtime to be launched. It also enables auto-install of versions when they
67db96d56Sopenharmony_ci * are requested but no installation can be found.
77db96d56Sopenharmony_ci */
87db96d56Sopenharmony_ci
97db96d56Sopenharmony_ci#define __STDC_WANT_LIB_EXT1__ 1
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci#include <windows.h>
127db96d56Sopenharmony_ci#include <pathcch.h>
137db96d56Sopenharmony_ci#include <fcntl.h>
147db96d56Sopenharmony_ci#include <io.h>
157db96d56Sopenharmony_ci#include <shlobj.h>
167db96d56Sopenharmony_ci#include <stdio.h>
177db96d56Sopenharmony_ci#include <stdbool.h>
187db96d56Sopenharmony_ci#include <tchar.h>
197db96d56Sopenharmony_ci#include <assert.h>
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ci#define MS_WINDOWS
227db96d56Sopenharmony_ci#include "patchlevel.h"
237db96d56Sopenharmony_ci
247db96d56Sopenharmony_ci#define MAXLEN PATHCCH_MAX_CCH
257db96d56Sopenharmony_ci#define MSGSIZE 1024
267db96d56Sopenharmony_ci
277db96d56Sopenharmony_ci#define RC_NO_STD_HANDLES   100
287db96d56Sopenharmony_ci#define RC_CREATE_PROCESS   101
297db96d56Sopenharmony_ci#define RC_BAD_VIRTUAL_PATH 102
307db96d56Sopenharmony_ci#define RC_NO_PYTHON        103
317db96d56Sopenharmony_ci#define RC_NO_MEMORY        104
327db96d56Sopenharmony_ci#define RC_NO_SCRIPT        105
337db96d56Sopenharmony_ci#define RC_NO_VENV_CFG      106
347db96d56Sopenharmony_ci#define RC_BAD_VENV_CFG     107
357db96d56Sopenharmony_ci#define RC_NO_COMMANDLINE   108
367db96d56Sopenharmony_ci#define RC_INTERNAL_ERROR   109
377db96d56Sopenharmony_ci#define RC_DUPLICATE_ITEM   110
387db96d56Sopenharmony_ci#define RC_INSTALLING       111
397db96d56Sopenharmony_ci#define RC_NO_PYTHON_AT_ALL 112
407db96d56Sopenharmony_ci#define RC_NO_SHEBANG       113
417db96d56Sopenharmony_ci#define RC_RECURSIVE_SHEBANG 114
427db96d56Sopenharmony_ci
437db96d56Sopenharmony_cistatic FILE * log_fp = NULL;
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_civoid
467db96d56Sopenharmony_cidebug(wchar_t * format, ...)
477db96d56Sopenharmony_ci{
487db96d56Sopenharmony_ci    va_list va;
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_ci    if (log_fp != NULL) {
517db96d56Sopenharmony_ci        wchar_t buffer[MAXLEN];
527db96d56Sopenharmony_ci        int r = 0;
537db96d56Sopenharmony_ci        va_start(va, format);
547db96d56Sopenharmony_ci        r = vswprintf_s(buffer, MAXLEN, format, va);
557db96d56Sopenharmony_ci        va_end(va);
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_ci        if (r <= 0) {
587db96d56Sopenharmony_ci            return;
597db96d56Sopenharmony_ci        }
607db96d56Sopenharmony_ci        fputws(buffer, log_fp);
617db96d56Sopenharmony_ci        while (r && isspace(buffer[r])) {
627db96d56Sopenharmony_ci            buffer[r--] = L'\0';
637db96d56Sopenharmony_ci        }
647db96d56Sopenharmony_ci        if (buffer[0]) {
657db96d56Sopenharmony_ci            OutputDebugStringW(buffer);
667db96d56Sopenharmony_ci        }
677db96d56Sopenharmony_ci    }
687db96d56Sopenharmony_ci}
697db96d56Sopenharmony_ci
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_civoid
727db96d56Sopenharmony_ciformatWinerror(int rc, wchar_t * message, int size)
737db96d56Sopenharmony_ci{
747db96d56Sopenharmony_ci    FormatMessageW(
757db96d56Sopenharmony_ci        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
767db96d56Sopenharmony_ci        NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
777db96d56Sopenharmony_ci        message, size, NULL);
787db96d56Sopenharmony_ci}
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_civoid
827db96d56Sopenharmony_ciwinerror(int err, wchar_t * format, ... )
837db96d56Sopenharmony_ci{
847db96d56Sopenharmony_ci    va_list va;
857db96d56Sopenharmony_ci    wchar_t message[MSGSIZE];
867db96d56Sopenharmony_ci    wchar_t win_message[MSGSIZE];
877db96d56Sopenharmony_ci    int len;
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci    if (err == 0) {
907db96d56Sopenharmony_ci        err = GetLastError();
917db96d56Sopenharmony_ci    }
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_ci    va_start(va, format);
947db96d56Sopenharmony_ci    len = _vsnwprintf_s(message, MSGSIZE, _TRUNCATE, format, va);
957db96d56Sopenharmony_ci    va_end(va);
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci    formatWinerror(err, win_message, MSGSIZE);
987db96d56Sopenharmony_ci    if (len >= 0) {
997db96d56Sopenharmony_ci        _snwprintf_s(&message[len], MSGSIZE - len, _TRUNCATE, L": %s",
1007db96d56Sopenharmony_ci                     win_message);
1017db96d56Sopenharmony_ci    }
1027db96d56Sopenharmony_ci
1037db96d56Sopenharmony_ci#if !defined(_WINDOWS)
1047db96d56Sopenharmony_ci    fwprintf(stderr, L"%s\n", message);
1057db96d56Sopenharmony_ci#else
1067db96d56Sopenharmony_ci    MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...",
1077db96d56Sopenharmony_ci               MB_OK);
1087db96d56Sopenharmony_ci#endif
1097db96d56Sopenharmony_ci}
1107db96d56Sopenharmony_ci
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_civoid
1137db96d56Sopenharmony_cierror(wchar_t * format, ... )
1147db96d56Sopenharmony_ci{
1157db96d56Sopenharmony_ci    va_list va;
1167db96d56Sopenharmony_ci    wchar_t message[MSGSIZE];
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_ci    va_start(va, format);
1197db96d56Sopenharmony_ci    _vsnwprintf_s(message, MSGSIZE, _TRUNCATE, format, va);
1207db96d56Sopenharmony_ci    va_end(va);
1217db96d56Sopenharmony_ci
1227db96d56Sopenharmony_ci#if !defined(_WINDOWS)
1237db96d56Sopenharmony_ci    fwprintf(stderr, L"%s\n", message);
1247db96d56Sopenharmony_ci#else
1257db96d56Sopenharmony_ci    MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...",
1267db96d56Sopenharmony_ci               MB_OK);
1277db96d56Sopenharmony_ci#endif
1287db96d56Sopenharmony_ci}
1297db96d56Sopenharmony_ci
1307db96d56Sopenharmony_ci
1317db96d56Sopenharmony_citypedef BOOL (*PIsWow64Process2)(HANDLE, USHORT*, USHORT*);
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_ciUSHORT
1357db96d56Sopenharmony_ci_getNativeMachine()
1367db96d56Sopenharmony_ci{
1377db96d56Sopenharmony_ci    static USHORT _nativeMachine = IMAGE_FILE_MACHINE_UNKNOWN;
1387db96d56Sopenharmony_ci    if (_nativeMachine == IMAGE_FILE_MACHINE_UNKNOWN) {
1397db96d56Sopenharmony_ci        USHORT processMachine;
1407db96d56Sopenharmony_ci        HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
1417db96d56Sopenharmony_ci        PIsWow64Process2 IsWow64Process2 = kernel32 ?
1427db96d56Sopenharmony_ci            (PIsWow64Process2)GetProcAddress(kernel32, "IsWow64Process2") :
1437db96d56Sopenharmony_ci            NULL;
1447db96d56Sopenharmony_ci        if (!IsWow64Process2) {
1457db96d56Sopenharmony_ci            BOOL wow64Process;
1467db96d56Sopenharmony_ci            if (!IsWow64Process(NULL, &wow64Process)) {
1477db96d56Sopenharmony_ci                winerror(0, L"Checking process type");
1487db96d56Sopenharmony_ci            } else if (wow64Process) {
1497db96d56Sopenharmony_ci                // We should always be a 32-bit executable, so if running
1507db96d56Sopenharmony_ci                // under emulation, it must be a 64-bit host.
1517db96d56Sopenharmony_ci                _nativeMachine = IMAGE_FILE_MACHINE_AMD64;
1527db96d56Sopenharmony_ci            } else {
1537db96d56Sopenharmony_ci                // Not running under emulation, and an old enough OS to not
1547db96d56Sopenharmony_ci                // have IsWow64Process2, so assume it's x86.
1557db96d56Sopenharmony_ci                _nativeMachine = IMAGE_FILE_MACHINE_I386;
1567db96d56Sopenharmony_ci            }
1577db96d56Sopenharmony_ci        } else if (!IsWow64Process2(NULL, &processMachine, &_nativeMachine)) {
1587db96d56Sopenharmony_ci            winerror(0, L"Checking process type");
1597db96d56Sopenharmony_ci        }
1607db96d56Sopenharmony_ci    }
1617db96d56Sopenharmony_ci    return _nativeMachine;
1627db96d56Sopenharmony_ci}
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_cibool
1667db96d56Sopenharmony_ciisAMD64Host()
1677db96d56Sopenharmony_ci{
1687db96d56Sopenharmony_ci    return _getNativeMachine() == IMAGE_FILE_MACHINE_AMD64;
1697db96d56Sopenharmony_ci}
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_cibool
1737db96d56Sopenharmony_ciisARM64Host()
1747db96d56Sopenharmony_ci{
1757db96d56Sopenharmony_ci    return _getNativeMachine() == IMAGE_FILE_MACHINE_ARM64;
1767db96d56Sopenharmony_ci}
1777db96d56Sopenharmony_ci
1787db96d56Sopenharmony_ci
1797db96d56Sopenharmony_cibool
1807db96d56Sopenharmony_ciisEnvVarSet(const wchar_t *name)
1817db96d56Sopenharmony_ci{
1827db96d56Sopenharmony_ci    /* only looking for non-empty, which means at least one character
1837db96d56Sopenharmony_ci       and the null terminator */
1847db96d56Sopenharmony_ci    return GetEnvironmentVariableW(name, NULL, 0) >= 2;
1857db96d56Sopenharmony_ci}
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_ci
1887db96d56Sopenharmony_cibool
1897db96d56Sopenharmony_cijoin(wchar_t *buffer, size_t bufferLength, const wchar_t *fragment)
1907db96d56Sopenharmony_ci{
1917db96d56Sopenharmony_ci    if (SUCCEEDED(PathCchCombineEx(buffer, bufferLength, buffer, fragment, PATHCCH_ALLOW_LONG_PATHS))) {
1927db96d56Sopenharmony_ci        return true;
1937db96d56Sopenharmony_ci    }
1947db96d56Sopenharmony_ci    return false;
1957db96d56Sopenharmony_ci}
1967db96d56Sopenharmony_ci
1977db96d56Sopenharmony_ci
1987db96d56Sopenharmony_ciint
1997db96d56Sopenharmony_ci_compare(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
2007db96d56Sopenharmony_ci{
2017db96d56Sopenharmony_ci    // Empty strings sort first
2027db96d56Sopenharmony_ci    if (!x || !xLen) {
2037db96d56Sopenharmony_ci        return (!y || !yLen) ? 0 : -1;
2047db96d56Sopenharmony_ci    } else if (!y || !yLen) {
2057db96d56Sopenharmony_ci        return 1;
2067db96d56Sopenharmony_ci    }
2077db96d56Sopenharmony_ci    switch (CompareStringEx(
2087db96d56Sopenharmony_ci        LOCALE_NAME_INVARIANT, NORM_IGNORECASE | SORT_DIGITSASNUMBERS,
2097db96d56Sopenharmony_ci        x, xLen, y, yLen,
2107db96d56Sopenharmony_ci        NULL, NULL, 0
2117db96d56Sopenharmony_ci    )) {
2127db96d56Sopenharmony_ci    case CSTR_LESS_THAN:
2137db96d56Sopenharmony_ci        return -1;
2147db96d56Sopenharmony_ci    case CSTR_EQUAL:
2157db96d56Sopenharmony_ci        return 0;
2167db96d56Sopenharmony_ci    case CSTR_GREATER_THAN:
2177db96d56Sopenharmony_ci        return 1;
2187db96d56Sopenharmony_ci    default:
2197db96d56Sopenharmony_ci        winerror(0, L"Error comparing '%.*s' and '%.*s' (compare)", xLen, x, yLen, y);
2207db96d56Sopenharmony_ci        return -1;
2217db96d56Sopenharmony_ci    }
2227db96d56Sopenharmony_ci}
2237db96d56Sopenharmony_ci
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_ciint
2267db96d56Sopenharmony_ci_compareArgument(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
2277db96d56Sopenharmony_ci{
2287db96d56Sopenharmony_ci    // Empty strings sort first
2297db96d56Sopenharmony_ci    if (!x || !xLen) {
2307db96d56Sopenharmony_ci        return (!y || !yLen) ? 0 : -1;
2317db96d56Sopenharmony_ci    } else if (!y || !yLen) {
2327db96d56Sopenharmony_ci        return 1;
2337db96d56Sopenharmony_ci    }
2347db96d56Sopenharmony_ci    switch (CompareStringEx(
2357db96d56Sopenharmony_ci        LOCALE_NAME_INVARIANT, 0,
2367db96d56Sopenharmony_ci        x, xLen, y, yLen,
2377db96d56Sopenharmony_ci        NULL, NULL, 0
2387db96d56Sopenharmony_ci    )) {
2397db96d56Sopenharmony_ci    case CSTR_LESS_THAN:
2407db96d56Sopenharmony_ci        return -1;
2417db96d56Sopenharmony_ci    case CSTR_EQUAL:
2427db96d56Sopenharmony_ci        return 0;
2437db96d56Sopenharmony_ci    case CSTR_GREATER_THAN:
2447db96d56Sopenharmony_ci        return 1;
2457db96d56Sopenharmony_ci    default:
2467db96d56Sopenharmony_ci        winerror(0, L"Error comparing '%.*s' and '%.*s' (compareArgument)", xLen, x, yLen, y);
2477db96d56Sopenharmony_ci        return -1;
2487db96d56Sopenharmony_ci    }
2497db96d56Sopenharmony_ci}
2507db96d56Sopenharmony_ci
2517db96d56Sopenharmony_ciint
2527db96d56Sopenharmony_ci_comparePath(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
2537db96d56Sopenharmony_ci{
2547db96d56Sopenharmony_ci    // Empty strings sort first
2557db96d56Sopenharmony_ci    if (!x || !xLen) {
2567db96d56Sopenharmony_ci        return !y || !yLen ? 0 : -1;
2577db96d56Sopenharmony_ci    } else if (!y || !yLen) {
2587db96d56Sopenharmony_ci        return 1;
2597db96d56Sopenharmony_ci    }
2607db96d56Sopenharmony_ci    switch (CompareStringOrdinal(x, xLen, y, yLen, TRUE)) {
2617db96d56Sopenharmony_ci    case CSTR_LESS_THAN:
2627db96d56Sopenharmony_ci        return -1;
2637db96d56Sopenharmony_ci    case CSTR_EQUAL:
2647db96d56Sopenharmony_ci        return 0;
2657db96d56Sopenharmony_ci    case CSTR_GREATER_THAN:
2667db96d56Sopenharmony_ci        return 1;
2677db96d56Sopenharmony_ci    default:
2687db96d56Sopenharmony_ci        winerror(0, L"Error comparing '%.*s' and '%.*s' (comparePath)", xLen, x, yLen, y);
2697db96d56Sopenharmony_ci        return -1;
2707db96d56Sopenharmony_ci    }
2717db96d56Sopenharmony_ci}
2727db96d56Sopenharmony_ci
2737db96d56Sopenharmony_ci
2747db96d56Sopenharmony_cibool
2757db96d56Sopenharmony_ci_startsWith(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
2767db96d56Sopenharmony_ci{
2777db96d56Sopenharmony_ci    if (!x || !y) {
2787db96d56Sopenharmony_ci        return false;
2797db96d56Sopenharmony_ci    }
2807db96d56Sopenharmony_ci    yLen = yLen < 0 ? (int)wcsnlen_s(y, MAXLEN) : yLen;
2817db96d56Sopenharmony_ci    xLen = xLen < 0 ? (int)wcsnlen_s(x, MAXLEN) : xLen;
2827db96d56Sopenharmony_ci    return xLen >= yLen && 0 == _compare(x, yLen, y, yLen);
2837db96d56Sopenharmony_ci}
2847db96d56Sopenharmony_ci
2857db96d56Sopenharmony_ci
2867db96d56Sopenharmony_cibool
2877db96d56Sopenharmony_ci_startsWithArgument(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
2887db96d56Sopenharmony_ci{
2897db96d56Sopenharmony_ci    if (!x || !y) {
2907db96d56Sopenharmony_ci        return false;
2917db96d56Sopenharmony_ci    }
2927db96d56Sopenharmony_ci    yLen = yLen < 0 ? (int)wcsnlen_s(y, MAXLEN) : yLen;
2937db96d56Sopenharmony_ci    xLen = xLen < 0 ? (int)wcsnlen_s(x, MAXLEN) : xLen;
2947db96d56Sopenharmony_ci    return xLen >= yLen && 0 == _compareArgument(x, yLen, y, yLen);
2957db96d56Sopenharmony_ci}
2967db96d56Sopenharmony_ci
2977db96d56Sopenharmony_ci
2987db96d56Sopenharmony_ci// Unlike regular startsWith, this function requires that the following
2997db96d56Sopenharmony_ci// character is either NULL (that is, the entire string matches) or is one of
3007db96d56Sopenharmony_ci// the characters in 'separators'.
3017db96d56Sopenharmony_cibool
3027db96d56Sopenharmony_ci_startsWithSeparated(const wchar_t *x, int xLen, const wchar_t *y, int yLen, const wchar_t *separators)
3037db96d56Sopenharmony_ci{
3047db96d56Sopenharmony_ci    if (!x || !y) {
3057db96d56Sopenharmony_ci        return false;
3067db96d56Sopenharmony_ci    }
3077db96d56Sopenharmony_ci    yLen = yLen < 0 ? (int)wcsnlen_s(y, MAXLEN) : yLen;
3087db96d56Sopenharmony_ci    xLen = xLen < 0 ? (int)wcsnlen_s(x, MAXLEN) : xLen;
3097db96d56Sopenharmony_ci    if (xLen < yLen) {
3107db96d56Sopenharmony_ci        return false;
3117db96d56Sopenharmony_ci    }
3127db96d56Sopenharmony_ci    if (xLen == yLen) {
3137db96d56Sopenharmony_ci        return 0 == _compare(x, xLen, y, yLen);
3147db96d56Sopenharmony_ci    }
3157db96d56Sopenharmony_ci    return separators &&
3167db96d56Sopenharmony_ci        0 == _compare(x, yLen, y, yLen) &&
3177db96d56Sopenharmony_ci        wcschr(separators, x[yLen]) != NULL;
3187db96d56Sopenharmony_ci}
3197db96d56Sopenharmony_ci
3207db96d56Sopenharmony_ci
3217db96d56Sopenharmony_ci
3227db96d56Sopenharmony_ci/******************************************************************************\
3237db96d56Sopenharmony_ci ***                               HELP TEXT                                ***
3247db96d56Sopenharmony_ci\******************************************************************************/
3257db96d56Sopenharmony_ci
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ciint
3287db96d56Sopenharmony_cishowHelpText(wchar_t ** argv)
3297db96d56Sopenharmony_ci{
3307db96d56Sopenharmony_ci    // The help text is stored in launcher-usage.txt, which is compiled into
3317db96d56Sopenharmony_ci    // the launcher and loaded at runtime if needed.
3327db96d56Sopenharmony_ci    //
3337db96d56Sopenharmony_ci    // The file must be UTF-8. There are two substitutions:
3347db96d56Sopenharmony_ci    //  %ls - PY_VERSION (as wchar_t*)
3357db96d56Sopenharmony_ci    //  %ls - argv[0] (as wchar_t*)
3367db96d56Sopenharmony_ci    HRSRC res = FindResourceExW(NULL, L"USAGE", MAKEINTRESOURCE(1), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
3377db96d56Sopenharmony_ci    HGLOBAL resData = res ? LoadResource(NULL, res) : NULL;
3387db96d56Sopenharmony_ci    const char *usage = resData ? (const char*)LockResource(resData) : NULL;
3397db96d56Sopenharmony_ci    if (usage == NULL) {
3407db96d56Sopenharmony_ci        winerror(0, L"Unable to load usage text");
3417db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
3427db96d56Sopenharmony_ci    }
3437db96d56Sopenharmony_ci
3447db96d56Sopenharmony_ci    DWORD cbData = SizeofResource(NULL, res);
3457db96d56Sopenharmony_ci    DWORD cchUsage = MultiByteToWideChar(CP_UTF8, 0, usage, cbData, NULL, 0);
3467db96d56Sopenharmony_ci    if (!cchUsage) {
3477db96d56Sopenharmony_ci        winerror(0, L"Unable to preprocess usage text");
3487db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
3497db96d56Sopenharmony_ci    }
3507db96d56Sopenharmony_ci
3517db96d56Sopenharmony_ci    cchUsage += 1;
3527db96d56Sopenharmony_ci    wchar_t *wUsage = (wchar_t*)malloc(cchUsage * sizeof(wchar_t));
3537db96d56Sopenharmony_ci    cchUsage = MultiByteToWideChar(CP_UTF8, 0, usage, cbData, wUsage, cchUsage);
3547db96d56Sopenharmony_ci    if (!cchUsage) {
3557db96d56Sopenharmony_ci        winerror(0, L"Unable to preprocess usage text");
3567db96d56Sopenharmony_ci        free((void *)wUsage);
3577db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
3587db96d56Sopenharmony_ci    }
3597db96d56Sopenharmony_ci    // Ensure null termination
3607db96d56Sopenharmony_ci    wUsage[cchUsage] = L'\0';
3617db96d56Sopenharmony_ci
3627db96d56Sopenharmony_ci    fwprintf(stdout, wUsage, (L"" PY_VERSION), argv[0]);
3637db96d56Sopenharmony_ci    fflush(stdout);
3647db96d56Sopenharmony_ci
3657db96d56Sopenharmony_ci    free((void *)wUsage);
3667db96d56Sopenharmony_ci
3677db96d56Sopenharmony_ci    return 0;
3687db96d56Sopenharmony_ci}
3697db96d56Sopenharmony_ci
3707db96d56Sopenharmony_ci
3717db96d56Sopenharmony_ci/******************************************************************************\
3727db96d56Sopenharmony_ci ***                              SEARCH INFO                               ***
3737db96d56Sopenharmony_ci\******************************************************************************/
3747db96d56Sopenharmony_ci
3757db96d56Sopenharmony_ci
3767db96d56Sopenharmony_cistruct _SearchInfoBuffer {
3777db96d56Sopenharmony_ci    struct _SearchInfoBuffer *next;
3787db96d56Sopenharmony_ci    wchar_t buffer[0];
3797db96d56Sopenharmony_ci};
3807db96d56Sopenharmony_ci
3817db96d56Sopenharmony_ci
3827db96d56Sopenharmony_citypedef struct {
3837db96d56Sopenharmony_ci    // the original string, managed by the OS
3847db96d56Sopenharmony_ci    const wchar_t *originalCmdLine;
3857db96d56Sopenharmony_ci    // pointer into the cmdline to mark what we've consumed
3867db96d56Sopenharmony_ci    const wchar_t *restOfCmdLine;
3877db96d56Sopenharmony_ci    // if known/discovered, the full executable path of our runtime
3887db96d56Sopenharmony_ci    const wchar_t *executablePath;
3897db96d56Sopenharmony_ci    // pointer and length into cmdline for the file to check for a
3907db96d56Sopenharmony_ci    // shebang line, if any. Length can be -1 if the string is null
3917db96d56Sopenharmony_ci    // terminated.
3927db96d56Sopenharmony_ci    const wchar_t *scriptFile;
3937db96d56Sopenharmony_ci    int scriptFileLength;
3947db96d56Sopenharmony_ci    // pointer and length into cmdline or a static string with the
3957db96d56Sopenharmony_ci    // name of the target executable. Length can be -1 if the string
3967db96d56Sopenharmony_ci    // is null terminated.
3977db96d56Sopenharmony_ci    const wchar_t *executable;
3987db96d56Sopenharmony_ci    int executableLength;
3997db96d56Sopenharmony_ci    // pointer and length into a string with additional interpreter
4007db96d56Sopenharmony_ci    // arguments to include before restOfCmdLine. Length can be -1 if
4017db96d56Sopenharmony_ci    // the string is null terminated.
4027db96d56Sopenharmony_ci    const wchar_t *executableArgs;
4037db96d56Sopenharmony_ci    int executableArgsLength;
4047db96d56Sopenharmony_ci    // pointer and length into cmdline or a static string with the
4057db96d56Sopenharmony_ci    // company name for PEP 514 lookup. Length can be -1 if the string
4067db96d56Sopenharmony_ci    // is null terminated.
4077db96d56Sopenharmony_ci    const wchar_t *company;
4087db96d56Sopenharmony_ci    int companyLength;
4097db96d56Sopenharmony_ci    // pointer and length into cmdline or a static string with the
4107db96d56Sopenharmony_ci    // tag for PEP 514 lookup. Length can be -1 if the string is
4117db96d56Sopenharmony_ci    // null terminated.
4127db96d56Sopenharmony_ci    const wchar_t *tag;
4137db96d56Sopenharmony_ci    int tagLength;
4147db96d56Sopenharmony_ci    // if true, treats 'tag' as a non-PEP 514 filter
4157db96d56Sopenharmony_ci    bool oldStyleTag;
4167db96d56Sopenharmony_ci    // if true, ignores 'tag' when a high priority environment is found
4177db96d56Sopenharmony_ci    // gh-92817: This is currently set when a tag is read from configuration or
4187db96d56Sopenharmony_ci    // the environment, rather than the command line or a shebang line, and the
4197db96d56Sopenharmony_ci    // only currently possible high priority environment is an active virtual
4207db96d56Sopenharmony_ci    // environment
4217db96d56Sopenharmony_ci    bool lowPriorityTag;
4227db96d56Sopenharmony_ci    // if true, allow PEP 514 lookup to override 'executable'
4237db96d56Sopenharmony_ci    bool allowExecutableOverride;
4247db96d56Sopenharmony_ci    // if true, allow a nearby pyvenv.cfg to locate the executable
4257db96d56Sopenharmony_ci    bool allowPyvenvCfg;
4267db96d56Sopenharmony_ci    // if true, allow defaults (env/py.ini) to clarify/override tags
4277db96d56Sopenharmony_ci    bool allowDefaults;
4287db96d56Sopenharmony_ci    // if true, prefer windowed (console-less) executable
4297db96d56Sopenharmony_ci    bool windowed;
4307db96d56Sopenharmony_ci    // if true, only list detected runtimes without launching
4317db96d56Sopenharmony_ci    bool list;
4327db96d56Sopenharmony_ci    // if true, only list detected runtimes with paths without launching
4337db96d56Sopenharmony_ci    bool listPaths;
4347db96d56Sopenharmony_ci    // if true, display help message before contiuning
4357db96d56Sopenharmony_ci    bool help;
4367db96d56Sopenharmony_ci    // if set, limits search to registry keys with the specified Company
4377db96d56Sopenharmony_ci    // This is intended for debugging and testing only
4387db96d56Sopenharmony_ci    const wchar_t *limitToCompany;
4397db96d56Sopenharmony_ci    // dynamically allocated buffers to free later
4407db96d56Sopenharmony_ci    struct _SearchInfoBuffer *_buffer;
4417db96d56Sopenharmony_ci} SearchInfo;
4427db96d56Sopenharmony_ci
4437db96d56Sopenharmony_ci
4447db96d56Sopenharmony_ciwchar_t *
4457db96d56Sopenharmony_ciallocSearchInfoBuffer(SearchInfo *search, int wcharCount)
4467db96d56Sopenharmony_ci{
4477db96d56Sopenharmony_ci    struct _SearchInfoBuffer *buffer = (struct _SearchInfoBuffer*)malloc(
4487db96d56Sopenharmony_ci        sizeof(struct _SearchInfoBuffer) +
4497db96d56Sopenharmony_ci        wcharCount * sizeof(wchar_t)
4507db96d56Sopenharmony_ci    );
4517db96d56Sopenharmony_ci    if (!buffer) {
4527db96d56Sopenharmony_ci        return NULL;
4537db96d56Sopenharmony_ci    }
4547db96d56Sopenharmony_ci    buffer->next = search->_buffer;
4557db96d56Sopenharmony_ci    search->_buffer = buffer;
4567db96d56Sopenharmony_ci    return buffer->buffer;
4577db96d56Sopenharmony_ci}
4587db96d56Sopenharmony_ci
4597db96d56Sopenharmony_ci
4607db96d56Sopenharmony_civoid
4617db96d56Sopenharmony_cifreeSearchInfo(SearchInfo *search)
4627db96d56Sopenharmony_ci{
4637db96d56Sopenharmony_ci    struct _SearchInfoBuffer *b = search->_buffer;
4647db96d56Sopenharmony_ci    search->_buffer = NULL;
4657db96d56Sopenharmony_ci    while (b) {
4667db96d56Sopenharmony_ci        struct _SearchInfoBuffer *nextB = b->next;
4677db96d56Sopenharmony_ci        free((void *)b);
4687db96d56Sopenharmony_ci        b = nextB;
4697db96d56Sopenharmony_ci    }
4707db96d56Sopenharmony_ci}
4717db96d56Sopenharmony_ci
4727db96d56Sopenharmony_ci
4737db96d56Sopenharmony_civoid
4747db96d56Sopenharmony_ci_debugStringAndLength(const wchar_t *s, int len, const wchar_t *name)
4757db96d56Sopenharmony_ci{
4767db96d56Sopenharmony_ci    if (!s) {
4777db96d56Sopenharmony_ci        debug(L"%s: (null)\n", name);
4787db96d56Sopenharmony_ci    } else if (len == 0) {
4797db96d56Sopenharmony_ci        debug(L"%s: (empty)\n", name);
4807db96d56Sopenharmony_ci    } else if (len < 0) {
4817db96d56Sopenharmony_ci        debug(L"%s: %s\n", name, s);
4827db96d56Sopenharmony_ci    } else {
4837db96d56Sopenharmony_ci        debug(L"%s: %.*ls\n", name, len, s);
4847db96d56Sopenharmony_ci    }
4857db96d56Sopenharmony_ci}
4867db96d56Sopenharmony_ci
4877db96d56Sopenharmony_ci
4887db96d56Sopenharmony_civoid
4897db96d56Sopenharmony_cidumpSearchInfo(SearchInfo *search)
4907db96d56Sopenharmony_ci{
4917db96d56Sopenharmony_ci    if (!log_fp) {
4927db96d56Sopenharmony_ci        return;
4937db96d56Sopenharmony_ci    }
4947db96d56Sopenharmony_ci
4957db96d56Sopenharmony_ci#define DEBUGNAME(s) L"SearchInfo." ## s
4967db96d56Sopenharmony_ci#define DEBUG(s) debug(DEBUGNAME(#s) L": %s\n", (search->s) ? (search->s) : L"(null)")
4977db96d56Sopenharmony_ci#define DEBUG_2(s, sl) _debugStringAndLength((search->s), (search->sl), DEBUGNAME(#s))
4987db96d56Sopenharmony_ci#define DEBUG_BOOL(s) debug(DEBUGNAME(#s) L": %s\n", (search->s) ? L"True" : L"False")
4997db96d56Sopenharmony_ci    DEBUG(originalCmdLine);
5007db96d56Sopenharmony_ci    DEBUG(restOfCmdLine);
5017db96d56Sopenharmony_ci    DEBUG(executablePath);
5027db96d56Sopenharmony_ci    DEBUG_2(scriptFile, scriptFileLength);
5037db96d56Sopenharmony_ci    DEBUG_2(executable, executableLength);
5047db96d56Sopenharmony_ci    DEBUG_2(executableArgs, executableArgsLength);
5057db96d56Sopenharmony_ci    DEBUG_2(company, companyLength);
5067db96d56Sopenharmony_ci    DEBUG_2(tag, tagLength);
5077db96d56Sopenharmony_ci    DEBUG_BOOL(oldStyleTag);
5087db96d56Sopenharmony_ci    DEBUG_BOOL(lowPriorityTag);
5097db96d56Sopenharmony_ci    DEBUG_BOOL(allowDefaults);
5107db96d56Sopenharmony_ci    DEBUG_BOOL(allowExecutableOverride);
5117db96d56Sopenharmony_ci    DEBUG_BOOL(windowed);
5127db96d56Sopenharmony_ci    DEBUG_BOOL(list);
5137db96d56Sopenharmony_ci    DEBUG_BOOL(listPaths);
5147db96d56Sopenharmony_ci    DEBUG_BOOL(help);
5157db96d56Sopenharmony_ci    DEBUG(limitToCompany);
5167db96d56Sopenharmony_ci#undef DEBUG_BOOL
5177db96d56Sopenharmony_ci#undef DEBUG_2
5187db96d56Sopenharmony_ci#undef DEBUG
5197db96d56Sopenharmony_ci#undef DEBUGNAME
5207db96d56Sopenharmony_ci}
5217db96d56Sopenharmony_ci
5227db96d56Sopenharmony_ci
5237db96d56Sopenharmony_ciint
5247db96d56Sopenharmony_cifindArgv0Length(const wchar_t *buffer, int bufferLength)
5257db96d56Sopenharmony_ci{
5267db96d56Sopenharmony_ci    // Note: this implements semantics that are only valid for argv0.
5277db96d56Sopenharmony_ci    // Specifically, there is no escaping of quotes, and quotes within
5287db96d56Sopenharmony_ci    // the argument have no effect. A quoted argv0 must start and end
5297db96d56Sopenharmony_ci    // with a double quote character; otherwise, it ends at the first
5307db96d56Sopenharmony_ci    // ' ' or '\t'.
5317db96d56Sopenharmony_ci    int quoted = buffer[0] == L'"';
5327db96d56Sopenharmony_ci    for (int i = 1; bufferLength < 0 || i < bufferLength; ++i) {
5337db96d56Sopenharmony_ci        switch (buffer[i]) {
5347db96d56Sopenharmony_ci        case L'\0':
5357db96d56Sopenharmony_ci            return i;
5367db96d56Sopenharmony_ci        case L' ':
5377db96d56Sopenharmony_ci        case L'\t':
5387db96d56Sopenharmony_ci            if (!quoted) {
5397db96d56Sopenharmony_ci                return i;
5407db96d56Sopenharmony_ci            }
5417db96d56Sopenharmony_ci            break;
5427db96d56Sopenharmony_ci        case L'"':
5437db96d56Sopenharmony_ci            if (quoted) {
5447db96d56Sopenharmony_ci                return i + 1;
5457db96d56Sopenharmony_ci            }
5467db96d56Sopenharmony_ci            break;
5477db96d56Sopenharmony_ci        }
5487db96d56Sopenharmony_ci    }
5497db96d56Sopenharmony_ci    return bufferLength;
5507db96d56Sopenharmony_ci}
5517db96d56Sopenharmony_ci
5527db96d56Sopenharmony_ci
5537db96d56Sopenharmony_ciconst wchar_t *
5547db96d56Sopenharmony_cifindArgv0End(const wchar_t *buffer, int bufferLength)
5557db96d56Sopenharmony_ci{
5567db96d56Sopenharmony_ci    return &buffer[findArgv0Length(buffer, bufferLength)];
5577db96d56Sopenharmony_ci}
5587db96d56Sopenharmony_ci
5597db96d56Sopenharmony_ci
5607db96d56Sopenharmony_ci/******************************************************************************\
5617db96d56Sopenharmony_ci ***                          COMMAND-LINE PARSING                          ***
5627db96d56Sopenharmony_ci\******************************************************************************/
5637db96d56Sopenharmony_ci
5647db96d56Sopenharmony_ci
5657db96d56Sopenharmony_ciint
5667db96d56Sopenharmony_ciparseCommandLine(SearchInfo *search)
5677db96d56Sopenharmony_ci{
5687db96d56Sopenharmony_ci    if (!search || !search->originalCmdLine) {
5697db96d56Sopenharmony_ci        return RC_NO_COMMANDLINE;
5707db96d56Sopenharmony_ci    }
5717db96d56Sopenharmony_ci
5727db96d56Sopenharmony_ci    const wchar_t *argv0End = findArgv0End(search->originalCmdLine, -1);
5737db96d56Sopenharmony_ci    const wchar_t *tail = argv0End; // will be start of the executable name
5747db96d56Sopenharmony_ci    const wchar_t *end = argv0End;  // will be end of the executable name
5757db96d56Sopenharmony_ci    search->restOfCmdLine = argv0End;   // will be first space after argv0
5767db96d56Sopenharmony_ci    while (--tail != search->originalCmdLine) {
5777db96d56Sopenharmony_ci        if (*tail == L'"' && end == argv0End) {
5787db96d56Sopenharmony_ci            // Move the "end" up to the quote, so we also allow moving for
5797db96d56Sopenharmony_ci            // a period later on.
5807db96d56Sopenharmony_ci            end = argv0End = tail;
5817db96d56Sopenharmony_ci        } else if (*tail == L'.' && end == argv0End) {
5827db96d56Sopenharmony_ci            end = tail;
5837db96d56Sopenharmony_ci        } else if (*tail == L'\\' || *tail == L'/') {
5847db96d56Sopenharmony_ci            ++tail;
5857db96d56Sopenharmony_ci            break;
5867db96d56Sopenharmony_ci        }
5877db96d56Sopenharmony_ci    }
5887db96d56Sopenharmony_ci    if (tail == search->originalCmdLine && tail[0] == L'"') {
5897db96d56Sopenharmony_ci        ++tail;
5907db96d56Sopenharmony_ci    }
5917db96d56Sopenharmony_ci    // Without special cases, we can now fill in the search struct
5927db96d56Sopenharmony_ci    int tailLen = (int)(end ? (end - tail) : wcsnlen_s(tail, MAXLEN));
5937db96d56Sopenharmony_ci    search->executableLength = -1;
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci    // Our special cases are as follows
5967db96d56Sopenharmony_ci#define MATCHES(s) (0 == _comparePath(tail, tailLen, (s), -1))
5977db96d56Sopenharmony_ci#define STARTSWITH(s) _startsWith(tail, tailLen, (s), -1)
5987db96d56Sopenharmony_ci    if (MATCHES(L"py")) {
5997db96d56Sopenharmony_ci        search->executable = L"python.exe";
6007db96d56Sopenharmony_ci        search->allowExecutableOverride = true;
6017db96d56Sopenharmony_ci        search->allowDefaults = true;
6027db96d56Sopenharmony_ci    } else if (MATCHES(L"pyw")) {
6037db96d56Sopenharmony_ci        search->executable = L"pythonw.exe";
6047db96d56Sopenharmony_ci        search->allowExecutableOverride = true;
6057db96d56Sopenharmony_ci        search->allowDefaults = true;
6067db96d56Sopenharmony_ci        search->windowed = true;
6077db96d56Sopenharmony_ci    } else if (MATCHES(L"py_d")) {
6087db96d56Sopenharmony_ci        search->executable = L"python_d.exe";
6097db96d56Sopenharmony_ci        search->allowExecutableOverride = true;
6107db96d56Sopenharmony_ci        search->allowDefaults = true;
6117db96d56Sopenharmony_ci    } else if (MATCHES(L"pyw_d")) {
6127db96d56Sopenharmony_ci        search->executable = L"pythonw_d.exe";
6137db96d56Sopenharmony_ci        search->allowExecutableOverride = true;
6147db96d56Sopenharmony_ci        search->allowDefaults = true;
6157db96d56Sopenharmony_ci        search->windowed = true;
6167db96d56Sopenharmony_ci    } else if (STARTSWITH(L"python3")) {
6177db96d56Sopenharmony_ci        search->executable = L"python.exe";
6187db96d56Sopenharmony_ci        search->tag = &tail[6];
6197db96d56Sopenharmony_ci        search->tagLength = tailLen - 6;
6207db96d56Sopenharmony_ci        search->allowExecutableOverride = true;
6217db96d56Sopenharmony_ci        search->oldStyleTag = true;
6227db96d56Sopenharmony_ci        search->allowPyvenvCfg = true;
6237db96d56Sopenharmony_ci    } else if (STARTSWITH(L"pythonw3")) {
6247db96d56Sopenharmony_ci        search->executable = L"pythonw.exe";
6257db96d56Sopenharmony_ci        search->tag = &tail[7];
6267db96d56Sopenharmony_ci        search->tagLength = tailLen - 7;
6277db96d56Sopenharmony_ci        search->allowExecutableOverride = true;
6287db96d56Sopenharmony_ci        search->oldStyleTag = true;
6297db96d56Sopenharmony_ci        search->allowPyvenvCfg = true;
6307db96d56Sopenharmony_ci        search->windowed = true;
6317db96d56Sopenharmony_ci    } else {
6327db96d56Sopenharmony_ci        search->executable = tail;
6337db96d56Sopenharmony_ci        search->executableLength = tailLen;
6347db96d56Sopenharmony_ci        search->allowPyvenvCfg = true;
6357db96d56Sopenharmony_ci    }
6367db96d56Sopenharmony_ci#undef STARTSWITH
6377db96d56Sopenharmony_ci#undef MATCHES
6387db96d56Sopenharmony_ci
6397db96d56Sopenharmony_ci    // First argument might be one of our options. If so, consume it,
6407db96d56Sopenharmony_ci    // update flags and then set restOfCmdLine.
6417db96d56Sopenharmony_ci    const wchar_t *arg = search->restOfCmdLine;
6427db96d56Sopenharmony_ci    while(*arg && isspace(*arg)) { ++arg; }
6437db96d56Sopenharmony_ci#define MATCHES(s) (0 == _compareArgument(arg, argLen, (s), -1))
6447db96d56Sopenharmony_ci#define STARTSWITH(s) _startsWithArgument(arg, argLen, (s), -1)
6457db96d56Sopenharmony_ci    if (*arg && *arg == L'-' && *++arg) {
6467db96d56Sopenharmony_ci        tail = arg;
6477db96d56Sopenharmony_ci        while (*tail && !isspace(*tail)) { ++tail; }
6487db96d56Sopenharmony_ci        int argLen = (int)(tail - arg);
6497db96d56Sopenharmony_ci        if (argLen > 0) {
6507db96d56Sopenharmony_ci            if (STARTSWITH(L"2") || STARTSWITH(L"3")) {
6517db96d56Sopenharmony_ci                // All arguments starting with 2 or 3 are assumed to be version tags
6527db96d56Sopenharmony_ci                search->tag = arg;
6537db96d56Sopenharmony_ci                search->tagLength = argLen;
6547db96d56Sopenharmony_ci                search->oldStyleTag = true;
6557db96d56Sopenharmony_ci                search->restOfCmdLine = tail;
6567db96d56Sopenharmony_ci            } else if (STARTSWITH(L"V:") || STARTSWITH(L"-version:")) {
6577db96d56Sopenharmony_ci                // Arguments starting with 'V:' specify company and/or tag
6587db96d56Sopenharmony_ci                const wchar_t *argStart = wcschr(arg, L':') + 1;
6597db96d56Sopenharmony_ci                const wchar_t *tagStart = wcschr(argStart, L'/') ;
6607db96d56Sopenharmony_ci                if (tagStart) {
6617db96d56Sopenharmony_ci                    search->company = argStart;
6627db96d56Sopenharmony_ci                    search->companyLength = (int)(tagStart - argStart);
6637db96d56Sopenharmony_ci                    search->tag = tagStart + 1;
6647db96d56Sopenharmony_ci                } else {
6657db96d56Sopenharmony_ci                    search->tag = argStart;
6667db96d56Sopenharmony_ci                }
6677db96d56Sopenharmony_ci                search->tagLength = (int)(tail - search->tag);
6687db96d56Sopenharmony_ci                search->allowDefaults = false;
6697db96d56Sopenharmony_ci                search->restOfCmdLine = tail;
6707db96d56Sopenharmony_ci            } else if (MATCHES(L"0") || MATCHES(L"-list")) {
6717db96d56Sopenharmony_ci                search->list = true;
6727db96d56Sopenharmony_ci                search->restOfCmdLine = tail;
6737db96d56Sopenharmony_ci            } else if (MATCHES(L"0p") || MATCHES(L"-list-paths")) {
6747db96d56Sopenharmony_ci                search->listPaths = true;
6757db96d56Sopenharmony_ci                search->restOfCmdLine = tail;
6767db96d56Sopenharmony_ci            } else if (MATCHES(L"h") || MATCHES(L"-help")) {
6777db96d56Sopenharmony_ci                search->help = true;
6787db96d56Sopenharmony_ci                // Do not update restOfCmdLine so that we trigger the help
6797db96d56Sopenharmony_ci                // message from whichever interpreter we select
6807db96d56Sopenharmony_ci            }
6817db96d56Sopenharmony_ci        }
6827db96d56Sopenharmony_ci    }
6837db96d56Sopenharmony_ci#undef STARTSWITH
6847db96d56Sopenharmony_ci#undef MATCHES
6857db96d56Sopenharmony_ci
6867db96d56Sopenharmony_ci    // Might have a script filename. If it looks like a filename, add
6877db96d56Sopenharmony_ci    // it to the SearchInfo struct for later reference.
6887db96d56Sopenharmony_ci    arg = search->restOfCmdLine;
6897db96d56Sopenharmony_ci    while(*arg && isspace(*arg)) { ++arg; }
6907db96d56Sopenharmony_ci    if (*arg && *arg != L'-') {
6917db96d56Sopenharmony_ci        search->scriptFile = arg;
6927db96d56Sopenharmony_ci        if (*arg == L'"') {
6937db96d56Sopenharmony_ci            ++search->scriptFile;
6947db96d56Sopenharmony_ci            while (*++arg && *arg != L'"') { }
6957db96d56Sopenharmony_ci        } else {
6967db96d56Sopenharmony_ci            while (*arg && !isspace(*arg)) { ++arg; }
6977db96d56Sopenharmony_ci        }
6987db96d56Sopenharmony_ci        search->scriptFileLength = (int)(arg - search->scriptFile);
6997db96d56Sopenharmony_ci    }
7007db96d56Sopenharmony_ci
7017db96d56Sopenharmony_ci    return 0;
7027db96d56Sopenharmony_ci}
7037db96d56Sopenharmony_ci
7047db96d56Sopenharmony_ci
7057db96d56Sopenharmony_ciint
7067db96d56Sopenharmony_ci_decodeShebang(SearchInfo *search, const char *buffer, int bufferLength, bool onlyUtf8, wchar_t **decoded, int *decodedLength)
7077db96d56Sopenharmony_ci{
7087db96d56Sopenharmony_ci    DWORD cp = CP_UTF8;
7097db96d56Sopenharmony_ci    int wideLen = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, buffer, bufferLength, NULL, 0);
7107db96d56Sopenharmony_ci    if (!wideLen) {
7117db96d56Sopenharmony_ci        cp = CP_ACP;
7127db96d56Sopenharmony_ci        wideLen = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, buffer, bufferLength, NULL, 0);
7137db96d56Sopenharmony_ci        if (!wideLen) {
7147db96d56Sopenharmony_ci            debug(L"# Failed to decode shebang line (0x%08X)\n", GetLastError());
7157db96d56Sopenharmony_ci            return RC_BAD_VIRTUAL_PATH;
7167db96d56Sopenharmony_ci        }
7177db96d56Sopenharmony_ci    }
7187db96d56Sopenharmony_ci    wchar_t *b = allocSearchInfoBuffer(search, wideLen + 1);
7197db96d56Sopenharmony_ci    if (!b) {
7207db96d56Sopenharmony_ci        return RC_NO_MEMORY;
7217db96d56Sopenharmony_ci    }
7227db96d56Sopenharmony_ci    wideLen = MultiByteToWideChar(cp, 0, buffer, bufferLength, b, wideLen + 1);
7237db96d56Sopenharmony_ci    if (!wideLen) {
7247db96d56Sopenharmony_ci        debug(L"# Failed to decode shebang line (0x%08X)\n", GetLastError());
7257db96d56Sopenharmony_ci        return RC_BAD_VIRTUAL_PATH;
7267db96d56Sopenharmony_ci    }
7277db96d56Sopenharmony_ci    b[wideLen] = L'\0';
7287db96d56Sopenharmony_ci    *decoded = b;
7297db96d56Sopenharmony_ci    *decodedLength = wideLen;
7307db96d56Sopenharmony_ci    return 0;
7317db96d56Sopenharmony_ci}
7327db96d56Sopenharmony_ci
7337db96d56Sopenharmony_ci
7347db96d56Sopenharmony_cibool
7357db96d56Sopenharmony_ci_shebangStartsWith(const wchar_t *buffer, int bufferLength, const wchar_t *prefix, const wchar_t **rest, int *firstArgumentLength)
7367db96d56Sopenharmony_ci{
7377db96d56Sopenharmony_ci    int prefixLength = (int)wcsnlen_s(prefix, MAXLEN);
7387db96d56Sopenharmony_ci    if (bufferLength < prefixLength || !_startsWithArgument(buffer, bufferLength, prefix, prefixLength)) {
7397db96d56Sopenharmony_ci        return false;
7407db96d56Sopenharmony_ci    }
7417db96d56Sopenharmony_ci    if (rest) {
7427db96d56Sopenharmony_ci        *rest = &buffer[prefixLength];
7437db96d56Sopenharmony_ci    }
7447db96d56Sopenharmony_ci    if (firstArgumentLength) {
7457db96d56Sopenharmony_ci        int i = prefixLength;
7467db96d56Sopenharmony_ci        while (i < bufferLength && !isspace(buffer[i])) {
7477db96d56Sopenharmony_ci            i += 1;
7487db96d56Sopenharmony_ci        }
7497db96d56Sopenharmony_ci        *firstArgumentLength = i - prefixLength;
7507db96d56Sopenharmony_ci    }
7517db96d56Sopenharmony_ci    return true;
7527db96d56Sopenharmony_ci}
7537db96d56Sopenharmony_ci
7547db96d56Sopenharmony_ci
7557db96d56Sopenharmony_ciint
7567db96d56Sopenharmony_cisearchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength)
7577db96d56Sopenharmony_ci{
7587db96d56Sopenharmony_ci    if (isEnvVarSet(L"PYLAUNCHER_NO_SEARCH_PATH")) {
7597db96d56Sopenharmony_ci        return RC_NO_SHEBANG;
7607db96d56Sopenharmony_ci    }
7617db96d56Sopenharmony_ci
7627db96d56Sopenharmony_ci    wchar_t *command;
7637db96d56Sopenharmony_ci    int commandLength;
7647db96d56Sopenharmony_ci    if (!_shebangStartsWith(shebang, shebangLength, L"/usr/bin/env ", &command, &commandLength)) {
7657db96d56Sopenharmony_ci        return RC_NO_SHEBANG;
7667db96d56Sopenharmony_ci    }
7677db96d56Sopenharmony_ci
7687db96d56Sopenharmony_ci    if (!commandLength || commandLength == MAXLEN) {
7697db96d56Sopenharmony_ci        return RC_BAD_VIRTUAL_PATH;
7707db96d56Sopenharmony_ci    }
7717db96d56Sopenharmony_ci
7727db96d56Sopenharmony_ci    int lastDot = commandLength;
7737db96d56Sopenharmony_ci    while (lastDot > 0 && command[lastDot] != L'.') {
7747db96d56Sopenharmony_ci        lastDot -= 1;
7757db96d56Sopenharmony_ci    }
7767db96d56Sopenharmony_ci    if (!lastDot) {
7777db96d56Sopenharmony_ci        lastDot = commandLength;
7787db96d56Sopenharmony_ci    }
7797db96d56Sopenharmony_ci
7807db96d56Sopenharmony_ci    wchar_t filename[MAXLEN];
7817db96d56Sopenharmony_ci    if (wcsncpy_s(filename, MAXLEN, command, lastDot)) {
7827db96d56Sopenharmony_ci        return RC_BAD_VIRTUAL_PATH;
7837db96d56Sopenharmony_ci    }
7847db96d56Sopenharmony_ci
7857db96d56Sopenharmony_ci    const wchar_t *ext = L".exe";
7867db96d56Sopenharmony_ci    // If the command already has an extension, we do not want to add it again
7877db96d56Sopenharmony_ci    if (!lastDot || _comparePath(&filename[lastDot], -1, ext, -1)) {
7887db96d56Sopenharmony_ci        if (wcscat_s(filename, MAXLEN, L".exe")) {
7897db96d56Sopenharmony_ci            return RC_BAD_VIRTUAL_PATH;
7907db96d56Sopenharmony_ci        }
7917db96d56Sopenharmony_ci    }
7927db96d56Sopenharmony_ci
7937db96d56Sopenharmony_ci    wchar_t pathVariable[MAXLEN];
7947db96d56Sopenharmony_ci    int n = GetEnvironmentVariableW(L"PATH", pathVariable, MAXLEN);
7957db96d56Sopenharmony_ci    if (!n) {
7967db96d56Sopenharmony_ci        if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
7977db96d56Sopenharmony_ci            return RC_NO_SHEBANG;
7987db96d56Sopenharmony_ci        }
7997db96d56Sopenharmony_ci        winerror(0, L"Failed to read PATH\n", filename);
8007db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
8017db96d56Sopenharmony_ci    }
8027db96d56Sopenharmony_ci
8037db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
8047db96d56Sopenharmony_ci    n = SearchPathW(pathVariable, filename, NULL, MAXLEN, buffer, NULL);
8057db96d56Sopenharmony_ci    if (!n) {
8067db96d56Sopenharmony_ci        if (GetLastError() == ERROR_FILE_NOT_FOUND) {
8077db96d56Sopenharmony_ci            debug(L"# Did not find %s on PATH\n", filename);
8087db96d56Sopenharmony_ci            // If we didn't find it on PATH, let normal handling take over
8097db96d56Sopenharmony_ci            return RC_NO_SHEBANG;
8107db96d56Sopenharmony_ci        }
8117db96d56Sopenharmony_ci        // Other errors should cause us to break
8127db96d56Sopenharmony_ci        winerror(0, L"Failed to find %s on PATH\n", filename);
8137db96d56Sopenharmony_ci        return RC_BAD_VIRTUAL_PATH;
8147db96d56Sopenharmony_ci    }
8157db96d56Sopenharmony_ci
8167db96d56Sopenharmony_ci    // Check that we aren't going to call ourselves again
8177db96d56Sopenharmony_ci    // If we are, pretend there was no shebang and let normal handling take over
8187db96d56Sopenharmony_ci    if (GetModuleFileNameW(NULL, filename, MAXLEN) &&
8197db96d56Sopenharmony_ci        0 == _comparePath(filename, -1, buffer, -1)) {
8207db96d56Sopenharmony_ci        debug(L"# ignoring recursive shebang command\n");
8217db96d56Sopenharmony_ci        return RC_RECURSIVE_SHEBANG;
8227db96d56Sopenharmony_ci    }
8237db96d56Sopenharmony_ci
8247db96d56Sopenharmony_ci    wchar_t *buf = allocSearchInfoBuffer(search, n + 1);
8257db96d56Sopenharmony_ci    if (!buf || wcscpy_s(buf, n + 1, buffer)) {
8267db96d56Sopenharmony_ci        return RC_NO_MEMORY;
8277db96d56Sopenharmony_ci    }
8287db96d56Sopenharmony_ci
8297db96d56Sopenharmony_ci    search->executablePath = buf;
8307db96d56Sopenharmony_ci    search->executableArgs = &command[commandLength];
8317db96d56Sopenharmony_ci    search->executableArgsLength = shebangLength - commandLength;
8327db96d56Sopenharmony_ci    debug(L"# Found %s on PATH\n", buf);
8337db96d56Sopenharmony_ci
8347db96d56Sopenharmony_ci    return 0;
8357db96d56Sopenharmony_ci}
8367db96d56Sopenharmony_ci
8377db96d56Sopenharmony_ci
8387db96d56Sopenharmony_ciint
8397db96d56Sopenharmony_ci_readIni(const wchar_t *section, const wchar_t *settingName, wchar_t *buffer, int bufferLength)
8407db96d56Sopenharmony_ci{
8417db96d56Sopenharmony_ci    wchar_t iniPath[MAXLEN];
8427db96d56Sopenharmony_ci    int n;
8437db96d56Sopenharmony_ci    if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, iniPath)) &&
8447db96d56Sopenharmony_ci        join(iniPath, MAXLEN, L"py.ini")) {
8457db96d56Sopenharmony_ci        debug(L"# Reading from %s for %s/%s\n", iniPath, section, settingName);
8467db96d56Sopenharmony_ci        n = GetPrivateProfileStringW(section, settingName, NULL, buffer, bufferLength, iniPath);
8477db96d56Sopenharmony_ci        if (n) {
8487db96d56Sopenharmony_ci            debug(L"# Found %s in %s\n", settingName, iniPath);
8497db96d56Sopenharmony_ci            return n;
8507db96d56Sopenharmony_ci        } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
8517db96d56Sopenharmony_ci            debug(L"# Did not find file %s\n", iniPath);
8527db96d56Sopenharmony_ci        } else {
8537db96d56Sopenharmony_ci            winerror(0, L"Failed to read from %s\n", iniPath);
8547db96d56Sopenharmony_ci        }
8557db96d56Sopenharmony_ci    }
8567db96d56Sopenharmony_ci    if (GetModuleFileNameW(NULL, iniPath, MAXLEN) &&
8577db96d56Sopenharmony_ci        SUCCEEDED(PathCchRemoveFileSpec(iniPath, MAXLEN)) &&
8587db96d56Sopenharmony_ci        join(iniPath, MAXLEN, L"py.ini")) {
8597db96d56Sopenharmony_ci        debug(L"# Reading from %s for %s/%s\n", iniPath, section, settingName);
8607db96d56Sopenharmony_ci        n = GetPrivateProfileStringW(section, settingName, NULL, buffer, MAXLEN, iniPath);
8617db96d56Sopenharmony_ci        if (n) {
8627db96d56Sopenharmony_ci            debug(L"# Found %s in %s\n", settingName, iniPath);
8637db96d56Sopenharmony_ci            return n;
8647db96d56Sopenharmony_ci        } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
8657db96d56Sopenharmony_ci            debug(L"# Did not find file %s\n", iniPath);
8667db96d56Sopenharmony_ci        } else {
8677db96d56Sopenharmony_ci            winerror(0, L"Failed to read from %s\n", iniPath);
8687db96d56Sopenharmony_ci        }
8697db96d56Sopenharmony_ci    }
8707db96d56Sopenharmony_ci    return 0;
8717db96d56Sopenharmony_ci}
8727db96d56Sopenharmony_ci
8737db96d56Sopenharmony_ci
8747db96d56Sopenharmony_cibool
8757db96d56Sopenharmony_ci_findCommand(SearchInfo *search, const wchar_t *command, int commandLength)
8767db96d56Sopenharmony_ci{
8777db96d56Sopenharmony_ci    wchar_t commandBuffer[MAXLEN];
8787db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
8797db96d56Sopenharmony_ci    wcsncpy_s(commandBuffer, MAXLEN, command, commandLength);
8807db96d56Sopenharmony_ci    int n = _readIni(L"commands", commandBuffer, buffer, MAXLEN);
8817db96d56Sopenharmony_ci    if (!n) {
8827db96d56Sopenharmony_ci        return false;
8837db96d56Sopenharmony_ci    }
8847db96d56Sopenharmony_ci    wchar_t *path = allocSearchInfoBuffer(search, n + 1);
8857db96d56Sopenharmony_ci    if (!path) {
8867db96d56Sopenharmony_ci        return false;
8877db96d56Sopenharmony_ci    }
8887db96d56Sopenharmony_ci    wcscpy_s(path, n + 1, buffer);
8897db96d56Sopenharmony_ci    search->executablePath = path;
8907db96d56Sopenharmony_ci    return true;
8917db96d56Sopenharmony_ci}
8927db96d56Sopenharmony_ci
8937db96d56Sopenharmony_ci
8947db96d56Sopenharmony_ciint
8957db96d56Sopenharmony_ci_useShebangAsExecutable(SearchInfo *search, const wchar_t *shebang, int shebangLength)
8967db96d56Sopenharmony_ci{
8977db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
8987db96d56Sopenharmony_ci    wchar_t script[MAXLEN];
8997db96d56Sopenharmony_ci    wchar_t command[MAXLEN];
9007db96d56Sopenharmony_ci
9017db96d56Sopenharmony_ci    int commandLength = 0;
9027db96d56Sopenharmony_ci    int inQuote = 0;
9037db96d56Sopenharmony_ci
9047db96d56Sopenharmony_ci    if (!shebang || !shebangLength) {
9057db96d56Sopenharmony_ci        return 0;
9067db96d56Sopenharmony_ci    }
9077db96d56Sopenharmony_ci
9087db96d56Sopenharmony_ci    wchar_t *pC = command;
9097db96d56Sopenharmony_ci    for (int i = 0; i < shebangLength; ++i) {
9107db96d56Sopenharmony_ci        wchar_t c = shebang[i];
9117db96d56Sopenharmony_ci        if (isspace(c) && !inQuote) {
9127db96d56Sopenharmony_ci            commandLength = i;
9137db96d56Sopenharmony_ci            break;
9147db96d56Sopenharmony_ci        } else if (c == L'"') {
9157db96d56Sopenharmony_ci            inQuote = !inQuote;
9167db96d56Sopenharmony_ci        } else if (c == L'/' || c == L'\\') {
9177db96d56Sopenharmony_ci            *pC++ = L'\\';
9187db96d56Sopenharmony_ci        } else {
9197db96d56Sopenharmony_ci            *pC++ = c;
9207db96d56Sopenharmony_ci        }
9217db96d56Sopenharmony_ci    }
9227db96d56Sopenharmony_ci    *pC = L'\0';
9237db96d56Sopenharmony_ci
9247db96d56Sopenharmony_ci    if (!GetCurrentDirectoryW(MAXLEN, buffer) ||
9257db96d56Sopenharmony_ci        wcsncpy_s(script, MAXLEN, search->scriptFile, search->scriptFileLength) ||
9267db96d56Sopenharmony_ci        FAILED(PathCchCombineEx(buffer, MAXLEN, buffer, script,
9277db96d56Sopenharmony_ci                                PATHCCH_ALLOW_LONG_PATHS)) ||
9287db96d56Sopenharmony_ci        FAILED(PathCchRemoveFileSpec(buffer, MAXLEN)) ||
9297db96d56Sopenharmony_ci        FAILED(PathCchCombineEx(buffer, MAXLEN, buffer, command,
9307db96d56Sopenharmony_ci                                PATHCCH_ALLOW_LONG_PATHS))
9317db96d56Sopenharmony_ci    ) {
9327db96d56Sopenharmony_ci        return RC_NO_MEMORY;
9337db96d56Sopenharmony_ci    }
9347db96d56Sopenharmony_ci
9357db96d56Sopenharmony_ci    int n = (int)wcsnlen(buffer, MAXLEN);
9367db96d56Sopenharmony_ci    wchar_t *path = allocSearchInfoBuffer(search, n + 1);
9377db96d56Sopenharmony_ci    if (!path) {
9387db96d56Sopenharmony_ci        return RC_NO_MEMORY;
9397db96d56Sopenharmony_ci    }
9407db96d56Sopenharmony_ci    wcscpy_s(path, n + 1, buffer);
9417db96d56Sopenharmony_ci    search->executablePath = path;
9427db96d56Sopenharmony_ci    if (commandLength) {
9437db96d56Sopenharmony_ci        search->executableArgs = &shebang[commandLength];
9447db96d56Sopenharmony_ci        search->executableArgsLength = shebangLength - commandLength;
9457db96d56Sopenharmony_ci    }
9467db96d56Sopenharmony_ci    return 0;
9477db96d56Sopenharmony_ci}
9487db96d56Sopenharmony_ci
9497db96d56Sopenharmony_ci
9507db96d56Sopenharmony_ciint
9517db96d56Sopenharmony_cicheckShebang(SearchInfo *search)
9527db96d56Sopenharmony_ci{
9537db96d56Sopenharmony_ci    // Do not check shebang if a tag was provided or if no script file
9547db96d56Sopenharmony_ci    // was found on the command line.
9557db96d56Sopenharmony_ci    if (search->tag || !search->scriptFile) {
9567db96d56Sopenharmony_ci        return 0;
9577db96d56Sopenharmony_ci    }
9587db96d56Sopenharmony_ci
9597db96d56Sopenharmony_ci    if (search->scriptFileLength < 0) {
9607db96d56Sopenharmony_ci        search->scriptFileLength = (int)wcsnlen_s(search->scriptFile, MAXLEN);
9617db96d56Sopenharmony_ci    }
9627db96d56Sopenharmony_ci
9637db96d56Sopenharmony_ci    wchar_t *scriptFile = (wchar_t*)malloc(sizeof(wchar_t) * (search->scriptFileLength + 1));
9647db96d56Sopenharmony_ci    if (!scriptFile) {
9657db96d56Sopenharmony_ci        return RC_NO_MEMORY;
9667db96d56Sopenharmony_ci    }
9677db96d56Sopenharmony_ci
9687db96d56Sopenharmony_ci    wcsncpy_s(scriptFile, search->scriptFileLength + 1,
9697db96d56Sopenharmony_ci              search->scriptFile, search->scriptFileLength);
9707db96d56Sopenharmony_ci
9717db96d56Sopenharmony_ci    HANDLE hFile = CreateFileW(scriptFile, GENERIC_READ,
9727db96d56Sopenharmony_ci        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
9737db96d56Sopenharmony_ci        NULL, OPEN_EXISTING, 0, NULL);
9747db96d56Sopenharmony_ci
9757db96d56Sopenharmony_ci    if (hFile == INVALID_HANDLE_VALUE) {
9767db96d56Sopenharmony_ci        debug(L"# Failed to open %s for shebang parsing (0x%08X)\n",
9777db96d56Sopenharmony_ci              scriptFile, GetLastError());
9787db96d56Sopenharmony_ci        free(scriptFile);
9797db96d56Sopenharmony_ci        return 0;
9807db96d56Sopenharmony_ci    }
9817db96d56Sopenharmony_ci
9827db96d56Sopenharmony_ci    DWORD bytesRead = 0;
9837db96d56Sopenharmony_ci    char buffer[4096];
9847db96d56Sopenharmony_ci    if (!ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
9857db96d56Sopenharmony_ci        debug(L"# Failed to read %s for shebang parsing (0x%08X)\n",
9867db96d56Sopenharmony_ci              scriptFile, GetLastError());
9877db96d56Sopenharmony_ci        free(scriptFile);
9887db96d56Sopenharmony_ci        return 0;
9897db96d56Sopenharmony_ci    }
9907db96d56Sopenharmony_ci
9917db96d56Sopenharmony_ci    CloseHandle(hFile);
9927db96d56Sopenharmony_ci    debug(L"# Read %d bytes from %s to find shebang line\n", bytesRead, scriptFile);
9937db96d56Sopenharmony_ci    free(scriptFile);
9947db96d56Sopenharmony_ci
9957db96d56Sopenharmony_ci
9967db96d56Sopenharmony_ci    char *b = buffer;
9977db96d56Sopenharmony_ci    bool onlyUtf8 = false;
9987db96d56Sopenharmony_ci    if (bytesRead > 3 && *b == 0xEF) {
9997db96d56Sopenharmony_ci        if (*++b == 0xBB && *++b == 0xBF) {
10007db96d56Sopenharmony_ci            // Allow a UTF-8 BOM
10017db96d56Sopenharmony_ci            ++b;
10027db96d56Sopenharmony_ci            bytesRead -= 3;
10037db96d56Sopenharmony_ci            onlyUtf8 = true;
10047db96d56Sopenharmony_ci        } else {
10057db96d56Sopenharmony_ci            debug(L"# Invalid BOM in shebang line");
10067db96d56Sopenharmony_ci            return 0;
10077db96d56Sopenharmony_ci        }
10087db96d56Sopenharmony_ci    }
10097db96d56Sopenharmony_ci    if (bytesRead <= 2 || b[0] != '#' || b[1] != '!') {
10107db96d56Sopenharmony_ci        // No shebang (#!) at start of line
10117db96d56Sopenharmony_ci        debug(L"# No valid shebang line");
10127db96d56Sopenharmony_ci        return 0;
10137db96d56Sopenharmony_ci    }
10147db96d56Sopenharmony_ci    ++b;
10157db96d56Sopenharmony_ci    --bytesRead;
10167db96d56Sopenharmony_ci    while (--bytesRead > 0 && isspace(*++b)) { }
10177db96d56Sopenharmony_ci    char *start = b;
10187db96d56Sopenharmony_ci    while (--bytesRead > 0 && *++b != '\r' && *b != '\n') { }
10197db96d56Sopenharmony_ci    wchar_t *shebang;
10207db96d56Sopenharmony_ci    int shebangLength;
10217db96d56Sopenharmony_ci    // We add 1 when bytesRead==0, as in that case we hit EOF and b points
10227db96d56Sopenharmony_ci    // to the last character in the file, not the newline
10237db96d56Sopenharmony_ci    int exitCode = _decodeShebang(search, start, (int)(b - start + (bytesRead == 0)), onlyUtf8, &shebang, &shebangLength);
10247db96d56Sopenharmony_ci    if (exitCode) {
10257db96d56Sopenharmony_ci        return exitCode;
10267db96d56Sopenharmony_ci    }
10277db96d56Sopenharmony_ci    debug(L"Shebang: %s\n", shebang);
10287db96d56Sopenharmony_ci
10297db96d56Sopenharmony_ci    // Handle shebangs that we should search PATH for
10307db96d56Sopenharmony_ci    exitCode = searchPath(search, shebang, shebangLength);
10317db96d56Sopenharmony_ci    if (exitCode != RC_NO_SHEBANG) {
10327db96d56Sopenharmony_ci        return exitCode;
10337db96d56Sopenharmony_ci    }
10347db96d56Sopenharmony_ci
10357db96d56Sopenharmony_ci    // Handle some known, case-sensitive shebangs
10367db96d56Sopenharmony_ci    const wchar_t *command;
10377db96d56Sopenharmony_ci    int commandLength;
10387db96d56Sopenharmony_ci    // Each template must end with "python"
10397db96d56Sopenharmony_ci    static const wchar_t *shebangTemplates[] = {
10407db96d56Sopenharmony_ci        L"/usr/bin/env python",
10417db96d56Sopenharmony_ci        L"/usr/bin/python",
10427db96d56Sopenharmony_ci        L"/usr/local/bin/python",
10437db96d56Sopenharmony_ci        L"python",
10447db96d56Sopenharmony_ci        NULL
10457db96d56Sopenharmony_ci    };
10467db96d56Sopenharmony_ci
10477db96d56Sopenharmony_ci    for (const wchar_t **tmpl = shebangTemplates; *tmpl; ++tmpl) {
10487db96d56Sopenharmony_ci        // Just to make sure we don't mess this up in the future
10497db96d56Sopenharmony_ci        assert(0 == wcscmp(L"python", (*tmpl) + wcslen(*tmpl) - 6));
10507db96d56Sopenharmony_ci
10517db96d56Sopenharmony_ci        if (_shebangStartsWith(shebang, shebangLength, *tmpl, &command, &commandLength)) {
10527db96d56Sopenharmony_ci            // Search for "python{command}" overrides. All templates end with
10537db96d56Sopenharmony_ci            // "python", so we prepend it by jumping back 6 characters
10547db96d56Sopenharmony_ci            if (_findCommand(search, &command[-6], commandLength + 6)) {
10557db96d56Sopenharmony_ci                search->executableArgs = &command[commandLength];
10567db96d56Sopenharmony_ci                search->executableArgsLength = shebangLength - commandLength;
10577db96d56Sopenharmony_ci                debug(L"# Treating shebang command '%.*s' as %s\n",
10587db96d56Sopenharmony_ci                    commandLength + 6, &command[-6], search->executablePath);
10597db96d56Sopenharmony_ci                return 0;
10607db96d56Sopenharmony_ci            }
10617db96d56Sopenharmony_ci
10627db96d56Sopenharmony_ci            search->tag = command;
10637db96d56Sopenharmony_ci            search->tagLength = commandLength;
10647db96d56Sopenharmony_ci            // If we had 'python3.12.exe' then we want to strip the suffix
10657db96d56Sopenharmony_ci            // off of the tag
10667db96d56Sopenharmony_ci            if (search->tagLength > 4) {
10677db96d56Sopenharmony_ci                const wchar_t *suffix = &search->tag[search->tagLength - 4];
10687db96d56Sopenharmony_ci                if (0 == _comparePath(suffix, 4, L".exe", -1)) {
10697db96d56Sopenharmony_ci                    search->tagLength -= 4;
10707db96d56Sopenharmony_ci                }
10717db96d56Sopenharmony_ci            }
10727db96d56Sopenharmony_ci            // If we had 'python3_d' then we want to strip the '_d' (any
10737db96d56Sopenharmony_ci            // '.exe' is already gone)
10747db96d56Sopenharmony_ci            if (search->tagLength > 2) {
10757db96d56Sopenharmony_ci                const wchar_t *suffix = &search->tag[search->tagLength - 2];
10767db96d56Sopenharmony_ci                if (0 == _comparePath(suffix, 2, L"_d", -1)) {
10777db96d56Sopenharmony_ci                    search->tagLength -= 2;
10787db96d56Sopenharmony_ci                }
10797db96d56Sopenharmony_ci            }
10807db96d56Sopenharmony_ci            search->oldStyleTag = true;
10817db96d56Sopenharmony_ci            search->executableArgs = &command[commandLength];
10827db96d56Sopenharmony_ci            search->executableArgsLength = shebangLength - commandLength;
10837db96d56Sopenharmony_ci            if (search->tag && search->tagLength) {
10847db96d56Sopenharmony_ci                debug(L"# Treating shebang command '%.*s' as 'py -%.*s'\n",
10857db96d56Sopenharmony_ci                    commandLength, command, search->tagLength, search->tag);
10867db96d56Sopenharmony_ci            } else {
10877db96d56Sopenharmony_ci                debug(L"# Treating shebang command '%.*s' as 'py'\n",
10887db96d56Sopenharmony_ci                    commandLength, command);
10897db96d56Sopenharmony_ci            }
10907db96d56Sopenharmony_ci            return 0;
10917db96d56Sopenharmony_ci        }
10927db96d56Sopenharmony_ci    }
10937db96d56Sopenharmony_ci
10947db96d56Sopenharmony_ci    // Unrecognised executables are first tried as command aliases
10957db96d56Sopenharmony_ci    commandLength = 0;
10967db96d56Sopenharmony_ci    while (commandLength < shebangLength && !isspace(shebang[commandLength])) {
10977db96d56Sopenharmony_ci        commandLength += 1;
10987db96d56Sopenharmony_ci    }
10997db96d56Sopenharmony_ci    if (_findCommand(search, shebang, commandLength)) {
11007db96d56Sopenharmony_ci        search->executableArgs = &shebang[commandLength];
11017db96d56Sopenharmony_ci        search->executableArgsLength = shebangLength - commandLength;
11027db96d56Sopenharmony_ci        debug(L"# Treating shebang command '%.*s' as %s\n",
11037db96d56Sopenharmony_ci            commandLength, shebang, search->executablePath);
11047db96d56Sopenharmony_ci        return 0;
11057db96d56Sopenharmony_ci    }
11067db96d56Sopenharmony_ci
11077db96d56Sopenharmony_ci    // Unrecognised commands are joined to the script's directory and treated
11087db96d56Sopenharmony_ci    // as the executable path
11097db96d56Sopenharmony_ci    return _useShebangAsExecutable(search, shebang, shebangLength);
11107db96d56Sopenharmony_ci}
11117db96d56Sopenharmony_ci
11127db96d56Sopenharmony_ci
11137db96d56Sopenharmony_ciint
11147db96d56Sopenharmony_cicheckDefaults(SearchInfo *search)
11157db96d56Sopenharmony_ci{
11167db96d56Sopenharmony_ci    if (!search->allowDefaults) {
11177db96d56Sopenharmony_ci        return 0;
11187db96d56Sopenharmony_ci    }
11197db96d56Sopenharmony_ci
11207db96d56Sopenharmony_ci    // Only resolve old-style (or absent) tags to defaults
11217db96d56Sopenharmony_ci    if (search->tag && search->tagLength && !search->oldStyleTag) {
11227db96d56Sopenharmony_ci        return 0;
11237db96d56Sopenharmony_ci    }
11247db96d56Sopenharmony_ci
11257db96d56Sopenharmony_ci    // If tag is only a major version number, expand it from the environment
11267db96d56Sopenharmony_ci    // or an ini file
11277db96d56Sopenharmony_ci    const wchar_t *iniSettingName = NULL;
11287db96d56Sopenharmony_ci    const wchar_t *envSettingName = NULL;
11297db96d56Sopenharmony_ci    if (!search->tag || !search->tagLength) {
11307db96d56Sopenharmony_ci        iniSettingName = L"python";
11317db96d56Sopenharmony_ci        envSettingName = L"py_python";
11327db96d56Sopenharmony_ci    } else if (0 == wcsncmp(search->tag, L"3", search->tagLength)) {
11337db96d56Sopenharmony_ci        iniSettingName = L"python3";
11347db96d56Sopenharmony_ci        envSettingName = L"py_python3";
11357db96d56Sopenharmony_ci    } else if (0 == wcsncmp(search->tag, L"2", search->tagLength)) {
11367db96d56Sopenharmony_ci        iniSettingName = L"python2";
11377db96d56Sopenharmony_ci        envSettingName = L"py_python2";
11387db96d56Sopenharmony_ci    } else {
11397db96d56Sopenharmony_ci        debug(L"# Cannot select defaults for tag '%.*s'\n", search->tagLength, search->tag);
11407db96d56Sopenharmony_ci        return 0;
11417db96d56Sopenharmony_ci    }
11427db96d56Sopenharmony_ci
11437db96d56Sopenharmony_ci    // First, try to read an environment variable
11447db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
11457db96d56Sopenharmony_ci    int n = GetEnvironmentVariableW(envSettingName, buffer, MAXLEN);
11467db96d56Sopenharmony_ci
11477db96d56Sopenharmony_ci    // If none found, check in our two .ini files instead
11487db96d56Sopenharmony_ci    if (!n) {
11497db96d56Sopenharmony_ci        n = _readIni(L"defaults", iniSettingName, buffer, MAXLEN);
11507db96d56Sopenharmony_ci    }
11517db96d56Sopenharmony_ci
11527db96d56Sopenharmony_ci    if (n) {
11537db96d56Sopenharmony_ci        wchar_t *tag = allocSearchInfoBuffer(search, n + 1);
11547db96d56Sopenharmony_ci        if (!tag) {
11557db96d56Sopenharmony_ci            return RC_NO_MEMORY;
11567db96d56Sopenharmony_ci        }
11577db96d56Sopenharmony_ci        wcscpy_s(tag, n + 1, buffer);
11587db96d56Sopenharmony_ci        wchar_t *slash = wcschr(tag, L'/');
11597db96d56Sopenharmony_ci        if (!slash) {
11607db96d56Sopenharmony_ci            search->tag = tag;
11617db96d56Sopenharmony_ci            search->tagLength = n;
11627db96d56Sopenharmony_ci            search->oldStyleTag = true;
11637db96d56Sopenharmony_ci        } else {
11647db96d56Sopenharmony_ci            search->company = tag;
11657db96d56Sopenharmony_ci            search->companyLength = (int)(slash - tag);
11667db96d56Sopenharmony_ci            search->tag = slash + 1;
11677db96d56Sopenharmony_ci            search->tagLength = n - (search->companyLength + 1);
11687db96d56Sopenharmony_ci            search->oldStyleTag = false;
11697db96d56Sopenharmony_ci        }
11707db96d56Sopenharmony_ci        // gh-92817: allow a high priority env to be selected even if it
11717db96d56Sopenharmony_ci        // doesn't match the tag
11727db96d56Sopenharmony_ci        search->lowPriorityTag = true;
11737db96d56Sopenharmony_ci    }
11747db96d56Sopenharmony_ci
11757db96d56Sopenharmony_ci    return 0;
11767db96d56Sopenharmony_ci}
11777db96d56Sopenharmony_ci
11787db96d56Sopenharmony_ci/******************************************************************************\
11797db96d56Sopenharmony_ci ***                          ENVIRONMENT SEARCH                            ***
11807db96d56Sopenharmony_ci\******************************************************************************/
11817db96d56Sopenharmony_ci
11827db96d56Sopenharmony_citypedef struct EnvironmentInfo {
11837db96d56Sopenharmony_ci    /* We use a binary tree and sort on insert */
11847db96d56Sopenharmony_ci    struct EnvironmentInfo *prev;
11857db96d56Sopenharmony_ci    struct EnvironmentInfo *next;
11867db96d56Sopenharmony_ci    /* parent is only used when constructing */
11877db96d56Sopenharmony_ci    struct EnvironmentInfo *parent;
11887db96d56Sopenharmony_ci    const wchar_t *company;
11897db96d56Sopenharmony_ci    const wchar_t *tag;
11907db96d56Sopenharmony_ci    int internalSortKey;
11917db96d56Sopenharmony_ci    const wchar_t *installDir;
11927db96d56Sopenharmony_ci    const wchar_t *executablePath;
11937db96d56Sopenharmony_ci    const wchar_t *executableArgs;
11947db96d56Sopenharmony_ci    const wchar_t *architecture;
11957db96d56Sopenharmony_ci    const wchar_t *displayName;
11967db96d56Sopenharmony_ci    bool highPriority;
11977db96d56Sopenharmony_ci} EnvironmentInfo;
11987db96d56Sopenharmony_ci
11997db96d56Sopenharmony_ci
12007db96d56Sopenharmony_ciint
12017db96d56Sopenharmony_cicopyWstr(const wchar_t **dest, const wchar_t *src)
12027db96d56Sopenharmony_ci{
12037db96d56Sopenharmony_ci    if (!dest) {
12047db96d56Sopenharmony_ci        return RC_NO_MEMORY;
12057db96d56Sopenharmony_ci    }
12067db96d56Sopenharmony_ci    if (!src) {
12077db96d56Sopenharmony_ci        *dest = NULL;
12087db96d56Sopenharmony_ci        return 0;
12097db96d56Sopenharmony_ci    }
12107db96d56Sopenharmony_ci    size_t n = wcsnlen_s(src, MAXLEN - 1) + 1;
12117db96d56Sopenharmony_ci    wchar_t *buffer = (wchar_t*)malloc(n * sizeof(wchar_t));
12127db96d56Sopenharmony_ci    if (!buffer) {
12137db96d56Sopenharmony_ci        return RC_NO_MEMORY;
12147db96d56Sopenharmony_ci    }
12157db96d56Sopenharmony_ci    wcsncpy_s(buffer, n, src, n - 1);
12167db96d56Sopenharmony_ci    *dest = (const wchar_t*)buffer;
12177db96d56Sopenharmony_ci    return 0;
12187db96d56Sopenharmony_ci}
12197db96d56Sopenharmony_ci
12207db96d56Sopenharmony_ci
12217db96d56Sopenharmony_ciEnvironmentInfo *
12227db96d56Sopenharmony_cinewEnvironmentInfo(const wchar_t *company, const wchar_t *tag)
12237db96d56Sopenharmony_ci{
12247db96d56Sopenharmony_ci    EnvironmentInfo *env = (EnvironmentInfo *)malloc(sizeof(EnvironmentInfo));
12257db96d56Sopenharmony_ci    if (!env) {
12267db96d56Sopenharmony_ci        return NULL;
12277db96d56Sopenharmony_ci    }
12287db96d56Sopenharmony_ci    memset(env, 0, sizeof(EnvironmentInfo));
12297db96d56Sopenharmony_ci    int exitCode = copyWstr(&env->company, company);
12307db96d56Sopenharmony_ci    if (exitCode) {
12317db96d56Sopenharmony_ci        free((void *)env);
12327db96d56Sopenharmony_ci        return NULL;
12337db96d56Sopenharmony_ci    }
12347db96d56Sopenharmony_ci    exitCode = copyWstr(&env->tag, tag);
12357db96d56Sopenharmony_ci    if (exitCode) {
12367db96d56Sopenharmony_ci        free((void *)env->company);
12377db96d56Sopenharmony_ci        free((void *)env);
12387db96d56Sopenharmony_ci        return NULL;
12397db96d56Sopenharmony_ci    }
12407db96d56Sopenharmony_ci    return env;
12417db96d56Sopenharmony_ci}
12427db96d56Sopenharmony_ci
12437db96d56Sopenharmony_ci
12447db96d56Sopenharmony_civoid
12457db96d56Sopenharmony_cifreeEnvironmentInfo(EnvironmentInfo *env)
12467db96d56Sopenharmony_ci{
12477db96d56Sopenharmony_ci    if (env) {
12487db96d56Sopenharmony_ci        free((void *)env->company);
12497db96d56Sopenharmony_ci        free((void *)env->tag);
12507db96d56Sopenharmony_ci        free((void *)env->installDir);
12517db96d56Sopenharmony_ci        free((void *)env->executablePath);
12527db96d56Sopenharmony_ci        free((void *)env->executableArgs);
12537db96d56Sopenharmony_ci        free((void *)env->displayName);
12547db96d56Sopenharmony_ci        freeEnvironmentInfo(env->prev);
12557db96d56Sopenharmony_ci        env->prev = NULL;
12567db96d56Sopenharmony_ci        freeEnvironmentInfo(env->next);
12577db96d56Sopenharmony_ci        env->next = NULL;
12587db96d56Sopenharmony_ci        free((void *)env);
12597db96d56Sopenharmony_ci    }
12607db96d56Sopenharmony_ci}
12617db96d56Sopenharmony_ci
12627db96d56Sopenharmony_ci
12637db96d56Sopenharmony_ci/* Specific string comparisons for sorting the tree */
12647db96d56Sopenharmony_ci
12657db96d56Sopenharmony_ciint
12667db96d56Sopenharmony_ci_compareCompany(const wchar_t *x, const wchar_t *y)
12677db96d56Sopenharmony_ci{
12687db96d56Sopenharmony_ci    if (!x && !y) {
12697db96d56Sopenharmony_ci        return 0;
12707db96d56Sopenharmony_ci    } else if (!x) {
12717db96d56Sopenharmony_ci        return -1;
12727db96d56Sopenharmony_ci    } else if (!y) {
12737db96d56Sopenharmony_ci        return 1;
12747db96d56Sopenharmony_ci    }
12757db96d56Sopenharmony_ci
12767db96d56Sopenharmony_ci    bool coreX = 0 == _compare(x, -1, L"PythonCore", -1);
12777db96d56Sopenharmony_ci    bool coreY = 0 == _compare(y, -1, L"PythonCore", -1);
12787db96d56Sopenharmony_ci    if (coreX) {
12797db96d56Sopenharmony_ci        return coreY ? 0 : -1;
12807db96d56Sopenharmony_ci    } else if (coreY) {
12817db96d56Sopenharmony_ci        return 1;
12827db96d56Sopenharmony_ci    }
12837db96d56Sopenharmony_ci    return _compare(x, -1, y, -1);
12847db96d56Sopenharmony_ci}
12857db96d56Sopenharmony_ci
12867db96d56Sopenharmony_ci
12877db96d56Sopenharmony_ciint
12887db96d56Sopenharmony_ci_compareTag(const wchar_t *x, const wchar_t *y)
12897db96d56Sopenharmony_ci{
12907db96d56Sopenharmony_ci    if (!x && !y) {
12917db96d56Sopenharmony_ci        return 0;
12927db96d56Sopenharmony_ci    } else if (!x) {
12937db96d56Sopenharmony_ci        return -1;
12947db96d56Sopenharmony_ci    } else if (!y) {
12957db96d56Sopenharmony_ci        return 1;
12967db96d56Sopenharmony_ci    }
12977db96d56Sopenharmony_ci
12987db96d56Sopenharmony_ci    // Compare up to the first dash. If not equal, that's our sort order
12997db96d56Sopenharmony_ci    const wchar_t *xDash = wcschr(x, L'-');
13007db96d56Sopenharmony_ci    const wchar_t *yDash = wcschr(y, L'-');
13017db96d56Sopenharmony_ci    int xToDash = xDash ? (int)(xDash - x) : -1;
13027db96d56Sopenharmony_ci    int yToDash = yDash ? (int)(yDash - y) : -1;
13037db96d56Sopenharmony_ci    int r = _compare(x, xToDash, y, yToDash);
13047db96d56Sopenharmony_ci    if (r) {
13057db96d56Sopenharmony_ci        return r;
13067db96d56Sopenharmony_ci    }
13077db96d56Sopenharmony_ci    // If we're equal up to the first dash, we want to sort one with
13087db96d56Sopenharmony_ci    // no dash *after* one with a dash. Otherwise, a reversed compare.
13097db96d56Sopenharmony_ci    // This works out because environments are sorted in descending tag
13107db96d56Sopenharmony_ci    // order, so that higher versions (probably) come first.
13117db96d56Sopenharmony_ci    // For PythonCore, our "X.Y" structure ensures that higher versions
13127db96d56Sopenharmony_ci    // come first. Everyone else will just have to deal with it.
13137db96d56Sopenharmony_ci    if (xDash && yDash) {
13147db96d56Sopenharmony_ci        return _compare(yDash, -1, xDash, -1);
13157db96d56Sopenharmony_ci    } else if (xDash) {
13167db96d56Sopenharmony_ci        return -1;
13177db96d56Sopenharmony_ci    } else if (yDash) {
13187db96d56Sopenharmony_ci        return 1;
13197db96d56Sopenharmony_ci    }
13207db96d56Sopenharmony_ci    return 0;
13217db96d56Sopenharmony_ci}
13227db96d56Sopenharmony_ci
13237db96d56Sopenharmony_ci
13247db96d56Sopenharmony_ciint
13257db96d56Sopenharmony_ciaddEnvironmentInfo(EnvironmentInfo **root, EnvironmentInfo* parent, EnvironmentInfo *node)
13267db96d56Sopenharmony_ci{
13277db96d56Sopenharmony_ci    EnvironmentInfo *r = *root;
13287db96d56Sopenharmony_ci    if (!r) {
13297db96d56Sopenharmony_ci        *root = node;
13307db96d56Sopenharmony_ci        node->parent = parent;
13317db96d56Sopenharmony_ci        return 0;
13327db96d56Sopenharmony_ci    }
13337db96d56Sopenharmony_ci    // Sort by company name
13347db96d56Sopenharmony_ci    switch (_compareCompany(node->company, r->company)) {
13357db96d56Sopenharmony_ci    case -1:
13367db96d56Sopenharmony_ci        return addEnvironmentInfo(&r->prev, r, node);
13377db96d56Sopenharmony_ci    case 1:
13387db96d56Sopenharmony_ci        return addEnvironmentInfo(&r->next, r, node);
13397db96d56Sopenharmony_ci    case 0:
13407db96d56Sopenharmony_ci        break;
13417db96d56Sopenharmony_ci    }
13427db96d56Sopenharmony_ci    // Then by tag (descending)
13437db96d56Sopenharmony_ci    switch (_compareTag(node->tag, r->tag)) {
13447db96d56Sopenharmony_ci    case -1:
13457db96d56Sopenharmony_ci        return addEnvironmentInfo(&r->next, r, node);
13467db96d56Sopenharmony_ci    case 1:
13477db96d56Sopenharmony_ci        return addEnvironmentInfo(&r->prev, r, node);
13487db96d56Sopenharmony_ci    case 0:
13497db96d56Sopenharmony_ci        break;
13507db96d56Sopenharmony_ci    }
13517db96d56Sopenharmony_ci    // Then keep the one with the lowest internal sort key
13527db96d56Sopenharmony_ci    if (node->internalSortKey < r->internalSortKey) {
13537db96d56Sopenharmony_ci        // Replace the current node
13547db96d56Sopenharmony_ci        node->parent = r->parent;
13557db96d56Sopenharmony_ci        if (node->parent) {
13567db96d56Sopenharmony_ci            if (node->parent->prev == r) {
13577db96d56Sopenharmony_ci                node->parent->prev = node;
13587db96d56Sopenharmony_ci            } else if (node->parent->next == r) {
13597db96d56Sopenharmony_ci                node->parent->next = node;
13607db96d56Sopenharmony_ci            } else {
13617db96d56Sopenharmony_ci                debug(L"# Inconsistent parent value in tree\n");
13627db96d56Sopenharmony_ci                freeEnvironmentInfo(node);
13637db96d56Sopenharmony_ci                return RC_INTERNAL_ERROR;
13647db96d56Sopenharmony_ci            }
13657db96d56Sopenharmony_ci        } else {
13667db96d56Sopenharmony_ci            // If node has no parent, then it is the root.
13677db96d56Sopenharmony_ci            *root = node;
13687db96d56Sopenharmony_ci        }
13697db96d56Sopenharmony_ci
13707db96d56Sopenharmony_ci        node->next = r->next;
13717db96d56Sopenharmony_ci        node->prev = r->prev;
13727db96d56Sopenharmony_ci
13737db96d56Sopenharmony_ci        debug(L"# replaced %s/%s/%i in tree\n", node->company, node->tag, node->internalSortKey);
13747db96d56Sopenharmony_ci        freeEnvironmentInfo(r);
13757db96d56Sopenharmony_ci    } else {
13767db96d56Sopenharmony_ci        debug(L"# not adding %s/%s/%i to tree\n", node->company, node->tag, node->internalSortKey);
13777db96d56Sopenharmony_ci        return RC_DUPLICATE_ITEM;
13787db96d56Sopenharmony_ci    }
13797db96d56Sopenharmony_ci    return 0;
13807db96d56Sopenharmony_ci}
13817db96d56Sopenharmony_ci
13827db96d56Sopenharmony_ci
13837db96d56Sopenharmony_ci/******************************************************************************\
13847db96d56Sopenharmony_ci ***                            REGISTRY SEARCH                             ***
13857db96d56Sopenharmony_ci\******************************************************************************/
13867db96d56Sopenharmony_ci
13877db96d56Sopenharmony_ci
13887db96d56Sopenharmony_ciint
13897db96d56Sopenharmony_ci_registryReadString(const wchar_t **dest, HKEY root, const wchar_t *subkey, const wchar_t *value)
13907db96d56Sopenharmony_ci{
13917db96d56Sopenharmony_ci    // Note that this is bytes (hence 'cb'), not characters ('cch')
13927db96d56Sopenharmony_ci    DWORD cbData = 0;
13937db96d56Sopenharmony_ci    DWORD flags = RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ;
13947db96d56Sopenharmony_ci
13957db96d56Sopenharmony_ci    if (ERROR_SUCCESS != RegGetValueW(root, subkey, value, flags, NULL, NULL, &cbData)) {
13967db96d56Sopenharmony_ci        return 0;
13977db96d56Sopenharmony_ci    }
13987db96d56Sopenharmony_ci
13997db96d56Sopenharmony_ci    wchar_t *buffer = (wchar_t*)malloc(cbData);
14007db96d56Sopenharmony_ci    if (!buffer) {
14017db96d56Sopenharmony_ci        return RC_NO_MEMORY;
14027db96d56Sopenharmony_ci    }
14037db96d56Sopenharmony_ci
14047db96d56Sopenharmony_ci    if (ERROR_SUCCESS == RegGetValueW(root, subkey, value, flags, NULL, buffer, &cbData)) {
14057db96d56Sopenharmony_ci        *dest = buffer;
14067db96d56Sopenharmony_ci    } else {
14077db96d56Sopenharmony_ci        free((void *)buffer);
14087db96d56Sopenharmony_ci    }
14097db96d56Sopenharmony_ci    return 0;
14107db96d56Sopenharmony_ci}
14117db96d56Sopenharmony_ci
14127db96d56Sopenharmony_ci
14137db96d56Sopenharmony_ciint
14147db96d56Sopenharmony_ci_combineWithInstallDir(const wchar_t **dest, const wchar_t *installDir, const wchar_t *fragment, int fragmentLength)
14157db96d56Sopenharmony_ci{
14167db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
14177db96d56Sopenharmony_ci    wchar_t fragmentBuffer[MAXLEN];
14187db96d56Sopenharmony_ci    if (wcsncpy_s(fragmentBuffer, MAXLEN, fragment, fragmentLength)) {
14197db96d56Sopenharmony_ci        return RC_NO_MEMORY;
14207db96d56Sopenharmony_ci    }
14217db96d56Sopenharmony_ci
14227db96d56Sopenharmony_ci    if (FAILED(PathCchCombineEx(buffer, MAXLEN, installDir, fragmentBuffer, PATHCCH_ALLOW_LONG_PATHS))) {
14237db96d56Sopenharmony_ci        return RC_NO_MEMORY;
14247db96d56Sopenharmony_ci    }
14257db96d56Sopenharmony_ci
14267db96d56Sopenharmony_ci    return copyWstr(dest, buffer);
14277db96d56Sopenharmony_ci}
14287db96d56Sopenharmony_ci
14297db96d56Sopenharmony_ci
14307db96d56Sopenharmony_cibool
14317db96d56Sopenharmony_ci_isLegacyVersion(EnvironmentInfo *env)
14327db96d56Sopenharmony_ci{
14337db96d56Sopenharmony_ci    // Check if backwards-compatibility is required.
14347db96d56Sopenharmony_ci    // Specifically PythonCore versions 2.X and 3.0 - 3.5 do not implement PEP 514.
14357db96d56Sopenharmony_ci    if (0 != _compare(env->company, -1, L"PythonCore", -1)) {
14367db96d56Sopenharmony_ci        return false;
14377db96d56Sopenharmony_ci    }
14387db96d56Sopenharmony_ci
14397db96d56Sopenharmony_ci    int versionMajor, versionMinor;
14407db96d56Sopenharmony_ci    int n = swscanf_s(env->tag, L"%d.%d", &versionMajor, &versionMinor);
14417db96d56Sopenharmony_ci    if (n != 2) {
14427db96d56Sopenharmony_ci        debug(L"# %s/%s has an invalid version tag\n", env->company, env->tag);
14437db96d56Sopenharmony_ci        return false;
14447db96d56Sopenharmony_ci    }
14457db96d56Sopenharmony_ci
14467db96d56Sopenharmony_ci    return versionMajor == 2
14477db96d56Sopenharmony_ci        || (versionMajor == 3 && versionMinor >= 0 && versionMinor <= 5);
14487db96d56Sopenharmony_ci}
14497db96d56Sopenharmony_ci
14507db96d56Sopenharmony_ciint
14517db96d56Sopenharmony_ci_registryReadLegacyEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch)
14527db96d56Sopenharmony_ci{
14537db96d56Sopenharmony_ci    // Backwards-compatibility for PythonCore versions which do not implement PEP 514.
14547db96d56Sopenharmony_ci    int exitCode = _combineWithInstallDir(
14557db96d56Sopenharmony_ci        &env->executablePath,
14567db96d56Sopenharmony_ci        env->installDir,
14577db96d56Sopenharmony_ci        search->executable,
14587db96d56Sopenharmony_ci        search->executableLength
14597db96d56Sopenharmony_ci    );
14607db96d56Sopenharmony_ci    if (exitCode) {
14617db96d56Sopenharmony_ci        return exitCode;
14627db96d56Sopenharmony_ci    }
14637db96d56Sopenharmony_ci
14647db96d56Sopenharmony_ci    if (search->windowed) {
14657db96d56Sopenharmony_ci        exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"WindowedExecutableArguments");
14667db96d56Sopenharmony_ci    }
14677db96d56Sopenharmony_ci    else {
14687db96d56Sopenharmony_ci        exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"ExecutableArguments");
14697db96d56Sopenharmony_ci    }
14707db96d56Sopenharmony_ci    if (exitCode) {
14717db96d56Sopenharmony_ci        return exitCode;
14727db96d56Sopenharmony_ci    }
14737db96d56Sopenharmony_ci
14747db96d56Sopenharmony_ci    if (fallbackArch) {
14757db96d56Sopenharmony_ci        copyWstr(&env->architecture, fallbackArch);
14767db96d56Sopenharmony_ci    } else {
14777db96d56Sopenharmony_ci        DWORD binaryType;
14787db96d56Sopenharmony_ci        BOOL success = GetBinaryTypeW(env->executablePath, &binaryType);
14797db96d56Sopenharmony_ci        if (!success) {
14807db96d56Sopenharmony_ci            return RC_NO_PYTHON;
14817db96d56Sopenharmony_ci        }
14827db96d56Sopenharmony_ci
14837db96d56Sopenharmony_ci        switch (binaryType) {
14847db96d56Sopenharmony_ci        case SCS_32BIT_BINARY:
14857db96d56Sopenharmony_ci            copyWstr(&env->architecture, L"32bit");
14867db96d56Sopenharmony_ci            break;
14877db96d56Sopenharmony_ci        case SCS_64BIT_BINARY:
14887db96d56Sopenharmony_ci            copyWstr(&env->architecture, L"64bit");
14897db96d56Sopenharmony_ci            break;
14907db96d56Sopenharmony_ci        default:
14917db96d56Sopenharmony_ci            return RC_NO_PYTHON;
14927db96d56Sopenharmony_ci        }
14937db96d56Sopenharmony_ci    }
14947db96d56Sopenharmony_ci
14957db96d56Sopenharmony_ci    if (0 == _compare(env->architecture, -1, L"32bit", -1)) {
14967db96d56Sopenharmony_ci        size_t tagLength = wcslen(env->tag);
14977db96d56Sopenharmony_ci        if (tagLength <= 3 || 0 != _compare(&env->tag[tagLength - 3], 3, L"-32", 3)) {
14987db96d56Sopenharmony_ci            const wchar_t *rawTag = env->tag;
14997db96d56Sopenharmony_ci            wchar_t *realTag = (wchar_t*) malloc(sizeof(wchar_t) * (tagLength + 4));
15007db96d56Sopenharmony_ci            if (!realTag) {
15017db96d56Sopenharmony_ci                return RC_NO_MEMORY;
15027db96d56Sopenharmony_ci            }
15037db96d56Sopenharmony_ci
15047db96d56Sopenharmony_ci            int count = swprintf_s(realTag, tagLength + 4, L"%s-32", env->tag);
15057db96d56Sopenharmony_ci            if (count == -1) {
15067db96d56Sopenharmony_ci                free(realTag);
15077db96d56Sopenharmony_ci                return RC_INTERNAL_ERROR;
15087db96d56Sopenharmony_ci            }
15097db96d56Sopenharmony_ci
15107db96d56Sopenharmony_ci            env->tag = realTag;
15117db96d56Sopenharmony_ci            free((void*)rawTag);
15127db96d56Sopenharmony_ci        }
15137db96d56Sopenharmony_ci    }
15147db96d56Sopenharmony_ci
15157db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
15167db96d56Sopenharmony_ci    if (swprintf_s(buffer, MAXLEN, L"Python %s", env->tag)) {
15177db96d56Sopenharmony_ci        copyWstr(&env->displayName, buffer);
15187db96d56Sopenharmony_ci    }
15197db96d56Sopenharmony_ci
15207db96d56Sopenharmony_ci    return 0;
15217db96d56Sopenharmony_ci}
15227db96d56Sopenharmony_ci
15237db96d56Sopenharmony_ci
15247db96d56Sopenharmony_ciint
15257db96d56Sopenharmony_ci_registryReadEnvironment(const SearchInfo *search, HKEY root, EnvironmentInfo *env, const wchar_t *fallbackArch)
15267db96d56Sopenharmony_ci{
15277db96d56Sopenharmony_ci    int exitCode = _registryReadString(&env->installDir, root, L"InstallPath", NULL);
15287db96d56Sopenharmony_ci    if (exitCode) {
15297db96d56Sopenharmony_ci        return exitCode;
15307db96d56Sopenharmony_ci    }
15317db96d56Sopenharmony_ci    if (!env->installDir) {
15327db96d56Sopenharmony_ci        return RC_NO_PYTHON;
15337db96d56Sopenharmony_ci    }
15347db96d56Sopenharmony_ci
15357db96d56Sopenharmony_ci    if (_isLegacyVersion(env)) {
15367db96d56Sopenharmony_ci        return _registryReadLegacyEnvironment(search, root, env, fallbackArch);
15377db96d56Sopenharmony_ci    }
15387db96d56Sopenharmony_ci
15397db96d56Sopenharmony_ci    // If pythonw.exe requested, check specific value
15407db96d56Sopenharmony_ci    if (search->windowed) {
15417db96d56Sopenharmony_ci        exitCode = _registryReadString(&env->executablePath, root, L"InstallPath", L"WindowedExecutablePath");
15427db96d56Sopenharmony_ci        if (!exitCode && env->executablePath) {
15437db96d56Sopenharmony_ci            exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"WindowedExecutableArguments");
15447db96d56Sopenharmony_ci        }
15457db96d56Sopenharmony_ci    }
15467db96d56Sopenharmony_ci    if (exitCode) {
15477db96d56Sopenharmony_ci        return exitCode;
15487db96d56Sopenharmony_ci    }
15497db96d56Sopenharmony_ci
15507db96d56Sopenharmony_ci    // Missing windowed path or non-windowed request means we use ExecutablePath
15517db96d56Sopenharmony_ci    if (!env->executablePath) {
15527db96d56Sopenharmony_ci        exitCode = _registryReadString(&env->executablePath, root, L"InstallPath", L"ExecutablePath");
15537db96d56Sopenharmony_ci        if (!exitCode && env->executablePath) {
15547db96d56Sopenharmony_ci            exitCode = _registryReadString(&env->executableArgs, root, L"InstallPath", L"ExecutableArguments");
15557db96d56Sopenharmony_ci        }
15567db96d56Sopenharmony_ci    }
15577db96d56Sopenharmony_ci    if (exitCode) {
15587db96d56Sopenharmony_ci        return exitCode;
15597db96d56Sopenharmony_ci    }
15607db96d56Sopenharmony_ci
15617db96d56Sopenharmony_ci    if (!env->executablePath) {
15627db96d56Sopenharmony_ci        debug(L"# %s/%s has no executable path\n", env->company, env->tag);
15637db96d56Sopenharmony_ci        return RC_NO_PYTHON;
15647db96d56Sopenharmony_ci    }
15657db96d56Sopenharmony_ci
15667db96d56Sopenharmony_ci    exitCode = _registryReadString(&env->architecture, root, NULL, L"SysArchitecture");
15677db96d56Sopenharmony_ci    if (exitCode) {
15687db96d56Sopenharmony_ci        return exitCode;
15697db96d56Sopenharmony_ci    }
15707db96d56Sopenharmony_ci
15717db96d56Sopenharmony_ci    exitCode = _registryReadString(&env->displayName, root, NULL, L"DisplayName");
15727db96d56Sopenharmony_ci    if (exitCode) {
15737db96d56Sopenharmony_ci        return exitCode;
15747db96d56Sopenharmony_ci    }
15757db96d56Sopenharmony_ci
15767db96d56Sopenharmony_ci    return 0;
15777db96d56Sopenharmony_ci}
15787db96d56Sopenharmony_ci
15797db96d56Sopenharmony_ciint
15807db96d56Sopenharmony_ci_registrySearchTags(const SearchInfo *search, EnvironmentInfo **result, HKEY root, int sortKey, const wchar_t *company, const wchar_t *fallbackArch)
15817db96d56Sopenharmony_ci{
15827db96d56Sopenharmony_ci    wchar_t buffer[256];
15837db96d56Sopenharmony_ci    int err = 0;
15847db96d56Sopenharmony_ci    int exitCode = 0;
15857db96d56Sopenharmony_ci    for (int i = 0; exitCode == 0; ++i) {
15867db96d56Sopenharmony_ci        DWORD cchBuffer = sizeof(buffer) / sizeof(buffer[0]);
15877db96d56Sopenharmony_ci        err = RegEnumKeyExW(root, i, buffer, &cchBuffer, NULL, NULL, NULL, NULL);
15887db96d56Sopenharmony_ci        if (err) {
15897db96d56Sopenharmony_ci            if (err != ERROR_NO_MORE_ITEMS) {
15907db96d56Sopenharmony_ci                winerror(0, L"Failed to read installs (tags) from the registry");
15917db96d56Sopenharmony_ci            }
15927db96d56Sopenharmony_ci            break;
15937db96d56Sopenharmony_ci        }
15947db96d56Sopenharmony_ci        HKEY subkey;
15957db96d56Sopenharmony_ci        if (ERROR_SUCCESS == RegOpenKeyExW(root, buffer, 0, KEY_READ, &subkey)) {
15967db96d56Sopenharmony_ci            EnvironmentInfo *env = newEnvironmentInfo(company, buffer);
15977db96d56Sopenharmony_ci            env->internalSortKey = sortKey;
15987db96d56Sopenharmony_ci            exitCode = _registryReadEnvironment(search, subkey, env, fallbackArch);
15997db96d56Sopenharmony_ci            RegCloseKey(subkey);
16007db96d56Sopenharmony_ci            if (exitCode == RC_NO_PYTHON) {
16017db96d56Sopenharmony_ci                freeEnvironmentInfo(env);
16027db96d56Sopenharmony_ci                exitCode = 0;
16037db96d56Sopenharmony_ci            } else if (!exitCode) {
16047db96d56Sopenharmony_ci                exitCode = addEnvironmentInfo(result, NULL, env);
16057db96d56Sopenharmony_ci                if (exitCode) {
16067db96d56Sopenharmony_ci                    freeEnvironmentInfo(env);
16077db96d56Sopenharmony_ci                    if (exitCode == RC_DUPLICATE_ITEM) {
16087db96d56Sopenharmony_ci                        exitCode = 0;
16097db96d56Sopenharmony_ci                    }
16107db96d56Sopenharmony_ci                }
16117db96d56Sopenharmony_ci            }
16127db96d56Sopenharmony_ci        }
16137db96d56Sopenharmony_ci    }
16147db96d56Sopenharmony_ci    return exitCode;
16157db96d56Sopenharmony_ci}
16167db96d56Sopenharmony_ci
16177db96d56Sopenharmony_ci
16187db96d56Sopenharmony_ciint
16197db96d56Sopenharmony_ciregistrySearch(const SearchInfo *search, EnvironmentInfo **result, HKEY root, int sortKey, const wchar_t *fallbackArch)
16207db96d56Sopenharmony_ci{
16217db96d56Sopenharmony_ci    wchar_t buffer[256];
16227db96d56Sopenharmony_ci    int err = 0;
16237db96d56Sopenharmony_ci    int exitCode = 0;
16247db96d56Sopenharmony_ci    for (int i = 0; exitCode == 0; ++i) {
16257db96d56Sopenharmony_ci        DWORD cchBuffer = sizeof(buffer) / sizeof(buffer[0]);
16267db96d56Sopenharmony_ci        err = RegEnumKeyExW(root, i, buffer, &cchBuffer, NULL, NULL, NULL, NULL);
16277db96d56Sopenharmony_ci        if (err) {
16287db96d56Sopenharmony_ci            if (err != ERROR_NO_MORE_ITEMS) {
16297db96d56Sopenharmony_ci                winerror(0, L"Failed to read distributors (company) from the registry");
16307db96d56Sopenharmony_ci            }
16317db96d56Sopenharmony_ci            break;
16327db96d56Sopenharmony_ci        }
16337db96d56Sopenharmony_ci        if (search->limitToCompany && 0 != _compare(search->limitToCompany, -1, buffer, cchBuffer)) {
16347db96d56Sopenharmony_ci            debug(L"# Skipping %s due to PYLAUNCHER_LIMIT_TO_COMPANY\n", buffer);
16357db96d56Sopenharmony_ci            continue;
16367db96d56Sopenharmony_ci        }
16377db96d56Sopenharmony_ci        HKEY subkey;
16387db96d56Sopenharmony_ci        if (ERROR_SUCCESS == RegOpenKeyExW(root, buffer, 0, KEY_READ, &subkey)) {
16397db96d56Sopenharmony_ci            exitCode = _registrySearchTags(search, result, subkey, sortKey, buffer, fallbackArch);
16407db96d56Sopenharmony_ci            RegCloseKey(subkey);
16417db96d56Sopenharmony_ci        }
16427db96d56Sopenharmony_ci    }
16437db96d56Sopenharmony_ci    return exitCode;
16447db96d56Sopenharmony_ci}
16457db96d56Sopenharmony_ci
16467db96d56Sopenharmony_ci
16477db96d56Sopenharmony_ci/******************************************************************************\
16487db96d56Sopenharmony_ci ***                            APP PACKAGE SEARCH                          ***
16497db96d56Sopenharmony_ci\******************************************************************************/
16507db96d56Sopenharmony_ci
16517db96d56Sopenharmony_ciint
16527db96d56Sopenharmony_ciappxSearch(const SearchInfo *search, EnvironmentInfo **result, const wchar_t *packageFamilyName, const wchar_t *tag, int sortKey)
16537db96d56Sopenharmony_ci{
16547db96d56Sopenharmony_ci    wchar_t realTag[32];
16557db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
16567db96d56Sopenharmony_ci    const wchar_t *exeName = search->executable;
16577db96d56Sopenharmony_ci    if (!exeName || search->allowExecutableOverride) {
16587db96d56Sopenharmony_ci        exeName = search->windowed ? L"pythonw.exe" : L"python.exe";
16597db96d56Sopenharmony_ci    }
16607db96d56Sopenharmony_ci
16617db96d56Sopenharmony_ci    if (FAILED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, buffer)) ||
16627db96d56Sopenharmony_ci        !join(buffer, MAXLEN, L"Microsoft\\WindowsApps") ||
16637db96d56Sopenharmony_ci        !join(buffer, MAXLEN, packageFamilyName) ||
16647db96d56Sopenharmony_ci        !join(buffer, MAXLEN, exeName)) {
16657db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
16667db96d56Sopenharmony_ci    }
16677db96d56Sopenharmony_ci
16687db96d56Sopenharmony_ci    if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(buffer)) {
16697db96d56Sopenharmony_ci        return RC_NO_PYTHON;
16707db96d56Sopenharmony_ci    }
16717db96d56Sopenharmony_ci
16727db96d56Sopenharmony_ci    // Assume packages are native architecture, which means we need to append
16737db96d56Sopenharmony_ci    // the '-arm64' on ARM64 host.
16747db96d56Sopenharmony_ci    wcscpy_s(realTag, 32, tag);
16757db96d56Sopenharmony_ci    if (isARM64Host()) {
16767db96d56Sopenharmony_ci        wcscat_s(realTag, 32, L"-arm64");
16777db96d56Sopenharmony_ci    }
16787db96d56Sopenharmony_ci
16797db96d56Sopenharmony_ci    EnvironmentInfo *env = newEnvironmentInfo(L"PythonCore", realTag);
16807db96d56Sopenharmony_ci    if (!env) {
16817db96d56Sopenharmony_ci        return RC_NO_MEMORY;
16827db96d56Sopenharmony_ci    }
16837db96d56Sopenharmony_ci    env->internalSortKey = sortKey;
16847db96d56Sopenharmony_ci    if (isAMD64Host()) {
16857db96d56Sopenharmony_ci        copyWstr(&env->architecture, L"64bit");
16867db96d56Sopenharmony_ci    } else if (isARM64Host()) {
16877db96d56Sopenharmony_ci        copyWstr(&env->architecture, L"ARM64");
16887db96d56Sopenharmony_ci    }
16897db96d56Sopenharmony_ci
16907db96d56Sopenharmony_ci    copyWstr(&env->executablePath, buffer);
16917db96d56Sopenharmony_ci
16927db96d56Sopenharmony_ci    if (swprintf_s(buffer, MAXLEN, L"Python %s (Store)", tag)) {
16937db96d56Sopenharmony_ci        copyWstr(&env->displayName, buffer);
16947db96d56Sopenharmony_ci    }
16957db96d56Sopenharmony_ci
16967db96d56Sopenharmony_ci    int exitCode = addEnvironmentInfo(result, NULL, env);
16977db96d56Sopenharmony_ci    if (exitCode) {
16987db96d56Sopenharmony_ci        freeEnvironmentInfo(env);
16997db96d56Sopenharmony_ci        if (exitCode == RC_DUPLICATE_ITEM) {
17007db96d56Sopenharmony_ci            exitCode = 0;
17017db96d56Sopenharmony_ci        }
17027db96d56Sopenharmony_ci    }
17037db96d56Sopenharmony_ci
17047db96d56Sopenharmony_ci
17057db96d56Sopenharmony_ci    return exitCode;
17067db96d56Sopenharmony_ci}
17077db96d56Sopenharmony_ci
17087db96d56Sopenharmony_ci
17097db96d56Sopenharmony_ci/******************************************************************************\
17107db96d56Sopenharmony_ci ***                      OVERRIDDEN EXECUTABLE PATH                        ***
17117db96d56Sopenharmony_ci\******************************************************************************/
17127db96d56Sopenharmony_ci
17137db96d56Sopenharmony_ci
17147db96d56Sopenharmony_ciint
17157db96d56Sopenharmony_ciexplicitOverrideSearch(const SearchInfo *search, EnvironmentInfo **result)
17167db96d56Sopenharmony_ci{
17177db96d56Sopenharmony_ci    if (!search->executablePath) {
17187db96d56Sopenharmony_ci        return 0;
17197db96d56Sopenharmony_ci    }
17207db96d56Sopenharmony_ci
17217db96d56Sopenharmony_ci    EnvironmentInfo *env = newEnvironmentInfo(NULL, NULL);
17227db96d56Sopenharmony_ci    if (!env) {
17237db96d56Sopenharmony_ci        return RC_NO_MEMORY;
17247db96d56Sopenharmony_ci    }
17257db96d56Sopenharmony_ci    env->internalSortKey = 10;
17267db96d56Sopenharmony_ci    int exitCode = copyWstr(&env->executablePath, search->executablePath);
17277db96d56Sopenharmony_ci    if (exitCode) {
17287db96d56Sopenharmony_ci        goto abort;
17297db96d56Sopenharmony_ci    }
17307db96d56Sopenharmony_ci    exitCode = copyWstr(&env->displayName, L"Explicit override");
17317db96d56Sopenharmony_ci    if (exitCode) {
17327db96d56Sopenharmony_ci        goto abort;
17337db96d56Sopenharmony_ci    }
17347db96d56Sopenharmony_ci    exitCode = addEnvironmentInfo(result, NULL, env);
17357db96d56Sopenharmony_ci    if (exitCode) {
17367db96d56Sopenharmony_ci        goto abort;
17377db96d56Sopenharmony_ci    }
17387db96d56Sopenharmony_ci    return 0;
17397db96d56Sopenharmony_ci
17407db96d56Sopenharmony_ciabort:
17417db96d56Sopenharmony_ci    freeEnvironmentInfo(env);
17427db96d56Sopenharmony_ci    if (exitCode == RC_DUPLICATE_ITEM) {
17437db96d56Sopenharmony_ci        exitCode = 0;
17447db96d56Sopenharmony_ci    }
17457db96d56Sopenharmony_ci    return exitCode;
17467db96d56Sopenharmony_ci}
17477db96d56Sopenharmony_ci
17487db96d56Sopenharmony_ci
17497db96d56Sopenharmony_ci/******************************************************************************\
17507db96d56Sopenharmony_ci ***                   ACTIVE VIRTUAL ENVIRONMENT SEARCH                    ***
17517db96d56Sopenharmony_ci\******************************************************************************/
17527db96d56Sopenharmony_ci
17537db96d56Sopenharmony_ciint
17547db96d56Sopenharmony_civirtualenvSearch(const SearchInfo *search, EnvironmentInfo **result)
17557db96d56Sopenharmony_ci{
17567db96d56Sopenharmony_ci    int exitCode = 0;
17577db96d56Sopenharmony_ci    EnvironmentInfo *env = NULL;
17587db96d56Sopenharmony_ci    wchar_t buffer[MAXLEN];
17597db96d56Sopenharmony_ci    int n = GetEnvironmentVariableW(L"VIRTUAL_ENV", buffer, MAXLEN);
17607db96d56Sopenharmony_ci    if (!n || !join(buffer, MAXLEN, L"Scripts") || !join(buffer, MAXLEN, search->executable)) {
17617db96d56Sopenharmony_ci        return 0;
17627db96d56Sopenharmony_ci    }
17637db96d56Sopenharmony_ci
17647db96d56Sopenharmony_ci    if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(buffer)) {
17657db96d56Sopenharmony_ci        debug(L"Python executable %s missing from virtual env\n", buffer);
17667db96d56Sopenharmony_ci        return 0;
17677db96d56Sopenharmony_ci    }
17687db96d56Sopenharmony_ci
17697db96d56Sopenharmony_ci    env = newEnvironmentInfo(NULL, NULL);
17707db96d56Sopenharmony_ci    if (!env) {
17717db96d56Sopenharmony_ci        return RC_NO_MEMORY;
17727db96d56Sopenharmony_ci    }
17737db96d56Sopenharmony_ci    env->highPriority = true;
17747db96d56Sopenharmony_ci    env->internalSortKey = 20;
17757db96d56Sopenharmony_ci    exitCode = copyWstr(&env->displayName, L"Active venv");
17767db96d56Sopenharmony_ci    if (exitCode) {
17777db96d56Sopenharmony_ci        goto abort;
17787db96d56Sopenharmony_ci    }
17797db96d56Sopenharmony_ci    exitCode = copyWstr(&env->executablePath, buffer);
17807db96d56Sopenharmony_ci    if (exitCode) {
17817db96d56Sopenharmony_ci        goto abort;
17827db96d56Sopenharmony_ci    }
17837db96d56Sopenharmony_ci    exitCode = addEnvironmentInfo(result, NULL, env);
17847db96d56Sopenharmony_ci    if (exitCode) {
17857db96d56Sopenharmony_ci        goto abort;
17867db96d56Sopenharmony_ci    }
17877db96d56Sopenharmony_ci    return 0;
17887db96d56Sopenharmony_ci
17897db96d56Sopenharmony_ciabort:
17907db96d56Sopenharmony_ci    freeEnvironmentInfo(env);
17917db96d56Sopenharmony_ci    if (exitCode == RC_DUPLICATE_ITEM) {
17927db96d56Sopenharmony_ci        return 0;
17937db96d56Sopenharmony_ci    }
17947db96d56Sopenharmony_ci    return exitCode;
17957db96d56Sopenharmony_ci}
17967db96d56Sopenharmony_ci
17977db96d56Sopenharmony_ci/******************************************************************************\
17987db96d56Sopenharmony_ci ***                           COLLECT ENVIRONMENTS                         ***
17997db96d56Sopenharmony_ci\******************************************************************************/
18007db96d56Sopenharmony_ci
18017db96d56Sopenharmony_ci
18027db96d56Sopenharmony_cistruct RegistrySearchInfo {
18037db96d56Sopenharmony_ci    // Registry subkey to search
18047db96d56Sopenharmony_ci    const wchar_t *subkey;
18057db96d56Sopenharmony_ci    // Registry hive to search
18067db96d56Sopenharmony_ci    HKEY hive;
18077db96d56Sopenharmony_ci    // Flags to use when opening the subkey
18087db96d56Sopenharmony_ci    DWORD flags;
18097db96d56Sopenharmony_ci    // Internal sort key to select between "identical" environments discovered
18107db96d56Sopenharmony_ci    // through different methods
18117db96d56Sopenharmony_ci    int sortKey;
18127db96d56Sopenharmony_ci    // Fallback value to assume for PythonCore entries missing a SysArchitecture value
18137db96d56Sopenharmony_ci    const wchar_t *fallbackArch;
18147db96d56Sopenharmony_ci};
18157db96d56Sopenharmony_ci
18167db96d56Sopenharmony_ci
18177db96d56Sopenharmony_cistruct RegistrySearchInfo REGISTRY_SEARCH[] = {
18187db96d56Sopenharmony_ci    {
18197db96d56Sopenharmony_ci        L"Software\\Python",
18207db96d56Sopenharmony_ci        HKEY_CURRENT_USER,
18217db96d56Sopenharmony_ci        KEY_READ,
18227db96d56Sopenharmony_ci        1,
18237db96d56Sopenharmony_ci        NULL
18247db96d56Sopenharmony_ci    },
18257db96d56Sopenharmony_ci    {
18267db96d56Sopenharmony_ci        L"Software\\Python",
18277db96d56Sopenharmony_ci        HKEY_LOCAL_MACHINE,
18287db96d56Sopenharmony_ci        KEY_READ | KEY_WOW64_64KEY,
18297db96d56Sopenharmony_ci        3,
18307db96d56Sopenharmony_ci        L"64bit"
18317db96d56Sopenharmony_ci    },
18327db96d56Sopenharmony_ci    {
18337db96d56Sopenharmony_ci        L"Software\\Python",
18347db96d56Sopenharmony_ci        HKEY_LOCAL_MACHINE,
18357db96d56Sopenharmony_ci        KEY_READ | KEY_WOW64_32KEY,
18367db96d56Sopenharmony_ci        4,
18377db96d56Sopenharmony_ci        L"32bit"
18387db96d56Sopenharmony_ci    },
18397db96d56Sopenharmony_ci    { NULL, 0, 0, 0, NULL }
18407db96d56Sopenharmony_ci};
18417db96d56Sopenharmony_ci
18427db96d56Sopenharmony_ci
18437db96d56Sopenharmony_cistruct AppxSearchInfo {
18447db96d56Sopenharmony_ci    // The package family name. Can be found for an installed package using the
18457db96d56Sopenharmony_ci    // Powershell "Get-AppxPackage" cmdlet
18467db96d56Sopenharmony_ci    const wchar_t *familyName;
18477db96d56Sopenharmony_ci    // The tag to treat the installation as
18487db96d56Sopenharmony_ci    const wchar_t *tag;
18497db96d56Sopenharmony_ci    // Internal sort key to select between "identical" environments discovered
18507db96d56Sopenharmony_ci    // through different methods
18517db96d56Sopenharmony_ci    int sortKey;
18527db96d56Sopenharmony_ci};
18537db96d56Sopenharmony_ci
18547db96d56Sopenharmony_ci
18557db96d56Sopenharmony_cistruct AppxSearchInfo APPX_SEARCH[] = {
18567db96d56Sopenharmony_ci    // Releases made through the Store
18577db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0", L"3.12", 10 },
18587db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0", L"3.11", 10 },
18597db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.10_qbz5n2kfra8p0", L"3.10", 10 },
18607db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0", L"3.9", 10 },
18617db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.8_qbz5n2kfra8p0", L"3.8", 10 },
18627db96d56Sopenharmony_ci
18637db96d56Sopenharmony_ci    // Side-loadable releases. Note that the publisher ID changes whenever we
18647db96d56Sopenharmony_ci    // renew our code-signing certificate, so the newer ID has a higher
18657db96d56Sopenharmony_ci    // priority (lower sortKey)
18667db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.12_3847v3x7pw1km", L"3.12", 11 },
18677db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.11_3847v3x7pw1km", L"3.11", 11 },
18687db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.11_hd69rhyc2wevp", L"3.11", 12 },
18697db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.10_3847v3x7pw1km", L"3.10", 11 },
18707db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.10_hd69rhyc2wevp", L"3.10", 12 },
18717db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.9_3847v3x7pw1km", L"3.9", 11 },
18727db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.9_hd69rhyc2wevp", L"3.9", 12 },
18737db96d56Sopenharmony_ci    { L"PythonSoftwareFoundation.Python.3.8_hd69rhyc2wevp", L"3.8", 12 },
18747db96d56Sopenharmony_ci    { NULL, NULL, 0 }
18757db96d56Sopenharmony_ci};
18767db96d56Sopenharmony_ci
18777db96d56Sopenharmony_ci
18787db96d56Sopenharmony_ciint
18797db96d56Sopenharmony_cicollectEnvironments(const SearchInfo *search, EnvironmentInfo **result)
18807db96d56Sopenharmony_ci{
18817db96d56Sopenharmony_ci    int exitCode = 0;
18827db96d56Sopenharmony_ci    HKEY root;
18837db96d56Sopenharmony_ci    EnvironmentInfo *env = NULL;
18847db96d56Sopenharmony_ci
18857db96d56Sopenharmony_ci    if (!result) {
18867db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
18877db96d56Sopenharmony_ci    }
18887db96d56Sopenharmony_ci    *result = NULL;
18897db96d56Sopenharmony_ci
18907db96d56Sopenharmony_ci    exitCode = explicitOverrideSearch(search, result);
18917db96d56Sopenharmony_ci    if (exitCode) {
18927db96d56Sopenharmony_ci        return exitCode;
18937db96d56Sopenharmony_ci    }
18947db96d56Sopenharmony_ci
18957db96d56Sopenharmony_ci    exitCode = virtualenvSearch(search, result);
18967db96d56Sopenharmony_ci    if (exitCode) {
18977db96d56Sopenharmony_ci        return exitCode;
18987db96d56Sopenharmony_ci    }
18997db96d56Sopenharmony_ci
19007db96d56Sopenharmony_ci    // If we aren't collecting all items to list them, we can exit now.
19017db96d56Sopenharmony_ci    if (env && !(search->list || search->listPaths)) {
19027db96d56Sopenharmony_ci        return 0;
19037db96d56Sopenharmony_ci    }
19047db96d56Sopenharmony_ci
19057db96d56Sopenharmony_ci    for (struct RegistrySearchInfo *info = REGISTRY_SEARCH; info->subkey; ++info) {
19067db96d56Sopenharmony_ci        if (ERROR_SUCCESS == RegOpenKeyExW(info->hive, info->subkey, 0, info->flags, &root)) {
19077db96d56Sopenharmony_ci            exitCode = registrySearch(search, result, root, info->sortKey, info->fallbackArch);
19087db96d56Sopenharmony_ci            RegCloseKey(root);
19097db96d56Sopenharmony_ci        }
19107db96d56Sopenharmony_ci        if (exitCode) {
19117db96d56Sopenharmony_ci            return exitCode;
19127db96d56Sopenharmony_ci        }
19137db96d56Sopenharmony_ci    }
19147db96d56Sopenharmony_ci
19157db96d56Sopenharmony_ci    if (search->limitToCompany) {
19167db96d56Sopenharmony_ci        debug(L"# Skipping APPX search due to PYLAUNCHER_LIMIT_TO_COMPANY\n");
19177db96d56Sopenharmony_ci        return 0;
19187db96d56Sopenharmony_ci    }
19197db96d56Sopenharmony_ci
19207db96d56Sopenharmony_ci    for (struct AppxSearchInfo *info = APPX_SEARCH; info->familyName; ++info) {
19217db96d56Sopenharmony_ci        exitCode = appxSearch(search, result, info->familyName, info->tag, info->sortKey);
19227db96d56Sopenharmony_ci        if (exitCode && exitCode != RC_NO_PYTHON) {
19237db96d56Sopenharmony_ci            return exitCode;
19247db96d56Sopenharmony_ci        }
19257db96d56Sopenharmony_ci    }
19267db96d56Sopenharmony_ci
19277db96d56Sopenharmony_ci    return 0;
19287db96d56Sopenharmony_ci}
19297db96d56Sopenharmony_ci
19307db96d56Sopenharmony_ci
19317db96d56Sopenharmony_ci/******************************************************************************\
19327db96d56Sopenharmony_ci ***                           INSTALL ON DEMAND                            ***
19337db96d56Sopenharmony_ci\******************************************************************************/
19347db96d56Sopenharmony_ci
19357db96d56Sopenharmony_cistruct StoreSearchInfo {
19367db96d56Sopenharmony_ci    // The tag a user is looking for
19377db96d56Sopenharmony_ci    const wchar_t *tag;
19387db96d56Sopenharmony_ci    // The Store ID for a package if it can be installed from the Microsoft
19397db96d56Sopenharmony_ci    // Store. These are obtained from the dashboard at
19407db96d56Sopenharmony_ci    // https://partner.microsoft.com/dashboard
19417db96d56Sopenharmony_ci    const wchar_t *storeId;
19427db96d56Sopenharmony_ci};
19437db96d56Sopenharmony_ci
19447db96d56Sopenharmony_ci
19457db96d56Sopenharmony_cistruct StoreSearchInfo STORE_SEARCH[] = {
19467db96d56Sopenharmony_ci    { L"3", /* 3.11 */ L"9NRWMJP3717K" },
19477db96d56Sopenharmony_ci    { L"3.12", L"9NCVDN91XZQP" },
19487db96d56Sopenharmony_ci    { L"3.11", L"9NRWMJP3717K" },
19497db96d56Sopenharmony_ci    { L"3.10", L"9PJPW5LDXLZ5" },
19507db96d56Sopenharmony_ci    { L"3.9", L"9P7QFQMJRFP7" },
19517db96d56Sopenharmony_ci    { L"3.8", L"9MSSZTT1N39L" },
19527db96d56Sopenharmony_ci    { NULL, NULL }
19537db96d56Sopenharmony_ci};
19547db96d56Sopenharmony_ci
19557db96d56Sopenharmony_ci
19567db96d56Sopenharmony_ciint
19577db96d56Sopenharmony_ci_installEnvironment(const wchar_t *command, const wchar_t *arguments)
19587db96d56Sopenharmony_ci{
19597db96d56Sopenharmony_ci    SHELLEXECUTEINFOW siw = {
19607db96d56Sopenharmony_ci        sizeof(SHELLEXECUTEINFOW),
19617db96d56Sopenharmony_ci        SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE,
19627db96d56Sopenharmony_ci        NULL, NULL,
19637db96d56Sopenharmony_ci        command, arguments, NULL,
19647db96d56Sopenharmony_ci        SW_SHOWNORMAL
19657db96d56Sopenharmony_ci    };
19667db96d56Sopenharmony_ci
19677db96d56Sopenharmony_ci    debug(L"# Installing with %s %s\n", command, arguments);
19687db96d56Sopenharmony_ci    if (isEnvVarSet(L"PYLAUNCHER_DRYRUN")) {
19697db96d56Sopenharmony_ci        debug(L"# Exiting due to PYLAUNCHER_DRYRUN\n");
19707db96d56Sopenharmony_ci        fflush(stdout);
19717db96d56Sopenharmony_ci        int mode = _setmode(_fileno(stdout), _O_U8TEXT);
19727db96d56Sopenharmony_ci        if (arguments) {
19737db96d56Sopenharmony_ci            fwprintf_s(stdout, L"\"%s\" %s\n", command, arguments);
19747db96d56Sopenharmony_ci        } else {
19757db96d56Sopenharmony_ci            fwprintf_s(stdout, L"\"%s\"\n", command);
19767db96d56Sopenharmony_ci        }
19777db96d56Sopenharmony_ci        fflush(stdout);
19787db96d56Sopenharmony_ci        if (mode >= 0) {
19797db96d56Sopenharmony_ci            _setmode(_fileno(stdout), mode);
19807db96d56Sopenharmony_ci        }
19817db96d56Sopenharmony_ci        return RC_INSTALLING;
19827db96d56Sopenharmony_ci    }
19837db96d56Sopenharmony_ci
19847db96d56Sopenharmony_ci    if (!ShellExecuteExW(&siw)) {
19857db96d56Sopenharmony_ci        return RC_NO_PYTHON;
19867db96d56Sopenharmony_ci    }
19877db96d56Sopenharmony_ci
19887db96d56Sopenharmony_ci    if (!siw.hProcess) {
19897db96d56Sopenharmony_ci        return RC_INSTALLING;
19907db96d56Sopenharmony_ci    }
19917db96d56Sopenharmony_ci
19927db96d56Sopenharmony_ci    WaitForSingleObjectEx(siw.hProcess, INFINITE, FALSE);
19937db96d56Sopenharmony_ci    DWORD exitCode = 0;
19947db96d56Sopenharmony_ci    if (GetExitCodeProcess(siw.hProcess, &exitCode) && exitCode == 0) {
19957db96d56Sopenharmony_ci        return 0;
19967db96d56Sopenharmony_ci    }
19977db96d56Sopenharmony_ci    return RC_INSTALLING;
19987db96d56Sopenharmony_ci}
19997db96d56Sopenharmony_ci
20007db96d56Sopenharmony_ci
20017db96d56Sopenharmony_ciconst wchar_t *WINGET_COMMAND = L"Microsoft\\WindowsApps\\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\\winget.exe";
20027db96d56Sopenharmony_ciconst wchar_t *WINGET_ARGUMENTS = L"install -q %s --exact --accept-package-agreements --source msstore";
20037db96d56Sopenharmony_ci
20047db96d56Sopenharmony_ciconst wchar_t *MSSTORE_COMMAND = L"ms-windows-store://pdp/?productid=%s";
20057db96d56Sopenharmony_ci
20067db96d56Sopenharmony_ciint
20077db96d56Sopenharmony_ciinstallEnvironment(const SearchInfo *search)
20087db96d56Sopenharmony_ci{
20097db96d56Sopenharmony_ci    // No tag? No installing
20107db96d56Sopenharmony_ci    if (!search->tag || !search->tagLength) {
20117db96d56Sopenharmony_ci        debug(L"# Cannot install Python with no tag specified\n");
20127db96d56Sopenharmony_ci        return RC_NO_PYTHON;
20137db96d56Sopenharmony_ci    }
20147db96d56Sopenharmony_ci
20157db96d56Sopenharmony_ci    // PEP 514 tag but not PythonCore? No installing
20167db96d56Sopenharmony_ci    if (!search->oldStyleTag &&
20177db96d56Sopenharmony_ci        search->company && search->companyLength &&
20187db96d56Sopenharmony_ci        0 != _compare(search->company, search->companyLength, L"PythonCore", -1)) {
20197db96d56Sopenharmony_ci        debug(L"# Cannot install for company %.*s\n", search->companyLength, search->company);
20207db96d56Sopenharmony_ci        return RC_NO_PYTHON;
20217db96d56Sopenharmony_ci    }
20227db96d56Sopenharmony_ci
20237db96d56Sopenharmony_ci    const wchar_t *storeId = NULL;
20247db96d56Sopenharmony_ci    for (struct StoreSearchInfo *info = STORE_SEARCH; info->tag; ++info) {
20257db96d56Sopenharmony_ci        if (0 == _compare(search->tag, search->tagLength, info->tag, -1)) {
20267db96d56Sopenharmony_ci            storeId = info->storeId;
20277db96d56Sopenharmony_ci            break;
20287db96d56Sopenharmony_ci        }
20297db96d56Sopenharmony_ci    }
20307db96d56Sopenharmony_ci
20317db96d56Sopenharmony_ci    if (!storeId) {
20327db96d56Sopenharmony_ci        return RC_NO_PYTHON;
20337db96d56Sopenharmony_ci    }
20347db96d56Sopenharmony_ci
20357db96d56Sopenharmony_ci    int exitCode;
20367db96d56Sopenharmony_ci    wchar_t command[MAXLEN];
20377db96d56Sopenharmony_ci    wchar_t arguments[MAXLEN];
20387db96d56Sopenharmony_ci    if (SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, command)) &&
20397db96d56Sopenharmony_ci        join(command, MAXLEN, WINGET_COMMAND) &&
20407db96d56Sopenharmony_ci        swprintf_s(arguments, MAXLEN, WINGET_ARGUMENTS, storeId)) {
20417db96d56Sopenharmony_ci        if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(command)) {
20427db96d56Sopenharmony_ci            formatWinerror(GetLastError(), arguments, MAXLEN);
20437db96d56Sopenharmony_ci            debug(L"# Skipping %s: %s\n", command, arguments);
20447db96d56Sopenharmony_ci        } else {
20457db96d56Sopenharmony_ci            fputws(L"Launching winget to install Python. The following output is from the install process\n\
20467db96d56Sopenharmony_ci***********************************************************************\n", stdout);
20477db96d56Sopenharmony_ci            exitCode = _installEnvironment(command, arguments);
20487db96d56Sopenharmony_ci            if (exitCode == RC_INSTALLING) {
20497db96d56Sopenharmony_ci                fputws(L"***********************************************************************\n\
20507db96d56Sopenharmony_ciPlease check the install status and run your command again.", stderr);
20517db96d56Sopenharmony_ci                return exitCode;
20527db96d56Sopenharmony_ci            } else if (exitCode) {
20537db96d56Sopenharmony_ci                return exitCode;
20547db96d56Sopenharmony_ci            }
20557db96d56Sopenharmony_ci            fputws(L"***********************************************************************\n\
20567db96d56Sopenharmony_ciInstall appears to have succeeded. Searching for new matching installs.\n", stdout);
20577db96d56Sopenharmony_ci            return 0;
20587db96d56Sopenharmony_ci        }
20597db96d56Sopenharmony_ci    }
20607db96d56Sopenharmony_ci
20617db96d56Sopenharmony_ci    if (swprintf_s(command, MAXLEN, MSSTORE_COMMAND, storeId)) {
20627db96d56Sopenharmony_ci        fputws(L"Opening the Microsoft Store to install Python. After installation, "
20637db96d56Sopenharmony_ci               L"please run your command again.\n", stderr);
20647db96d56Sopenharmony_ci        exitCode = _installEnvironment(command, NULL);
20657db96d56Sopenharmony_ci        if (exitCode) {
20667db96d56Sopenharmony_ci            return exitCode;
20677db96d56Sopenharmony_ci        }
20687db96d56Sopenharmony_ci        return 0;
20697db96d56Sopenharmony_ci    }
20707db96d56Sopenharmony_ci
20717db96d56Sopenharmony_ci    return RC_NO_PYTHON;
20727db96d56Sopenharmony_ci}
20737db96d56Sopenharmony_ci
20747db96d56Sopenharmony_ci/******************************************************************************\
20757db96d56Sopenharmony_ci ***                           ENVIRONMENT SELECT                           ***
20767db96d56Sopenharmony_ci\******************************************************************************/
20777db96d56Sopenharmony_ci
20787db96d56Sopenharmony_cibool
20797db96d56Sopenharmony_ci_companyMatches(const SearchInfo *search, const EnvironmentInfo *env)
20807db96d56Sopenharmony_ci{
20817db96d56Sopenharmony_ci    if (!search->company || !search->companyLength) {
20827db96d56Sopenharmony_ci        return true;
20837db96d56Sopenharmony_ci    }
20847db96d56Sopenharmony_ci    return 0 == _compare(env->company, -1, search->company, search->companyLength);
20857db96d56Sopenharmony_ci}
20867db96d56Sopenharmony_ci
20877db96d56Sopenharmony_ci
20887db96d56Sopenharmony_cibool
20897db96d56Sopenharmony_ci_tagMatches(const SearchInfo *search, const EnvironmentInfo *env, int searchTagLength)
20907db96d56Sopenharmony_ci{
20917db96d56Sopenharmony_ci    if (searchTagLength < 0) {
20927db96d56Sopenharmony_ci        searchTagLength = search->tagLength;
20937db96d56Sopenharmony_ci    }
20947db96d56Sopenharmony_ci    if (!search->tag || !searchTagLength) {
20957db96d56Sopenharmony_ci        return true;
20967db96d56Sopenharmony_ci    }
20977db96d56Sopenharmony_ci    return _startsWithSeparated(env->tag, -1, search->tag, searchTagLength, L".-");
20987db96d56Sopenharmony_ci}
20997db96d56Sopenharmony_ci
21007db96d56Sopenharmony_ci
21017db96d56Sopenharmony_cibool
21027db96d56Sopenharmony_ci_is32Bit(const EnvironmentInfo *env)
21037db96d56Sopenharmony_ci{
21047db96d56Sopenharmony_ci    if (env->architecture) {
21057db96d56Sopenharmony_ci        return 0 == _compare(env->architecture, -1, L"32bit", -1);
21067db96d56Sopenharmony_ci    }
21077db96d56Sopenharmony_ci    return false;
21087db96d56Sopenharmony_ci}
21097db96d56Sopenharmony_ci
21107db96d56Sopenharmony_ci
21117db96d56Sopenharmony_ciint
21127db96d56Sopenharmony_ci_selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentInfo **best)
21137db96d56Sopenharmony_ci{
21147db96d56Sopenharmony_ci    int exitCode = 0;
21157db96d56Sopenharmony_ci    while (env) {
21167db96d56Sopenharmony_ci        exitCode = _selectEnvironment(search, env->prev, best);
21177db96d56Sopenharmony_ci
21187db96d56Sopenharmony_ci        if (exitCode && exitCode != RC_NO_PYTHON) {
21197db96d56Sopenharmony_ci            return exitCode;
21207db96d56Sopenharmony_ci        } else if (!exitCode && *best) {
21217db96d56Sopenharmony_ci            return 0;
21227db96d56Sopenharmony_ci        }
21237db96d56Sopenharmony_ci
21247db96d56Sopenharmony_ci        if (env->highPriority && search->lowPriorityTag) {
21257db96d56Sopenharmony_ci            // This environment is marked high priority, and the search allows
21267db96d56Sopenharmony_ci            // it to be selected even though a tag is specified, so select it
21277db96d56Sopenharmony_ci            // gh-92817: this allows an active venv to be selected even when a
21287db96d56Sopenharmony_ci            // default tag has been found in py.ini or the environment
21297db96d56Sopenharmony_ci            *best = env;
21307db96d56Sopenharmony_ci            return 0;
21317db96d56Sopenharmony_ci        }
21327db96d56Sopenharmony_ci
21337db96d56Sopenharmony_ci        if (!search->oldStyleTag) {
21347db96d56Sopenharmony_ci            if (_companyMatches(search, env) && _tagMatches(search, env, -1)) {
21357db96d56Sopenharmony_ci                // Because of how our sort tree is set up, we will walk up the
21367db96d56Sopenharmony_ci                // "prev" side and implicitly select the "best" best. By
21377db96d56Sopenharmony_ci                // returning straight after a match, we skip the entire "next"
21387db96d56Sopenharmony_ci                // branch and won't ever select a "worse" best.
21397db96d56Sopenharmony_ci                *best = env;
21407db96d56Sopenharmony_ci                return 0;
21417db96d56Sopenharmony_ci            }
21427db96d56Sopenharmony_ci        } else if (0 == _compare(env->company, -1, L"PythonCore", -1)) {
21437db96d56Sopenharmony_ci            // Old-style tags can only match PythonCore entries
21447db96d56Sopenharmony_ci
21457db96d56Sopenharmony_ci            // If the tag ends with -64, we want to exclude 32-bit runtimes
21467db96d56Sopenharmony_ci            // (If the tag ends with -32, it will be filtered later)
21477db96d56Sopenharmony_ci            int tagLength = search->tagLength;
21487db96d56Sopenharmony_ci            bool exclude32Bit = false, only32Bit = false;
21497db96d56Sopenharmony_ci            if (tagLength > 3) {
21507db96d56Sopenharmony_ci                if (0 == _compareArgument(&search->tag[tagLength - 3], 3, L"-64", 3)) {
21517db96d56Sopenharmony_ci                    tagLength -= 3;
21527db96d56Sopenharmony_ci                    exclude32Bit = true;
21537db96d56Sopenharmony_ci                } else if (0 == _compareArgument(&search->tag[tagLength - 3], 3, L"-32", 3)) {
21547db96d56Sopenharmony_ci                    tagLength -= 3;
21557db96d56Sopenharmony_ci                    only32Bit = true;
21567db96d56Sopenharmony_ci                }
21577db96d56Sopenharmony_ci            }
21587db96d56Sopenharmony_ci
21597db96d56Sopenharmony_ci            if (_tagMatches(search, env, tagLength)) {
21607db96d56Sopenharmony_ci                if (exclude32Bit && _is32Bit(env)) {
21617db96d56Sopenharmony_ci                    debug(L"# Excluding %s/%s because it looks like 32bit\n", env->company, env->tag);
21627db96d56Sopenharmony_ci                } else if (only32Bit && !_is32Bit(env)) {
21637db96d56Sopenharmony_ci                    debug(L"# Excluding %s/%s because it doesn't look 32bit\n", env->company, env->tag);
21647db96d56Sopenharmony_ci                } else {
21657db96d56Sopenharmony_ci                    *best = env;
21667db96d56Sopenharmony_ci                    return 0;
21677db96d56Sopenharmony_ci                }
21687db96d56Sopenharmony_ci            }
21697db96d56Sopenharmony_ci        }
21707db96d56Sopenharmony_ci
21717db96d56Sopenharmony_ci        env = env->next;
21727db96d56Sopenharmony_ci    }
21737db96d56Sopenharmony_ci    return RC_NO_PYTHON;
21747db96d56Sopenharmony_ci}
21757db96d56Sopenharmony_ci
21767db96d56Sopenharmony_ciint
21777db96d56Sopenharmony_ciselectEnvironment(const SearchInfo *search, EnvironmentInfo *root, EnvironmentInfo **best)
21787db96d56Sopenharmony_ci{
21797db96d56Sopenharmony_ci    if (!best) {
21807db96d56Sopenharmony_ci        return RC_INTERNAL_ERROR;
21817db96d56Sopenharmony_ci    }
21827db96d56Sopenharmony_ci    if (!root) {
21837db96d56Sopenharmony_ci        *best = NULL;
21847db96d56Sopenharmony_ci        return RC_NO_PYTHON_AT_ALL;
21857db96d56Sopenharmony_ci    }
21867db96d56Sopenharmony_ci
21877db96d56Sopenharmony_ci    EnvironmentInfo *result = NULL;
21887db96d56Sopenharmony_ci    int exitCode = _selectEnvironment(search, root, &result);
21897db96d56Sopenharmony_ci    if (!exitCode) {
21907db96d56Sopenharmony_ci        *best = result;
21917db96d56Sopenharmony_ci    }
21927db96d56Sopenharmony_ci
21937db96d56Sopenharmony_ci    return exitCode;
21947db96d56Sopenharmony_ci}
21957db96d56Sopenharmony_ci
21967db96d56Sopenharmony_ci
21977db96d56Sopenharmony_ci/******************************************************************************\
21987db96d56Sopenharmony_ci ***                            LIST ENVIRONMENTS                           ***
21997db96d56Sopenharmony_ci\******************************************************************************/
22007db96d56Sopenharmony_ci
22017db96d56Sopenharmony_ci#define TAGWIDTH 16
22027db96d56Sopenharmony_ci
22037db96d56Sopenharmony_ciint
22047db96d56Sopenharmony_ci_printEnvironment(const EnvironmentInfo *env, FILE *out, bool showPath, const wchar_t *argument)
22057db96d56Sopenharmony_ci{
22067db96d56Sopenharmony_ci    if (showPath) {
22077db96d56Sopenharmony_ci        if (env->executablePath && env->executablePath[0]) {
22087db96d56Sopenharmony_ci            if (env->executableArgs && env->executableArgs[0]) {
22097db96d56Sopenharmony_ci                fwprintf(out, L" %-*s %s %s\n", TAGWIDTH, argument, env->executablePath, env->executableArgs);
22107db96d56Sopenharmony_ci            } else {
22117db96d56Sopenharmony_ci                fwprintf(out, L" %-*s %s\n", TAGWIDTH, argument, env->executablePath);
22127db96d56Sopenharmony_ci            }
22137db96d56Sopenharmony_ci        } else if (env->installDir && env->installDir[0]) {
22147db96d56Sopenharmony_ci            fwprintf(out, L" %-*s %s\n", TAGWIDTH, argument, env->installDir);
22157db96d56Sopenharmony_ci        } else {
22167db96d56Sopenharmony_ci            fwprintf(out, L" %s\n", argument);
22177db96d56Sopenharmony_ci        }
22187db96d56Sopenharmony_ci    } else if (env->displayName) {
22197db96d56Sopenharmony_ci        fwprintf(out, L" %-*s %s\n", TAGWIDTH, argument, env->displayName);
22207db96d56Sopenharmony_ci    } else {
22217db96d56Sopenharmony_ci        fwprintf(out, L" %s\n", argument);
22227db96d56Sopenharmony_ci    }
22237db96d56Sopenharmony_ci    return 0;
22247db96d56Sopenharmony_ci}
22257db96d56Sopenharmony_ci
22267db96d56Sopenharmony_ci
22277db96d56Sopenharmony_ciint
22287db96d56Sopenharmony_ci_listAllEnvironments(EnvironmentInfo *env, FILE * out, bool showPath, EnvironmentInfo *defaultEnv)
22297db96d56Sopenharmony_ci{
22307db96d56Sopenharmony_ci    wchar_t buffer[256];
22317db96d56Sopenharmony_ci    const int bufferSize = 256;
22327db96d56Sopenharmony_ci    while (env) {
22337db96d56Sopenharmony_ci        int exitCode = _listAllEnvironments(env->prev, out, showPath, defaultEnv);
22347db96d56Sopenharmony_ci        if (exitCode) {
22357db96d56Sopenharmony_ci            return exitCode;
22367db96d56Sopenharmony_ci        }
22377db96d56Sopenharmony_ci
22387db96d56Sopenharmony_ci        if (!env->company || !env->tag) {
22397db96d56Sopenharmony_ci            buffer[0] = L'\0';
22407db96d56Sopenharmony_ci        } else if (0 == _compare(env->company, -1, L"PythonCore", -1)) {
22417db96d56Sopenharmony_ci            swprintf_s(buffer, bufferSize, L"-V:%s", env->tag);
22427db96d56Sopenharmony_ci        } else {
22437db96d56Sopenharmony_ci            swprintf_s(buffer, bufferSize, L"-V:%s/%s", env->company, env->tag);
22447db96d56Sopenharmony_ci        }
22457db96d56Sopenharmony_ci
22467db96d56Sopenharmony_ci        if (env == defaultEnv) {
22477db96d56Sopenharmony_ci            wcscat_s(buffer, bufferSize, L" *");
22487db96d56Sopenharmony_ci        }
22497db96d56Sopenharmony_ci
22507db96d56Sopenharmony_ci        if (buffer[0]) {
22517db96d56Sopenharmony_ci            exitCode = _printEnvironment(env, out, showPath, buffer);
22527db96d56Sopenharmony_ci            if (exitCode) {
22537db96d56Sopenharmony_ci                return exitCode;
22547db96d56Sopenharmony_ci            }
22557db96d56Sopenharmony_ci        }
22567db96d56Sopenharmony_ci
22577db96d56Sopenharmony_ci        env = env->next;
22587db96d56Sopenharmony_ci    }
22597db96d56Sopenharmony_ci    return 0;
22607db96d56Sopenharmony_ci}
22617db96d56Sopenharmony_ci
22627db96d56Sopenharmony_ci
22637db96d56Sopenharmony_ciint
22647db96d56Sopenharmony_cilistEnvironments(EnvironmentInfo *env, FILE * out, bool showPath, EnvironmentInfo *defaultEnv)
22657db96d56Sopenharmony_ci{
22667db96d56Sopenharmony_ci    if (!env) {
22677db96d56Sopenharmony_ci        fwprintf_s(stdout, L"No installed Pythons found!\n");
22687db96d56Sopenharmony_ci        return 0;
22697db96d56Sopenharmony_ci    }
22707db96d56Sopenharmony_ci
22717db96d56Sopenharmony_ci    /* TODO: Do we want to display these?
22727db96d56Sopenharmony_ci       In favour, helps users see that '-3' is a good option
22737db96d56Sopenharmony_ci       Against, repeats the next line of output
22747db96d56Sopenharmony_ci    SearchInfo majorSearch;
22757db96d56Sopenharmony_ci    EnvironmentInfo *major;
22767db96d56Sopenharmony_ci    int exitCode;
22777db96d56Sopenharmony_ci
22787db96d56Sopenharmony_ci    if (showPath) {
22797db96d56Sopenharmony_ci        memset(&majorSearch, 0, sizeof(majorSearch));
22807db96d56Sopenharmony_ci        majorSearch.company = L"PythonCore";
22817db96d56Sopenharmony_ci        majorSearch.companyLength = -1;
22827db96d56Sopenharmony_ci        majorSearch.tag = L"3";
22837db96d56Sopenharmony_ci        majorSearch.tagLength = -1;
22847db96d56Sopenharmony_ci        majorSearch.oldStyleTag = true;
22857db96d56Sopenharmony_ci        major = NULL;
22867db96d56Sopenharmony_ci        exitCode = selectEnvironment(&majorSearch, env, &major);
22877db96d56Sopenharmony_ci        if (!exitCode && major) {
22887db96d56Sopenharmony_ci            exitCode = _printEnvironment(major, out, showPath, L"-3 *");
22897db96d56Sopenharmony_ci            isDefault = false;
22907db96d56Sopenharmony_ci            if (exitCode) {
22917db96d56Sopenharmony_ci                return exitCode;
22927db96d56Sopenharmony_ci            }
22937db96d56Sopenharmony_ci        }
22947db96d56Sopenharmony_ci        majorSearch.tag = L"2";
22957db96d56Sopenharmony_ci        major = NULL;
22967db96d56Sopenharmony_ci        exitCode = selectEnvironment(&majorSearch, env, &major);
22977db96d56Sopenharmony_ci        if (!exitCode && major) {
22987db96d56Sopenharmony_ci            exitCode = _printEnvironment(major, out, showPath, L"-2");
22997db96d56Sopenharmony_ci            if (exitCode) {
23007db96d56Sopenharmony_ci                return exitCode;
23017db96d56Sopenharmony_ci            }
23027db96d56Sopenharmony_ci        }
23037db96d56Sopenharmony_ci    }
23047db96d56Sopenharmony_ci    */
23057db96d56Sopenharmony_ci
23067db96d56Sopenharmony_ci    int mode = _setmode(_fileno(out), _O_U8TEXT);
23077db96d56Sopenharmony_ci    int exitCode = _listAllEnvironments(env, out, showPath, defaultEnv);
23087db96d56Sopenharmony_ci    fflush(out);
23097db96d56Sopenharmony_ci    if (mode >= 0) {
23107db96d56Sopenharmony_ci        _setmode(_fileno(out), mode);
23117db96d56Sopenharmony_ci    }
23127db96d56Sopenharmony_ci    return exitCode;
23137db96d56Sopenharmony_ci}
23147db96d56Sopenharmony_ci
23157db96d56Sopenharmony_ci
23167db96d56Sopenharmony_ci/******************************************************************************\
23177db96d56Sopenharmony_ci ***                           INTERPRETER LAUNCH                           ***
23187db96d56Sopenharmony_ci\******************************************************************************/
23197db96d56Sopenharmony_ci
23207db96d56Sopenharmony_ci
23217db96d56Sopenharmony_ciint
23227db96d56Sopenharmony_cicalculateCommandLine(const SearchInfo *search, const EnvironmentInfo *launch, wchar_t *buffer, int bufferLength)
23237db96d56Sopenharmony_ci{
23247db96d56Sopenharmony_ci    int exitCode = 0;
23257db96d56Sopenharmony_ci    const wchar_t *executablePath = NULL;
23267db96d56Sopenharmony_ci
23277db96d56Sopenharmony_ci    // Construct command line from a search override, or else the selected
23287db96d56Sopenharmony_ci    // environment's executablePath
23297db96d56Sopenharmony_ci    if (search->executablePath) {
23307db96d56Sopenharmony_ci        executablePath = search->executablePath;
23317db96d56Sopenharmony_ci    } else if (launch && launch->executablePath) {
23327db96d56Sopenharmony_ci        executablePath = launch->executablePath;
23337db96d56Sopenharmony_ci    }
23347db96d56Sopenharmony_ci
23357db96d56Sopenharmony_ci    // If we have an executable path, put it at the start of the command, but
23367db96d56Sopenharmony_ci    // only if the search allowed an override.
23377db96d56Sopenharmony_ci    // Otherwise, use the environment's installDir and the search's default
23387db96d56Sopenharmony_ci    // executable name.
23397db96d56Sopenharmony_ci    if (executablePath && search->allowExecutableOverride) {
23407db96d56Sopenharmony_ci        if (wcschr(executablePath, L' ') && executablePath[0] != L'"') {
23417db96d56Sopenharmony_ci            buffer[0] = L'"';
23427db96d56Sopenharmony_ci            exitCode = wcscpy_s(&buffer[1], bufferLength - 1, executablePath);
23437db96d56Sopenharmony_ci            if (!exitCode) {
23447db96d56Sopenharmony_ci                exitCode = wcscat_s(buffer, bufferLength, L"\"");
23457db96d56Sopenharmony_ci            }
23467db96d56Sopenharmony_ci        } else {
23477db96d56Sopenharmony_ci            exitCode = wcscpy_s(buffer, bufferLength, executablePath);
23487db96d56Sopenharmony_ci        }
23497db96d56Sopenharmony_ci    } else if (launch) {
23507db96d56Sopenharmony_ci        if (!launch->installDir) {
23517db96d56Sopenharmony_ci            fwprintf_s(stderr, L"Cannot launch %s %s because no install directory was specified",
23527db96d56Sopenharmony_ci                       launch->company, launch->tag);
23537db96d56Sopenharmony_ci            exitCode = RC_NO_PYTHON;
23547db96d56Sopenharmony_ci        } else if (!search->executable || !search->executableLength) {
23557db96d56Sopenharmony_ci            fwprintf_s(stderr, L"Cannot launch %s %s because no executable name is available",
23567db96d56Sopenharmony_ci                       launch->company, launch->tag);
23577db96d56Sopenharmony_ci            exitCode = RC_NO_PYTHON;
23587db96d56Sopenharmony_ci        } else {
23597db96d56Sopenharmony_ci            wchar_t executable[256];
23607db96d56Sopenharmony_ci            wcsncpy_s(executable, 256, search->executable, search->executableLength);
23617db96d56Sopenharmony_ci            if ((wcschr(launch->installDir, L' ') && launch->installDir[0] != L'"') ||
23627db96d56Sopenharmony_ci                (wcschr(executable, L' ') && executable[0] != L'"')) {
23637db96d56Sopenharmony_ci                buffer[0] = L'"';
23647db96d56Sopenharmony_ci                exitCode = wcscpy_s(&buffer[1], bufferLength - 1, launch->installDir);
23657db96d56Sopenharmony_ci                if (!exitCode) {
23667db96d56Sopenharmony_ci                    exitCode = join(buffer, bufferLength, executable) ? 0 : RC_NO_MEMORY;
23677db96d56Sopenharmony_ci                }
23687db96d56Sopenharmony_ci                if (!exitCode) {
23697db96d56Sopenharmony_ci                    exitCode = wcscat_s(buffer, bufferLength, L"\"");
23707db96d56Sopenharmony_ci                }
23717db96d56Sopenharmony_ci            } else {
23727db96d56Sopenharmony_ci                exitCode = wcscpy_s(buffer, bufferLength, launch->installDir);
23737db96d56Sopenharmony_ci                if (!exitCode) {
23747db96d56Sopenharmony_ci                    exitCode = join(buffer, bufferLength, executable) ? 0 : RC_NO_MEMORY;
23757db96d56Sopenharmony_ci                }
23767db96d56Sopenharmony_ci            }
23777db96d56Sopenharmony_ci        }
23787db96d56Sopenharmony_ci    } else {
23797db96d56Sopenharmony_ci        exitCode = RC_NO_PYTHON;
23807db96d56Sopenharmony_ci    }
23817db96d56Sopenharmony_ci
23827db96d56Sopenharmony_ci    if (!exitCode && launch && launch->executableArgs) {
23837db96d56Sopenharmony_ci        exitCode = wcscat_s(buffer, bufferLength, L" ");
23847db96d56Sopenharmony_ci        if (!exitCode) {
23857db96d56Sopenharmony_ci            exitCode = wcscat_s(buffer, bufferLength, launch->executableArgs);
23867db96d56Sopenharmony_ci        }
23877db96d56Sopenharmony_ci    }
23887db96d56Sopenharmony_ci
23897db96d56Sopenharmony_ci    if (!exitCode && search->executableArgs) {
23907db96d56Sopenharmony_ci        if (search->executableArgsLength < 0) {
23917db96d56Sopenharmony_ci            exitCode = wcscat_s(buffer, bufferLength, search->executableArgs);
23927db96d56Sopenharmony_ci        } else if (search->executableArgsLength > 0) {
23937db96d56Sopenharmony_ci            int end = (int)wcsnlen_s(buffer, MAXLEN);
23947db96d56Sopenharmony_ci            if (end < bufferLength - (search->executableArgsLength + 1)) {
23957db96d56Sopenharmony_ci                exitCode = wcsncpy_s(&buffer[end], bufferLength - end,
23967db96d56Sopenharmony_ci                    search->executableArgs, search->executableArgsLength);
23977db96d56Sopenharmony_ci            }
23987db96d56Sopenharmony_ci        }
23997db96d56Sopenharmony_ci    }
24007db96d56Sopenharmony_ci
24017db96d56Sopenharmony_ci    if (!exitCode && search->restOfCmdLine) {
24027db96d56Sopenharmony_ci        exitCode = wcscat_s(buffer, bufferLength, search->restOfCmdLine);
24037db96d56Sopenharmony_ci    }
24047db96d56Sopenharmony_ci
24057db96d56Sopenharmony_ci    return exitCode;
24067db96d56Sopenharmony_ci}
24077db96d56Sopenharmony_ci
24087db96d56Sopenharmony_ci
24097db96d56Sopenharmony_ci
24107db96d56Sopenharmony_ciBOOL
24117db96d56Sopenharmony_ci_safeDuplicateHandle(HANDLE in, HANDLE * pout, const wchar_t *nameForError)
24127db96d56Sopenharmony_ci{
24137db96d56Sopenharmony_ci    BOOL ok;
24147db96d56Sopenharmony_ci    HANDLE process = GetCurrentProcess();
24157db96d56Sopenharmony_ci    DWORD rc;
24167db96d56Sopenharmony_ci
24177db96d56Sopenharmony_ci    *pout = NULL;
24187db96d56Sopenharmony_ci    ok = DuplicateHandle(process, in, process, pout, 0, TRUE,
24197db96d56Sopenharmony_ci                         DUPLICATE_SAME_ACCESS);
24207db96d56Sopenharmony_ci    if (!ok) {
24217db96d56Sopenharmony_ci        rc = GetLastError();
24227db96d56Sopenharmony_ci        if (rc == ERROR_INVALID_HANDLE) {
24237db96d56Sopenharmony_ci            debug(L"DuplicateHandle returned ERROR_INVALID_HANDLE\n");
24247db96d56Sopenharmony_ci            ok = TRUE;
24257db96d56Sopenharmony_ci        }
24267db96d56Sopenharmony_ci        else {
24277db96d56Sopenharmony_ci            winerror(0, L"Failed to duplicate %s handle", nameForError);
24287db96d56Sopenharmony_ci        }
24297db96d56Sopenharmony_ci    }
24307db96d56Sopenharmony_ci    return ok;
24317db96d56Sopenharmony_ci}
24327db96d56Sopenharmony_ci
24337db96d56Sopenharmony_ciBOOL WINAPI
24347db96d56Sopenharmony_cictrl_c_handler(DWORD code)
24357db96d56Sopenharmony_ci{
24367db96d56Sopenharmony_ci    return TRUE;    /* We just ignore all control events. */
24377db96d56Sopenharmony_ci}
24387db96d56Sopenharmony_ci
24397db96d56Sopenharmony_ci
24407db96d56Sopenharmony_ciint
24417db96d56Sopenharmony_cilaunchEnvironment(const SearchInfo *search, const EnvironmentInfo *launch, wchar_t *launchCommand)
24427db96d56Sopenharmony_ci{
24437db96d56Sopenharmony_ci    HANDLE job;
24447db96d56Sopenharmony_ci    JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
24457db96d56Sopenharmony_ci    DWORD rc;
24467db96d56Sopenharmony_ci    BOOL ok;
24477db96d56Sopenharmony_ci    STARTUPINFOW si;
24487db96d56Sopenharmony_ci    PROCESS_INFORMATION pi;
24497db96d56Sopenharmony_ci
24507db96d56Sopenharmony_ci    // If this is a dryrun, do not actually launch
24517db96d56Sopenharmony_ci    if (isEnvVarSet(L"PYLAUNCHER_DRYRUN")) {
24527db96d56Sopenharmony_ci        debug(L"LaunchCommand: %s\n", launchCommand);
24537db96d56Sopenharmony_ci        debug(L"# Exiting due to PYLAUNCHER_DRYRUN variable\n");
24547db96d56Sopenharmony_ci        fflush(stdout);
24557db96d56Sopenharmony_ci        int mode = _setmode(_fileno(stdout), _O_U8TEXT);
24567db96d56Sopenharmony_ci        fwprintf(stdout, L"%s\n", launchCommand);
24577db96d56Sopenharmony_ci        fflush(stdout);
24587db96d56Sopenharmony_ci        if (mode >= 0) {
24597db96d56Sopenharmony_ci            _setmode(_fileno(stdout), mode);
24607db96d56Sopenharmony_ci        }
24617db96d56Sopenharmony_ci        return 0;
24627db96d56Sopenharmony_ci    }
24637db96d56Sopenharmony_ci
24647db96d56Sopenharmony_ci#if defined(_WINDOWS)
24657db96d56Sopenharmony_ci    /*
24667db96d56Sopenharmony_ci    When explorer launches a Windows (GUI) application, it displays
24677db96d56Sopenharmony_ci    the "app starting" (the "pointer + hourglass") cursor for a number
24687db96d56Sopenharmony_ci    of seconds, or until the app does something UI-ish (eg, creating a
24697db96d56Sopenharmony_ci    window, or fetching a message).  As this launcher doesn't do this
24707db96d56Sopenharmony_ci    directly, that cursor remains even after the child process does these
24717db96d56Sopenharmony_ci    things.  We avoid that by doing a simple post+get message.
24727db96d56Sopenharmony_ci    See http://bugs.python.org/issue17290 and
24737db96d56Sopenharmony_ci    https://bitbucket.org/vinay.sajip/pylauncher/issue/20/busy-cursor-for-a-long-time-when-running
24747db96d56Sopenharmony_ci    */
24757db96d56Sopenharmony_ci    MSG msg;
24767db96d56Sopenharmony_ci
24777db96d56Sopenharmony_ci    PostMessage(0, 0, 0, 0);
24787db96d56Sopenharmony_ci    GetMessage(&msg, 0, 0, 0);
24797db96d56Sopenharmony_ci#endif
24807db96d56Sopenharmony_ci
24817db96d56Sopenharmony_ci    debug(L"# about to run: %s\n", launchCommand);
24827db96d56Sopenharmony_ci    job = CreateJobObject(NULL, NULL);
24837db96d56Sopenharmony_ci    ok = QueryInformationJobObject(job, JobObjectExtendedLimitInformation,
24847db96d56Sopenharmony_ci                                  &info, sizeof(info), &rc);
24857db96d56Sopenharmony_ci    if (!ok || (rc != sizeof(info)) || !job) {
24867db96d56Sopenharmony_ci        winerror(0, L"Failed to query job information");
24877db96d56Sopenharmony_ci        return RC_CREATE_PROCESS;
24887db96d56Sopenharmony_ci    }
24897db96d56Sopenharmony_ci    info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
24907db96d56Sopenharmony_ci                                             JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
24917db96d56Sopenharmony_ci    ok = SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info,
24927db96d56Sopenharmony_ci                                 sizeof(info));
24937db96d56Sopenharmony_ci    if (!ok) {
24947db96d56Sopenharmony_ci        winerror(0, L"Failed to update job information");
24957db96d56Sopenharmony_ci        return RC_CREATE_PROCESS;
24967db96d56Sopenharmony_ci    }
24977db96d56Sopenharmony_ci    memset(&si, 0, sizeof(si));
24987db96d56Sopenharmony_ci    GetStartupInfoW(&si);
24997db96d56Sopenharmony_ci    if (!_safeDuplicateHandle(GetStdHandle(STD_INPUT_HANDLE), &si.hStdInput, L"stdin") ||
25007db96d56Sopenharmony_ci        !_safeDuplicateHandle(GetStdHandle(STD_OUTPUT_HANDLE), &si.hStdOutput, L"stdout") ||
25017db96d56Sopenharmony_ci        !_safeDuplicateHandle(GetStdHandle(STD_ERROR_HANDLE), &si.hStdError, L"stderr")) {
25027db96d56Sopenharmony_ci        return RC_NO_STD_HANDLES;
25037db96d56Sopenharmony_ci    }
25047db96d56Sopenharmony_ci
25057db96d56Sopenharmony_ci    ok = SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
25067db96d56Sopenharmony_ci    if (!ok) {
25077db96d56Sopenharmony_ci        winerror(0, L"Failed to update Control-C handler");
25087db96d56Sopenharmony_ci        return RC_NO_STD_HANDLES;
25097db96d56Sopenharmony_ci    }
25107db96d56Sopenharmony_ci
25117db96d56Sopenharmony_ci    si.dwFlags = STARTF_USESTDHANDLES;
25127db96d56Sopenharmony_ci    ok = CreateProcessW(NULL, launchCommand, NULL, NULL, TRUE,
25137db96d56Sopenharmony_ci                        0, NULL, NULL, &si, &pi);
25147db96d56Sopenharmony_ci    if (!ok) {
25157db96d56Sopenharmony_ci        winerror(0, L"Unable to create process using '%s'", launchCommand);
25167db96d56Sopenharmony_ci        return RC_CREATE_PROCESS;
25177db96d56Sopenharmony_ci    }
25187db96d56Sopenharmony_ci    AssignProcessToJobObject(job, pi.hProcess);
25197db96d56Sopenharmony_ci    CloseHandle(pi.hThread);
25207db96d56Sopenharmony_ci    WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE);
25217db96d56Sopenharmony_ci    ok = GetExitCodeProcess(pi.hProcess, &rc);
25227db96d56Sopenharmony_ci    if (!ok) {
25237db96d56Sopenharmony_ci        winerror(0, L"Failed to get exit code of process");
25247db96d56Sopenharmony_ci        return RC_CREATE_PROCESS;
25257db96d56Sopenharmony_ci    }
25267db96d56Sopenharmony_ci    debug(L"child process exit code: %d\n", rc);
25277db96d56Sopenharmony_ci    return rc;
25287db96d56Sopenharmony_ci}
25297db96d56Sopenharmony_ci
25307db96d56Sopenharmony_ci
25317db96d56Sopenharmony_ci/******************************************************************************\
25327db96d56Sopenharmony_ci ***                           PROCESS CONTROLLER                           ***
25337db96d56Sopenharmony_ci\******************************************************************************/
25347db96d56Sopenharmony_ci
25357db96d56Sopenharmony_ci
25367db96d56Sopenharmony_ciint
25377db96d56Sopenharmony_ciperformSearch(SearchInfo *search, EnvironmentInfo **envs)
25387db96d56Sopenharmony_ci{
25397db96d56Sopenharmony_ci    // First parse the command line for options
25407db96d56Sopenharmony_ci    int exitCode = parseCommandLine(search);
25417db96d56Sopenharmony_ci    if (exitCode) {
25427db96d56Sopenharmony_ci        return exitCode;
25437db96d56Sopenharmony_ci    }
25447db96d56Sopenharmony_ci
25457db96d56Sopenharmony_ci    // Check for a shebang line in our script file
25467db96d56Sopenharmony_ci    // (or return quickly if no script file was specified)
25477db96d56Sopenharmony_ci    exitCode = checkShebang(search);
25487db96d56Sopenharmony_ci    switch (exitCode) {
25497db96d56Sopenharmony_ci    case 0:
25507db96d56Sopenharmony_ci    case RC_NO_SHEBANG:
25517db96d56Sopenharmony_ci    case RC_RECURSIVE_SHEBANG:
25527db96d56Sopenharmony_ci        break;
25537db96d56Sopenharmony_ci    default:
25547db96d56Sopenharmony_ci        return exitCode;
25557db96d56Sopenharmony_ci    }
25567db96d56Sopenharmony_ci
25577db96d56Sopenharmony_ci    // Resolve old-style tags (possibly from a shebang) against py.ini entries
25587db96d56Sopenharmony_ci    // and environment variables.
25597db96d56Sopenharmony_ci    exitCode = checkDefaults(search);
25607db96d56Sopenharmony_ci    if (exitCode) {
25617db96d56Sopenharmony_ci        return exitCode;
25627db96d56Sopenharmony_ci    }
25637db96d56Sopenharmony_ci
25647db96d56Sopenharmony_ci    // If debugging is enabled, list our search criteria
25657db96d56Sopenharmony_ci    dumpSearchInfo(search);
25667db96d56Sopenharmony_ci
25677db96d56Sopenharmony_ci    // Find all matching environments
25687db96d56Sopenharmony_ci    exitCode = collectEnvironments(search, envs);
25697db96d56Sopenharmony_ci    if (exitCode) {
25707db96d56Sopenharmony_ci        return exitCode;
25717db96d56Sopenharmony_ci    }
25727db96d56Sopenharmony_ci
25737db96d56Sopenharmony_ci    return 0;
25747db96d56Sopenharmony_ci}
25757db96d56Sopenharmony_ci
25767db96d56Sopenharmony_ci
25777db96d56Sopenharmony_ciint
25787db96d56Sopenharmony_ciprocess(int argc, wchar_t ** argv)
25797db96d56Sopenharmony_ci{
25807db96d56Sopenharmony_ci    int exitCode = 0;
25817db96d56Sopenharmony_ci    int searchExitCode = 0;
25827db96d56Sopenharmony_ci    SearchInfo search = {0};
25837db96d56Sopenharmony_ci    EnvironmentInfo *envs = NULL;
25847db96d56Sopenharmony_ci    EnvironmentInfo *env = NULL;
25857db96d56Sopenharmony_ci    wchar_t launchCommand[MAXLEN];
25867db96d56Sopenharmony_ci
25877db96d56Sopenharmony_ci    memset(launchCommand, 0, sizeof(launchCommand));
25887db96d56Sopenharmony_ci
25897db96d56Sopenharmony_ci    if (isEnvVarSet(L"PYLAUNCHER_DEBUG")) {
25907db96d56Sopenharmony_ci        setvbuf(stderr, (char *)NULL, _IONBF, 0);
25917db96d56Sopenharmony_ci        log_fp = stderr;
25927db96d56Sopenharmony_ci        debug(L"argv0: %s\nversion: %S\n", argv[0], PY_VERSION);
25937db96d56Sopenharmony_ci    }
25947db96d56Sopenharmony_ci
25957db96d56Sopenharmony_ci    DWORD len = GetEnvironmentVariableW(L"PYLAUNCHER_LIMIT_TO_COMPANY", NULL, 0);
25967db96d56Sopenharmony_ci    if (len > 1) {
25977db96d56Sopenharmony_ci        wchar_t *limitToCompany = allocSearchInfoBuffer(&search, len);
25987db96d56Sopenharmony_ci        search.limitToCompany = limitToCompany;
25997db96d56Sopenharmony_ci        if (0 == GetEnvironmentVariableW(L"PYLAUNCHER_LIMIT_TO_COMPANY", limitToCompany, len)) {
26007db96d56Sopenharmony_ci            exitCode = RC_INTERNAL_ERROR;
26017db96d56Sopenharmony_ci            winerror(0, L"Failed to read PYLAUNCHER_LIMIT_TO_COMPANY variable");
26027db96d56Sopenharmony_ci            goto abort;
26037db96d56Sopenharmony_ci        }
26047db96d56Sopenharmony_ci    }
26057db96d56Sopenharmony_ci
26067db96d56Sopenharmony_ci    search.originalCmdLine = GetCommandLineW();
26077db96d56Sopenharmony_ci
26087db96d56Sopenharmony_ci    exitCode = performSearch(&search, &envs);
26097db96d56Sopenharmony_ci    if (exitCode) {
26107db96d56Sopenharmony_ci        goto abort;
26117db96d56Sopenharmony_ci    }
26127db96d56Sopenharmony_ci
26137db96d56Sopenharmony_ci    // Display the help text, but only exit on error
26147db96d56Sopenharmony_ci    if (search.help) {
26157db96d56Sopenharmony_ci        exitCode = showHelpText(argv);
26167db96d56Sopenharmony_ci        if (exitCode) {
26177db96d56Sopenharmony_ci            goto abort;
26187db96d56Sopenharmony_ci        }
26197db96d56Sopenharmony_ci    }
26207db96d56Sopenharmony_ci
26217db96d56Sopenharmony_ci    // Select best environment
26227db96d56Sopenharmony_ci    // This is early so that we can show the default when listing, but all
26237db96d56Sopenharmony_ci    // responses to any errors occur later.
26247db96d56Sopenharmony_ci    searchExitCode = selectEnvironment(&search, envs, &env);
26257db96d56Sopenharmony_ci
26267db96d56Sopenharmony_ci    // List all environments, then exit
26277db96d56Sopenharmony_ci    if (search.list || search.listPaths) {
26287db96d56Sopenharmony_ci        exitCode = listEnvironments(envs, stdout, search.listPaths, env);
26297db96d56Sopenharmony_ci        goto abort;
26307db96d56Sopenharmony_ci    }
26317db96d56Sopenharmony_ci
26327db96d56Sopenharmony_ci    // When debugging, list all discovered environments anyway
26337db96d56Sopenharmony_ci    if (log_fp) {
26347db96d56Sopenharmony_ci        exitCode = listEnvironments(envs, log_fp, true, NULL);
26357db96d56Sopenharmony_ci        if (exitCode) {
26367db96d56Sopenharmony_ci            goto abort;
26377db96d56Sopenharmony_ci        }
26387db96d56Sopenharmony_ci    }
26397db96d56Sopenharmony_ci
26407db96d56Sopenharmony_ci    // We searched earlier, so if we didn't find anything, now we react
26417db96d56Sopenharmony_ci    exitCode = searchExitCode;
26427db96d56Sopenharmony_ci    // If none found, and if permitted, install it
26437db96d56Sopenharmony_ci    if (exitCode == RC_NO_PYTHON && isEnvVarSet(L"PYLAUNCHER_ALLOW_INSTALL") ||
26447db96d56Sopenharmony_ci        isEnvVarSet(L"PYLAUNCHER_ALWAYS_INSTALL")) {
26457db96d56Sopenharmony_ci        exitCode = installEnvironment(&search);
26467db96d56Sopenharmony_ci        if (!exitCode) {
26477db96d56Sopenharmony_ci            // Successful install, so we need to re-scan and select again
26487db96d56Sopenharmony_ci            env = NULL;
26497db96d56Sopenharmony_ci            exitCode = performSearch(&search, &envs);
26507db96d56Sopenharmony_ci            if (exitCode) {
26517db96d56Sopenharmony_ci                goto abort;
26527db96d56Sopenharmony_ci            }
26537db96d56Sopenharmony_ci            exitCode = selectEnvironment(&search, envs, &env);
26547db96d56Sopenharmony_ci        }
26557db96d56Sopenharmony_ci    }
26567db96d56Sopenharmony_ci    if (exitCode == RC_NO_PYTHON) {
26577db96d56Sopenharmony_ci        fputws(L"No suitable Python runtime found\n", stderr);
26587db96d56Sopenharmony_ci        fputws(L"Pass --list (-0) to see all detected environments on your machine\n", stderr);
26597db96d56Sopenharmony_ci        if (!isEnvVarSet(L"PYLAUNCHER_ALLOW_INSTALL") && search.oldStyleTag) {
26607db96d56Sopenharmony_ci            fputws(L"or set environment variable PYLAUNCHER_ALLOW_INSTALL to use winget\n"
26617db96d56Sopenharmony_ci                   L"or open the Microsoft Store to the requested version.\n", stderr);
26627db96d56Sopenharmony_ci        }
26637db96d56Sopenharmony_ci        goto abort;
26647db96d56Sopenharmony_ci    }
26657db96d56Sopenharmony_ci    if (exitCode == RC_NO_PYTHON_AT_ALL) {
26667db96d56Sopenharmony_ci        fputws(L"No installed Python found!\n", stderr);
26677db96d56Sopenharmony_ci        goto abort;
26687db96d56Sopenharmony_ci    }
26697db96d56Sopenharmony_ci    if (exitCode) {
26707db96d56Sopenharmony_ci        goto abort;
26717db96d56Sopenharmony_ci    }
26727db96d56Sopenharmony_ci
26737db96d56Sopenharmony_ci    if (env) {
26747db96d56Sopenharmony_ci        debug(L"env.company: %s\nenv.tag: %s\n", env->company, env->tag);
26757db96d56Sopenharmony_ci    } else {
26767db96d56Sopenharmony_ci        debug(L"env.company: (null)\nenv.tag: (null)\n");
26777db96d56Sopenharmony_ci    }
26787db96d56Sopenharmony_ci
26797db96d56Sopenharmony_ci    exitCode = calculateCommandLine(&search, env, launchCommand, sizeof(launchCommand) / sizeof(launchCommand[0]));
26807db96d56Sopenharmony_ci    if (exitCode) {
26817db96d56Sopenharmony_ci        goto abort;
26827db96d56Sopenharmony_ci    }
26837db96d56Sopenharmony_ci
26847db96d56Sopenharmony_ci    // Launch selected runtime
26857db96d56Sopenharmony_ci    exitCode = launchEnvironment(&search, env, launchCommand);
26867db96d56Sopenharmony_ci
26877db96d56Sopenharmony_ciabort:
26887db96d56Sopenharmony_ci    freeSearchInfo(&search);
26897db96d56Sopenharmony_ci    freeEnvironmentInfo(envs);
26907db96d56Sopenharmony_ci    return exitCode;
26917db96d56Sopenharmony_ci}
26927db96d56Sopenharmony_ci
26937db96d56Sopenharmony_ci
26947db96d56Sopenharmony_ci#if defined(_WINDOWS)
26957db96d56Sopenharmony_ci
26967db96d56Sopenharmony_ciint WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
26977db96d56Sopenharmony_ci                   LPWSTR lpstrCmd, int nShow)
26987db96d56Sopenharmony_ci{
26997db96d56Sopenharmony_ci    return process(__argc, __wargv);
27007db96d56Sopenharmony_ci}
27017db96d56Sopenharmony_ci
27027db96d56Sopenharmony_ci#else
27037db96d56Sopenharmony_ci
27047db96d56Sopenharmony_ciint cdecl wmain(int argc, wchar_t ** argv)
27057db96d56Sopenharmony_ci{
27067db96d56Sopenharmony_ci    return process(argc, argv);
27077db96d56Sopenharmony_ci}
27087db96d56Sopenharmony_ci
27097db96d56Sopenharmony_ci#endif
2710