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