1/* 2 * Copyright (C) 2011-2013 Vinay Sajip. 3 * Licensed to PSF under a contributor agreement. 4 * 5 * Based on the work of: 6 * 7 * Mark Hammond (original author of Python version) 8 * Curt Hagenlocher (job management) 9 */ 10 11#include <windows.h> 12#include <shlobj.h> 13#include <stdio.h> 14#include <tchar.h> 15 16#define BUFSIZE 256 17#define MSGSIZE 1024 18 19/* Build options. */ 20#define SKIP_PREFIX 21#define SEARCH_PATH 22 23/* Error codes */ 24 25#define RC_NO_STD_HANDLES 100 26#define RC_CREATE_PROCESS 101 27#define RC_BAD_VIRTUAL_PATH 102 28#define RC_NO_PYTHON 103 29#define RC_NO_MEMORY 104 30/* 31 * SCRIPT_WRAPPER is used to choose one of the variants of an executable built 32 * from this source file. If not defined, the PEP 397 Python launcher is built; 33 * if defined, a script launcher of the type used by setuptools is built, which 34 * looks for a script name related to the executable name and runs that script 35 * with the appropriate Python interpreter. 36 * 37 * SCRIPT_WRAPPER should be undefined in the source, and defined in a VS project 38 * which builds the setuptools-style launcher. 39 */ 40#if defined(SCRIPT_WRAPPER) 41#define RC_NO_SCRIPT 105 42#endif 43/* 44 * VENV_REDIRECT is used to choose the variant that looks for an adjacent or 45 * one-level-higher pyvenv.cfg, and uses its "home" property to locate and 46 * launch the original python.exe. 47 */ 48#if defined(VENV_REDIRECT) 49#define RC_NO_VENV_CFG 106 50#define RC_BAD_VENV_CFG 107 51#endif 52 53/* Just for now - static definition */ 54 55static FILE * log_fp = NULL; 56 57static wchar_t * 58skip_whitespace(wchar_t * p) 59{ 60 while (*p && isspace(*p)) 61 ++p; 62 return p; 63} 64 65static void 66debug(wchar_t * format, ...) 67{ 68 va_list va; 69 70 if (log_fp != NULL) { 71 va_start(va, format); 72 vfwprintf_s(log_fp, format, va); 73 va_end(va); 74 } 75} 76 77static void 78winerror(int rc, wchar_t * message, int size) 79{ 80 FormatMessageW( 81 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 82 NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 83 message, size, NULL); 84} 85 86static void 87error(int rc, wchar_t * format, ... ) 88{ 89 va_list va; 90 wchar_t message[MSGSIZE]; 91 wchar_t win_message[MSGSIZE]; 92 int len; 93 94 va_start(va, format); 95 len = _vsnwprintf_s(message, MSGSIZE, _TRUNCATE, format, va); 96 va_end(va); 97 98 if (rc == 0) { /* a Windows error */ 99 winerror(GetLastError(), win_message, MSGSIZE); 100 if (len >= 0) { 101 _snwprintf_s(&message[len], MSGSIZE - len, _TRUNCATE, L": %ls", 102 win_message); 103 } 104 } 105 106#if !defined(_WINDOWS) 107 fwprintf(stderr, L"%ls\n", message); 108#else 109 MessageBoxW(NULL, message, L"Python Launcher is sorry to say ...", 110 MB_OK); 111#endif 112 exit(rc); 113} 114 115/* 116 * This function is here to simplify memory management 117 * and to treat blank values as if they are absent. 118 */ 119static wchar_t * get_env(wchar_t * key) 120{ 121 /* This is not thread-safe, just like getenv */ 122 static wchar_t buf[BUFSIZE]; 123 DWORD result = GetEnvironmentVariableW(key, buf, BUFSIZE); 124 125 if (result >= BUFSIZE) { 126 /* Large environment variable. Accept some leakage */ 127 wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1)); 128 if (buf2 == NULL) { 129 error(RC_NO_MEMORY, L"Could not allocate environment buffer"); 130 } 131 GetEnvironmentVariableW(key, buf2, result); 132 return buf2; 133 } 134 135 if (result == 0) 136 /* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND, 137 or an empty environment variable. */ 138 return NULL; 139 140 return buf; 141} 142 143#if defined(_DEBUG) 144/* Do not define EXECUTABLEPATH_VALUE in debug builds as it'll 145 never point to the debug build. */ 146#if defined(_WINDOWS) 147 148#define PYTHON_EXECUTABLE L"pythonw_d.exe" 149 150#else 151 152#define PYTHON_EXECUTABLE L"python_d.exe" 153 154#endif 155#else 156#if defined(_WINDOWS) 157 158#define PYTHON_EXECUTABLE L"pythonw.exe" 159#define EXECUTABLEPATH_VALUE L"WindowedExecutablePath" 160 161#else 162 163#define PYTHON_EXECUTABLE L"python.exe" 164#define EXECUTABLEPATH_VALUE L"ExecutablePath" 165 166#endif 167#endif 168 169#define MAX_VERSION_SIZE 8 170 171typedef struct { 172 wchar_t version[MAX_VERSION_SIZE]; /* m.n */ 173 int bits; /* 32 or 64 */ 174 wchar_t executable[MAX_PATH]; 175 wchar_t exe_display[MAX_PATH]; 176} INSTALLED_PYTHON; 177 178/* 179 * To avoid messing about with heap allocations, just assume we can allocate 180 * statically and never have to deal with more versions than this. 181 */ 182#define MAX_INSTALLED_PYTHONS 100 183 184static INSTALLED_PYTHON installed_pythons[MAX_INSTALLED_PYTHONS]; 185 186static size_t num_installed_pythons = 0; 187 188/* 189 * To hold SOFTWARE\Python\PythonCore\X.Y...\InstallPath 190 * The version name can be longer than MAX_VERSION_SIZE, but will be 191 * truncated to just X.Y for comparisons. 192 */ 193#define IP_BASE_SIZE 80 194#define IP_VERSION_SIZE 8 195#define IP_SIZE (IP_BASE_SIZE + IP_VERSION_SIZE) 196#define CORE_PATH L"SOFTWARE\\Python\\PythonCore" 197/* 198 * Installations from the Microsoft Store will set the same registry keys, 199 * but because of a limitation in Windows they cannot be enumerated normally 200 * (unless you have no other Python installations... which is probably false 201 * because that's the most likely way to get this launcher!) 202 * This key is under HKEY_LOCAL_MACHINE 203 */ 204#define LOOKASIDE_PATH L"SOFTWARE\\Microsoft\\AppModel\\Lookaside\\user\\Software\\Python\\PythonCore" 205 206static wchar_t * location_checks[] = { 207 L"\\", 208 L"\\PCbuild\\win32\\", 209 L"\\PCbuild\\amd64\\", 210 /* To support early 32bit versions of Python that stuck the build binaries 211 * directly in PCbuild... */ 212 L"\\PCbuild\\", 213 NULL 214}; 215 216static INSTALLED_PYTHON * 217find_existing_python(const wchar_t * path) 218{ 219 INSTALLED_PYTHON * result = NULL; 220 size_t i; 221 INSTALLED_PYTHON * ip; 222 223 for (i = 0, ip = installed_pythons; i < num_installed_pythons; i++, ip++) { 224 if (_wcsicmp(path, ip->executable) == 0) { 225 result = ip; 226 break; 227 } 228 } 229 return result; 230} 231 232static INSTALLED_PYTHON * 233find_existing_python2(int bits, const wchar_t * version) 234{ 235 INSTALLED_PYTHON * result = NULL; 236 size_t i; 237 INSTALLED_PYTHON * ip; 238 239 for (i = 0, ip = installed_pythons; i < num_installed_pythons; i++, ip++) { 240 if (bits == ip->bits && _wcsicmp(version, ip->version) == 0) { 241 result = ip; 242 break; 243 } 244 } 245 return result; 246} 247 248static void 249_locate_pythons_for_key(HKEY root, LPCWSTR subkey, REGSAM flags, int bits, 250 int display_name_only) 251{ 252 HKEY core_root, ip_key; 253 LSTATUS status = RegOpenKeyExW(root, subkey, 0, flags, &core_root); 254 wchar_t message[MSGSIZE]; 255 DWORD i; 256 size_t n; 257 BOOL ok, append_name; 258 DWORD type, data_size, attrs; 259 INSTALLED_PYTHON * ip, * pip; 260 wchar_t ip_version[IP_VERSION_SIZE]; 261 wchar_t ip_path[IP_SIZE]; 262 wchar_t * check; 263 wchar_t ** checkp; 264 wchar_t *key_name = (root == HKEY_LOCAL_MACHINE) ? L"HKLM" : L"HKCU"; 265 266 if (status != ERROR_SUCCESS) 267 debug(L"locate_pythons_for_key: unable to open PythonCore key in %ls\n", 268 key_name); 269 else { 270 ip = &installed_pythons[num_installed_pythons]; 271 for (i = 0; num_installed_pythons < MAX_INSTALLED_PYTHONS; i++) { 272 status = RegEnumKeyW(core_root, i, ip_version, IP_VERSION_SIZE); 273 if (status != ERROR_SUCCESS) { 274 if (status != ERROR_NO_MORE_ITEMS) { 275 /* unexpected error */ 276 winerror(status, message, MSGSIZE); 277 debug(L"Can't enumerate registry key for version %ls: %ls\n", 278 ip_version, message); 279 } 280 break; 281 } 282 else { 283 wcsncpy_s(ip->version, MAX_VERSION_SIZE, ip_version, 284 MAX_VERSION_SIZE-1); 285 /* Still treating version as "x.y" rather than sys.winver 286 * When PEP 514 tags are properly used, we shouldn't need 287 * to strip this off here. 288 */ 289 check = wcsrchr(ip->version, L'-'); 290 if (check && !wcscmp(check, L"-32")) { 291 *check = L'\0'; 292 } 293 _snwprintf_s(ip_path, IP_SIZE, _TRUNCATE, 294 L"%ls\\%ls\\InstallPath", subkey, ip_version); 295 status = RegOpenKeyExW(root, ip_path, 0, flags, &ip_key); 296 if (status != ERROR_SUCCESS) { 297 winerror(status, message, MSGSIZE); 298 /* Note: 'message' already has a trailing \n*/ 299 debug(L"%ls\\%ls: %ls", key_name, ip_path, message); 300 continue; 301 } 302 data_size = sizeof(ip->executable) - 1; 303 append_name = FALSE; 304#ifdef EXECUTABLEPATH_VALUE 305 status = RegQueryValueExW(ip_key, EXECUTABLEPATH_VALUE, NULL, &type, 306 (LPBYTE)ip->executable, &data_size); 307#else 308 status = ERROR_FILE_NOT_FOUND; /* actual error doesn't matter */ 309#endif 310 if (status != ERROR_SUCCESS || type != REG_SZ || !data_size) { 311 append_name = TRUE; 312 data_size = sizeof(ip->executable) - 1; 313 status = RegQueryValueExW(ip_key, NULL, NULL, &type, 314 (LPBYTE)ip->executable, &data_size); 315 if (status != ERROR_SUCCESS) { 316 winerror(status, message, MSGSIZE); 317 debug(L"%ls\\%ls: %ls\n", key_name, ip_path, message); 318 RegCloseKey(ip_key); 319 continue; 320 } 321 } 322 RegCloseKey(ip_key); 323 if (type != REG_SZ) { 324 continue; 325 } 326 327 data_size = data_size / sizeof(wchar_t) - 1; /* for NUL */ 328 if (ip->executable[data_size - 1] == L'\\') 329 --data_size; /* reg value ended in a backslash */ 330 /* ip->executable is data_size long */ 331 for (checkp = location_checks; *checkp; ++checkp) { 332 check = *checkp; 333 if (append_name) { 334 _snwprintf_s(&ip->executable[data_size], 335 MAX_PATH - data_size, 336 MAX_PATH - data_size, 337 L"%ls%ls", check, PYTHON_EXECUTABLE); 338 } 339 attrs = GetFileAttributesW(ip->executable); 340 if (attrs == INVALID_FILE_ATTRIBUTES) { 341 winerror(GetLastError(), message, MSGSIZE); 342 debug(L"locate_pythons_for_key: %ls: %ls", 343 ip->executable, message); 344 } 345 else if (attrs & FILE_ATTRIBUTE_DIRECTORY) { 346 debug(L"locate_pythons_for_key: '%ls' is a directory\n", 347 ip->executable); 348 } 349 else if (find_existing_python(ip->executable)) { 350 debug(L"locate_pythons_for_key: %ls: already found\n", 351 ip->executable); 352 } 353 else { 354 /* check the executable type. */ 355 if (bits) { 356 ip->bits = bits; 357 } else { 358 ok = GetBinaryTypeW(ip->executable, &attrs); 359 if (!ok) { 360 debug(L"Failure getting binary type: %ls\n", 361 ip->executable); 362 } 363 else { 364 if (attrs == SCS_64BIT_BINARY) 365 ip->bits = 64; 366 else if (attrs == SCS_32BIT_BINARY) 367 ip->bits = 32; 368 else 369 ip->bits = 0; 370 } 371 } 372 if (ip->bits == 0) { 373 debug(L"locate_pythons_for_key: %ls: \ 374invalid binary type: %X\n", 375 ip->executable, attrs); 376 } 377 else { 378 if (display_name_only) { 379 /* display just the executable name. This is 380 * primarily for the Store installs */ 381 const wchar_t *name = wcsrchr(ip->executable, L'\\'); 382 if (name) { 383 wcscpy_s(ip->exe_display, MAX_PATH, name+1); 384 } 385 } 386 if (wcschr(ip->executable, L' ') != NULL) { 387 /* has spaces, so quote, and set original as 388 * the display name */ 389 if (!ip->exe_display[0]) { 390 wcscpy_s(ip->exe_display, MAX_PATH, ip->executable); 391 } 392 n = wcslen(ip->executable); 393 memmove(&ip->executable[1], 394 ip->executable, n * sizeof(wchar_t)); 395 ip->executable[0] = L'\"'; 396 ip->executable[n + 1] = L'\"'; 397 ip->executable[n + 2] = L'\0'; 398 } 399 debug(L"locate_pythons_for_key: %ls \ 400is a %dbit executable\n", 401 ip->executable, ip->bits); 402 if (find_existing_python2(ip->bits, ip->version)) { 403 debug(L"locate_pythons_for_key: %ls-%i: already \ 404found\n", ip->version, ip->bits); 405 } 406 else { 407 ++num_installed_pythons; 408 pip = ip++; 409 if (num_installed_pythons >= 410 MAX_INSTALLED_PYTHONS) 411 break; 412 } 413 } 414 } 415 } 416 } 417 } 418 RegCloseKey(core_root); 419 } 420} 421 422static int 423compare_pythons(const void * p1, const void * p2) 424{ 425 INSTALLED_PYTHON * ip1 = (INSTALLED_PYTHON *) p1; 426 INSTALLED_PYTHON * ip2 = (INSTALLED_PYTHON *) p2; 427 /* note reverse sorting on version */ 428 int result = CompareStringW(LOCALE_INVARIANT, SORT_DIGITSASNUMBERS, 429 ip2->version, -1, ip1->version, -1); 430 switch (result) { 431 case 0: 432 error(0, L"CompareStringW failed"); 433 return 0; 434 case CSTR_LESS_THAN: 435 return -1; 436 case CSTR_EQUAL: 437 return ip2->bits - ip1->bits; /* 64 before 32 */ 438 case CSTR_GREATER_THAN: 439 return 1; 440 default: 441 return 0; // This should never be reached. 442 } 443} 444 445static void 446locate_pythons_for_key(HKEY root, REGSAM flags) 447{ 448 _locate_pythons_for_key(root, CORE_PATH, flags, 0, FALSE); 449} 450 451static void 452locate_store_pythons() 453{ 454#if defined(_M_X64) 455 /* 64bit process, so look in native registry */ 456 _locate_pythons_for_key(HKEY_LOCAL_MACHINE, LOOKASIDE_PATH, 457 KEY_READ, 64, TRUE); 458#else 459 /* 32bit process, so check that we're on 64bit OS */ 460 BOOL f64 = FALSE; 461 if (IsWow64Process(GetCurrentProcess(), &f64) && f64) { 462 _locate_pythons_for_key(HKEY_LOCAL_MACHINE, LOOKASIDE_PATH, 463 KEY_READ | KEY_WOW64_64KEY, 64, TRUE); 464 } 465#endif 466} 467 468static void 469locate_venv_python() 470{ 471 static wchar_t venv_python[MAX_PATH]; 472 INSTALLED_PYTHON * ip; 473 wchar_t *virtual_env = get_env(L"VIRTUAL_ENV"); 474 DWORD attrs; 475 476 /* Check for VIRTUAL_ENV environment variable */ 477 if (virtual_env == NULL || virtual_env[0] == L'\0') { 478 return; 479 } 480 481 /* Check for a python executable in the venv */ 482 debug(L"Checking for Python executable in virtual env '%ls'\n", virtual_env); 483 _snwprintf_s(venv_python, MAX_PATH, _TRUNCATE, 484 L"%ls\\Scripts\\%ls", virtual_env, PYTHON_EXECUTABLE); 485 attrs = GetFileAttributesW(venv_python); 486 if (attrs == INVALID_FILE_ATTRIBUTES) { 487 debug(L"Python executable %ls missing from virtual env\n", venv_python); 488 return; 489 } 490 491 ip = &installed_pythons[num_installed_pythons++]; 492 wcscpy_s(ip->executable, MAX_PATH, venv_python); 493 ip->bits = 0; 494 wcscpy_s(ip->version, MAX_VERSION_SIZE, L"venv"); 495} 496 497static void 498locate_all_pythons() 499{ 500 /* venv Python is highest priority */ 501 locate_venv_python(); 502#if defined(_M_X64) 503 /* If we are a 64bit process, first hit the 32bit keys. */ 504 debug(L"locating Pythons in 32bit registry\n"); 505 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_32KEY); 506 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_32KEY); 507#else 508 /* If we are a 32bit process on a 64bit Windows, first hit the 64bit keys.*/ 509 BOOL f64 = FALSE; 510 if (IsWow64Process(GetCurrentProcess(), &f64) && f64) { 511 debug(L"locating Pythons in 64bit registry\n"); 512 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_64KEY); 513 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_64KEY); 514 } 515#endif 516 /* now hit the "native" key for this process bittedness. */ 517 debug(L"locating Pythons in native registry\n"); 518 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ); 519 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ); 520 /* Store-installed Python is lowest priority */ 521 locate_store_pythons(); 522 qsort(installed_pythons, num_installed_pythons, sizeof(INSTALLED_PYTHON), 523 compare_pythons); 524} 525 526static INSTALLED_PYTHON * 527find_python_by_version(wchar_t const * wanted_ver) 528{ 529 INSTALLED_PYTHON * result = NULL; 530 INSTALLED_PYTHON * ip = installed_pythons; 531 size_t i, n; 532 size_t wlen = wcslen(wanted_ver); 533 int bits = 0; 534 535 if (wcsstr(wanted_ver, L"-32")) { 536 bits = 32; 537 wlen -= wcslen(L"-32"); 538 } 539 else if (wcsstr(wanted_ver, L"-64")) { /* Added option to select 64 bit explicitly */ 540 bits = 64; 541 wlen -= wcslen(L"-64"); 542 } 543 for (i = 0; i < num_installed_pythons; i++, ip++) { 544 n = wcslen(ip->version); 545 /* 546 * If wlen is greater than 1, we're probably trying to find a specific 547 * version and thus want an exact match: 3.1 != 3.10. Otherwise, we 548 * just want a prefix match. 549 */ 550 if ((wlen > 1) && (n != wlen)) { 551 continue; 552 } 553 if (n > wlen) { 554 n = wlen; 555 } 556 if ((wcsncmp(ip->version, wanted_ver, n) == 0) && 557 /* bits == 0 => don't care */ 558 ((bits == 0) || (ip->bits == bits))) { 559 result = ip; 560 break; 561 } 562 } 563 return result; 564} 565 566 567static wchar_t appdata_ini_path[MAX_PATH]; 568static wchar_t launcher_ini_path[MAX_PATH]; 569 570/* 571 * Get a value either from the environment or a configuration file. 572 * The key passed in will either be "python", "python2" or "python3". 573 */ 574static wchar_t * 575get_configured_value(wchar_t * key) 576{ 577/* 578 * Note: this static value is used to return a configured value 579 * obtained either from the environment or configuration file. 580 * This should be OK since there wouldn't be any concurrent calls. 581 */ 582 static wchar_t configured_value[MSGSIZE]; 583 wchar_t * result = NULL; 584 wchar_t * found_in = L"environment"; 585 DWORD size; 586 587 /* First, search the environment. */ 588 _snwprintf_s(configured_value, MSGSIZE, _TRUNCATE, L"py_%ls", key); 589 result = get_env(configured_value); 590 if (result == NULL && appdata_ini_path[0]) { 591 /* Not in environment: check local configuration. */ 592 size = GetPrivateProfileStringW(L"defaults", key, NULL, 593 configured_value, MSGSIZE, 594 appdata_ini_path); 595 if (size > 0) { 596 result = configured_value; 597 found_in = appdata_ini_path; 598 } 599 } 600 if (result == NULL && launcher_ini_path[0]) { 601 /* Not in environment or local: check global configuration. */ 602 size = GetPrivateProfileStringW(L"defaults", key, NULL, 603 configured_value, MSGSIZE, 604 launcher_ini_path); 605 if (size > 0) { 606 result = configured_value; 607 found_in = launcher_ini_path; 608 } 609 } 610 if (result) { 611 debug(L"found configured value '%ls=%ls' in %ls\n", 612 key, result, found_in ? found_in : L"(unknown)"); 613 } else { 614 debug(L"found no configured value for '%ls'\n", key); 615 } 616 return result; 617} 618 619static INSTALLED_PYTHON * 620locate_python(wchar_t * wanted_ver, BOOL from_shebang) 621{ 622 static wchar_t config_key [] = { L"pythonX" }; 623 static wchar_t * last_char = &config_key[sizeof(config_key) / 624 sizeof(wchar_t) - 2]; 625 INSTALLED_PYTHON * result = NULL; 626 size_t n = wcslen(wanted_ver); 627 wchar_t * configured_value; 628 629 if (num_installed_pythons == 0) 630 locate_all_pythons(); 631 632 if (n == 1) { /* just major version specified */ 633 *last_char = *wanted_ver; 634 configured_value = get_configured_value(config_key); 635 if (configured_value != NULL) 636 wanted_ver = configured_value; 637 } 638 if (*wanted_ver) { 639 result = find_python_by_version(wanted_ver); 640 debug(L"search for Python version '%ls' found ", wanted_ver); 641 if (result) { 642 debug(L"'%ls'\n", result->executable); 643 } else { 644 debug(L"no interpreter\n"); 645 } 646 } 647 else { 648 *last_char = L'\0'; /* look for an overall default */ 649 result = find_python_by_version(L"venv"); 650 if (result == NULL) { 651 configured_value = get_configured_value(config_key); 652 if (configured_value) 653 result = find_python_by_version(configured_value); 654 } 655 /* Not found a value yet - try by major version. 656 * If we're looking for an interpreter specified in a shebang line, 657 * we want to try Python 2 first, then Python 3 (for Unix and backward 658 * compatibility). If we're being called interactively, assume the user 659 * wants the latest version available, so try Python 3 first, then 660 * Python 2. 661 */ 662 if (result == NULL) 663 result = find_python_by_version(from_shebang ? L"2" : L"3"); 664 if (result == NULL) 665 result = find_python_by_version(from_shebang ? L"3" : L"2"); 666 debug(L"search for default Python found "); 667 if (result) { 668 debug(L"version %ls at '%ls'\n", 669 result->version, result->executable); 670 } else { 671 debug(L"no interpreter\n"); 672 } 673 } 674 return result; 675} 676 677#if defined(SCRIPT_WRAPPER) 678/* 679 * Check for a script located alongside the executable 680 */ 681 682#if defined(_WINDOWS) 683#define SCRIPT_SUFFIX L"-script.pyw" 684#else 685#define SCRIPT_SUFFIX L"-script.py" 686#endif 687 688static wchar_t wrapped_script_path[MAX_PATH]; 689 690/* Locate the script being wrapped. 691 * 692 * This code should store the name of the wrapped script in 693 * wrapped_script_path, or terminate the program with an error if there is no 694 * valid wrapped script file. 695 */ 696static void 697locate_wrapped_script() 698{ 699 wchar_t * p; 700 size_t plen; 701 DWORD attrs; 702 703 plen = GetModuleFileNameW(NULL, wrapped_script_path, MAX_PATH); 704 p = wcsrchr(wrapped_script_path, L'.'); 705 if (p == NULL) { 706 debug(L"GetModuleFileNameW returned value has no extension: %ls\n", 707 wrapped_script_path); 708 error(RC_NO_SCRIPT, L"Wrapper name '%ls' is not valid.", wrapped_script_path); 709 } 710 711 wcsncpy_s(p, MAX_PATH - (p - wrapped_script_path) + 1, SCRIPT_SUFFIX, _TRUNCATE); 712 attrs = GetFileAttributesW(wrapped_script_path); 713 if (attrs == INVALID_FILE_ATTRIBUTES) { 714 debug(L"File '%ls' non-existent\n", wrapped_script_path); 715 error(RC_NO_SCRIPT, L"Script file '%ls' is not present.", wrapped_script_path); 716 } 717 718 debug(L"Using wrapped script file '%ls'\n", wrapped_script_path); 719} 720#endif 721 722/* 723 * Process creation code 724 */ 725 726static BOOL 727safe_duplicate_handle(HANDLE in, HANDLE * pout) 728{ 729 BOOL ok; 730 HANDLE process = GetCurrentProcess(); 731 DWORD rc; 732 733 *pout = NULL; 734 ok = DuplicateHandle(process, in, process, pout, 0, TRUE, 735 DUPLICATE_SAME_ACCESS); 736 if (!ok) { 737 rc = GetLastError(); 738 if (rc == ERROR_INVALID_HANDLE) { 739 debug(L"DuplicateHandle returned ERROR_INVALID_HANDLE\n"); 740 ok = TRUE; 741 } 742 else { 743 debug(L"DuplicateHandle returned %d\n", rc); 744 } 745 } 746 return ok; 747} 748 749static BOOL WINAPI 750ctrl_c_handler(DWORD code) 751{ 752 return TRUE; /* We just ignore all control events. */ 753} 754 755static void 756run_child(wchar_t * cmdline) 757{ 758 HANDLE job; 759 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; 760 DWORD rc; 761 BOOL ok; 762 STARTUPINFOW si; 763 PROCESS_INFORMATION pi; 764 765#if defined(_WINDOWS) 766 /* 767 When explorer launches a Windows (GUI) application, it displays 768 the "app starting" (the "pointer + hourglass") cursor for a number 769 of seconds, or until the app does something UI-ish (eg, creating a 770 window, or fetching a message). As this launcher doesn't do this 771 directly, that cursor remains even after the child process does these 772 things. We avoid that by doing a simple post+get message. 773 See http://bugs.python.org/issue17290 and 774 https://bitbucket.org/vinay.sajip/pylauncher/issue/20/busy-cursor-for-a-long-time-when-running 775 */ 776 MSG msg; 777 778 PostMessage(0, 0, 0, 0); 779 GetMessage(&msg, 0, 0, 0); 780#endif 781 782 debug(L"run_child: about to run '%ls'\n", cmdline); 783 job = CreateJobObject(NULL, NULL); 784 ok = QueryInformationJobObject(job, JobObjectExtendedLimitInformation, 785 &info, sizeof(info), &rc); 786 if (!ok || (rc != sizeof(info)) || !job) 787 error(RC_CREATE_PROCESS, L"Job information querying failed"); 788 info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | 789 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; 790 ok = SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info, 791 sizeof(info)); 792 if (!ok) 793 error(RC_CREATE_PROCESS, L"Job information setting failed"); 794 memset(&si, 0, sizeof(si)); 795 GetStartupInfoW(&si); 796 ok = safe_duplicate_handle(GetStdHandle(STD_INPUT_HANDLE), &si.hStdInput); 797 if (!ok) 798 error(RC_NO_STD_HANDLES, L"stdin duplication failed"); 799 ok = safe_duplicate_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si.hStdOutput); 800 if (!ok) 801 error(RC_NO_STD_HANDLES, L"stdout duplication failed"); 802 ok = safe_duplicate_handle(GetStdHandle(STD_ERROR_HANDLE), &si.hStdError); 803 if (!ok) 804 error(RC_NO_STD_HANDLES, L"stderr duplication failed"); 805 806 ok = SetConsoleCtrlHandler(ctrl_c_handler, TRUE); 807 if (!ok) 808 error(RC_CREATE_PROCESS, L"control handler setting failed"); 809 810 si.dwFlags = STARTF_USESTDHANDLES; 811 ok = CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, 812 0, NULL, NULL, &si, &pi); 813 if (!ok) 814 error(RC_CREATE_PROCESS, L"Unable to create process using '%ls'", cmdline); 815 AssignProcessToJobObject(job, pi.hProcess); 816 CloseHandle(pi.hThread); 817 WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE); 818 ok = GetExitCodeProcess(pi.hProcess, &rc); 819 if (!ok) 820 error(RC_CREATE_PROCESS, L"Failed to get exit code of process"); 821 debug(L"child process exit code: %d\n", rc); 822 exit(rc); 823} 824 825static void 826invoke_child(wchar_t * executable, wchar_t * suffix, wchar_t * cmdline) 827{ 828 wchar_t * child_command; 829 size_t child_command_size; 830 BOOL no_suffix = (suffix == NULL) || (*suffix == L'\0'); 831 BOOL no_cmdline = (*cmdline == L'\0'); 832 833 if (no_suffix && no_cmdline) 834 run_child(executable); 835 else { 836 if (no_suffix) { 837 /* add 2 for space separator + terminating NUL. */ 838 child_command_size = wcslen(executable) + wcslen(cmdline) + 2; 839 } 840 else { 841 /* add 3 for 2 space separators + terminating NUL. */ 842 child_command_size = wcslen(executable) + wcslen(suffix) + 843 wcslen(cmdline) + 3; 844 } 845 child_command = calloc(child_command_size, sizeof(wchar_t)); 846 if (child_command == NULL) 847 error(RC_CREATE_PROCESS, L"unable to allocate %zd bytes for child command.", 848 child_command_size); 849 if (no_suffix) 850 _snwprintf_s(child_command, child_command_size, 851 child_command_size - 1, L"%ls %ls", 852 executable, cmdline); 853 else 854 _snwprintf_s(child_command, child_command_size, 855 child_command_size - 1, L"%ls %ls %ls", 856 executable, suffix, cmdline); 857 run_child(child_command); 858 free(child_command); 859 } 860} 861 862typedef struct { 863 wchar_t *shebang; 864 BOOL search; 865} SHEBANG; 866 867static SHEBANG builtin_virtual_paths [] = { 868 { L"/usr/bin/env python", TRUE }, 869 { L"/usr/bin/python", FALSE }, 870 { L"/usr/local/bin/python", FALSE }, 871 { L"python", FALSE }, 872 { NULL, FALSE }, 873}; 874 875/* For now, a static array of commands. */ 876 877#define MAX_COMMANDS 100 878 879typedef struct { 880 wchar_t key[MAX_PATH]; 881 wchar_t value[MSGSIZE]; 882} COMMAND; 883 884static COMMAND commands[MAX_COMMANDS]; 885static int num_commands = 0; 886 887#if defined(SKIP_PREFIX) 888 889static wchar_t * builtin_prefixes [] = { 890 /* These must be in an order that the longest matches should be found, 891 * i.e. if the prefix is "/usr/bin/env ", it should match that entry 892 * *before* matching "/usr/bin/". 893 */ 894 L"/usr/bin/env ", 895 L"/usr/bin/", 896 L"/usr/local/bin/", 897 NULL 898}; 899 900static wchar_t * skip_prefix(wchar_t * name) 901{ 902 wchar_t ** pp = builtin_prefixes; 903 wchar_t * result = name; 904 wchar_t * p; 905 size_t n; 906 907 for (; p = *pp; pp++) { 908 n = wcslen(p); 909 if (_wcsnicmp(p, name, n) == 0) { 910 result += n; /* skip the prefix */ 911 if (p[n - 1] == L' ') /* No empty strings in table, so n > 1 */ 912 result = skip_whitespace(result); 913 break; 914 } 915 } 916 return result; 917} 918 919#endif 920 921#if defined(SEARCH_PATH) 922 923static COMMAND path_command; 924 925static COMMAND * find_on_path(wchar_t * name) 926{ 927 wchar_t * pathext; 928 size_t varsize; 929 wchar_t * context = NULL; 930 wchar_t * extension; 931 COMMAND * result = NULL; 932 DWORD len; 933 errno_t rc; 934 935 wcscpy_s(path_command.key, MAX_PATH, name); 936 if (wcschr(name, L'.') != NULL) { 937 /* assume it has an extension. */ 938 len = SearchPathW(NULL, name, NULL, MSGSIZE, path_command.value, NULL); 939 if (len) { 940 result = &path_command; 941 } 942 } 943 else { 944 /* No extension - search using registered extensions. */ 945 rc = _wdupenv_s(&pathext, &varsize, L"PATHEXT"); 946 if (rc == 0) { 947 extension = wcstok_s(pathext, L";", &context); 948 while (extension) { 949 len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL); 950 if (len) { 951 result = &path_command; 952 break; 953 } 954 extension = wcstok_s(NULL, L";", &context); 955 } 956 free(pathext); 957 } 958 } 959 return result; 960} 961 962#endif 963 964static COMMAND * find_command(wchar_t * name) 965{ 966 COMMAND * result = NULL; 967 COMMAND * cp = commands; 968 int i; 969 970 for (i = 0; i < num_commands; i++, cp++) { 971 if (_wcsicmp(cp->key, name) == 0) { 972 result = cp; 973 break; 974 } 975 } 976#if defined(SEARCH_PATH) 977 if (result == NULL) 978 result = find_on_path(name); 979#endif 980 return result; 981} 982 983static void 984update_command(COMMAND * cp, wchar_t * name, wchar_t * cmdline) 985{ 986 wcsncpy_s(cp->key, MAX_PATH, name, _TRUNCATE); 987 wcsncpy_s(cp->value, MSGSIZE, cmdline, _TRUNCATE); 988} 989 990static void 991add_command(wchar_t * name, wchar_t * cmdline) 992{ 993 if (num_commands >= MAX_COMMANDS) { 994 debug(L"can't add %ls = '%ls': no room\n", name, cmdline); 995 } 996 else { 997 COMMAND * cp = &commands[num_commands++]; 998 999 update_command(cp, name, cmdline); 1000 } 1001} 1002 1003static void 1004read_config_file(wchar_t * config_path) 1005{ 1006 wchar_t keynames[MSGSIZE]; 1007 wchar_t value[MSGSIZE]; 1008 DWORD read; 1009 wchar_t * key; 1010 COMMAND * cp; 1011 wchar_t * cmdp; 1012 1013 read = GetPrivateProfileStringW(L"commands", NULL, NULL, keynames, MSGSIZE, 1014 config_path); 1015 if (read == MSGSIZE - 1) { 1016 debug(L"read_commands: %ls: not enough space for names\n", config_path); 1017 } 1018 key = keynames; 1019 while (*key) { 1020 read = GetPrivateProfileStringW(L"commands", key, NULL, value, MSGSIZE, 1021 config_path); 1022 if (read == MSGSIZE - 1) { 1023 debug(L"read_commands: %ls: not enough space for %ls\n", 1024 config_path, key); 1025 } 1026 cmdp = skip_whitespace(value); 1027 if (*cmdp) { 1028 cp = find_command(key); 1029 if (cp == NULL) 1030 add_command(key, value); 1031 else 1032 update_command(cp, key, value); 1033 } 1034 key += wcslen(key) + 1; 1035 } 1036} 1037 1038static void read_commands() 1039{ 1040 if (launcher_ini_path[0]) 1041 read_config_file(launcher_ini_path); 1042 if (appdata_ini_path[0]) 1043 read_config_file(appdata_ini_path); 1044} 1045 1046static BOOL 1047parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, 1048 wchar_t ** suffix, BOOL *search) 1049{ 1050 BOOL rc = FALSE; 1051 SHEBANG * vpp; 1052 size_t plen; 1053 wchar_t * p; 1054 wchar_t zapped; 1055 wchar_t * endp = shebang_line + nchars - 1; 1056 COMMAND * cp; 1057 wchar_t * skipped; 1058 1059 *command = NULL; /* failure return */ 1060 *suffix = NULL; 1061 *search = FALSE; 1062 1063 if ((*shebang_line++ == L'#') && (*shebang_line++ == L'!')) { 1064 shebang_line = skip_whitespace(shebang_line); 1065 if (*shebang_line) { 1066 *command = shebang_line; 1067 for (vpp = builtin_virtual_paths; vpp->shebang; ++vpp) { 1068 plen = wcslen(vpp->shebang); 1069 if (wcsncmp(shebang_line, vpp->shebang, plen) == 0) { 1070 rc = TRUE; 1071 *search = vpp->search; 1072 /* We can do this because all builtin commands contain 1073 * "python". 1074 */ 1075 *command = wcsstr(shebang_line, L"python"); 1076 break; 1077 } 1078 } 1079 if (vpp->shebang == NULL) { 1080 /* 1081 * Not found in builtins - look in customized commands. 1082 * 1083 * We can't permanently modify the shebang line in case 1084 * it's not a customized command, but we can temporarily 1085 * stick a NUL after the command while searching for it, 1086 * then put back the char we zapped. 1087 */ 1088#if defined(SKIP_PREFIX) 1089 skipped = skip_prefix(shebang_line); 1090#else 1091 skipped = shebang_line; 1092#endif 1093 p = wcspbrk(skipped, L" \t\r\n"); 1094 if (p != NULL) { 1095 zapped = *p; 1096 *p = L'\0'; 1097 } 1098 cp = find_command(skipped); 1099 if (p != NULL) 1100 *p = zapped; 1101 if (cp != NULL) { 1102 *command = cp->value; 1103 if (p != NULL) 1104 *suffix = skip_whitespace(p); 1105 } 1106 } 1107 /* remove trailing whitespace */ 1108 while ((endp > shebang_line) && isspace(*endp)) 1109 --endp; 1110 if (endp > shebang_line) 1111 endp[1] = L'\0'; 1112 } 1113 } 1114 return rc; 1115} 1116 1117/* #define CP_UTF8 65001 defined in winnls.h */ 1118#define CP_UTF16LE 1200 1119#define CP_UTF16BE 1201 1120#define CP_UTF32LE 12000 1121#define CP_UTF32BE 12001 1122 1123typedef struct { 1124 int length; 1125 char sequence[4]; 1126 UINT code_page; 1127} BOM; 1128 1129/* 1130 * Strictly, we don't need to handle UTF-16 and UTF-32, since Python itself 1131 * doesn't. Never mind, one day it might - there's no harm leaving it in. 1132 */ 1133static BOM BOMs[] = { 1134 { 3, { 0xEF, 0xBB, 0xBF }, CP_UTF8 }, /* UTF-8 - keep first */ 1135 /* Test UTF-32LE before UTF-16LE since UTF-16LE BOM is a prefix 1136 * of UTF-32LE BOM. */ 1137 { 4, { 0xFF, 0xFE, 0x00, 0x00 }, CP_UTF32LE }, /* UTF-32LE */ 1138 { 4, { 0x00, 0x00, 0xFE, 0xFF }, CP_UTF32BE }, /* UTF-32BE */ 1139 { 2, { 0xFF, 0xFE }, CP_UTF16LE }, /* UTF-16LE */ 1140 { 2, { 0xFE, 0xFF }, CP_UTF16BE }, /* UTF-16BE */ 1141 { 0 } /* sentinel */ 1142}; 1143 1144static BOM * 1145find_BOM(char * buffer) 1146{ 1147/* 1148 * Look for a BOM in the input and return a pointer to the 1149 * corresponding structure, or NULL if not found. 1150 */ 1151 BOM * result = NULL; 1152 BOM *bom; 1153 1154 for (bom = BOMs; bom->length; bom++) { 1155 if (strncmp(bom->sequence, buffer, bom->length) == 0) { 1156 result = bom; 1157 break; 1158 } 1159 } 1160 return result; 1161} 1162 1163static char * 1164find_terminator(char * buffer, int len, BOM *bom) 1165{ 1166 char * result = NULL; 1167 char * end = buffer + len; 1168 char * p; 1169 char c; 1170 int cp; 1171 1172 for (p = buffer; p < end; p++) { 1173 c = *p; 1174 if (c == '\r') { 1175 result = p; 1176 break; 1177 } 1178 if (c == '\n') { 1179 result = p; 1180 break; 1181 } 1182 } 1183 if (result != NULL) { 1184 cp = bom->code_page; 1185 1186 /* adjustments to include all bytes of the char */ 1187 /* no adjustment needed for UTF-8 or big endian */ 1188 if (cp == CP_UTF16LE) 1189 ++result; 1190 else if (cp == CP_UTF32LE) 1191 result += 3; 1192 ++result; /* point just past terminator */ 1193 } 1194 return result; 1195} 1196 1197static BOOL 1198validate_version(wchar_t * p) 1199{ 1200 /* 1201 Version information should start with the major version, 1202 Optionally followed by a period and a minor version, 1203 Optionally followed by a minus and one of 32 or 64. 1204 Valid examples: 1205 2 1206 3 1207 2.7 1208 3.6 1209 2.7-32 1210 The intent is to add to the valid patterns: 1211 3.10 1212 3-32 1213 3.6-64 1214 3-64 1215 */ 1216 BOOL result = (p != NULL); /* Default to False if null pointer. */ 1217 1218 result = result && iswdigit(*p); /* Result = False if first string element is not a digit. */ 1219 1220 while (result && iswdigit(*p)) /* Require a major version */ 1221 ++p; /* Skip all leading digit(s) */ 1222 if (result && (*p == L'.')) /* Allow . for major minor separator.*/ 1223 { 1224 result = iswdigit(*++p); /* Must be at least one digit */ 1225 while (result && iswdigit(*++p)) ; /* Skip any more Digits */ 1226 } 1227 if (result && (*p == L'-')) { /* Allow - for Bits Separator */ 1228 switch(*++p){ 1229 case L'3': /* 3 is OK */ 1230 result = (*++p == L'2') && !*++p; /* only if followed by 2 and ended.*/ 1231 break; 1232 case L'6': /* 6 is OK */ 1233 result = (*++p == L'4') && !*++p; /* only if followed by 4 and ended.*/ 1234 break; 1235 default: 1236 result = FALSE; 1237 break; 1238 } 1239 } 1240 result = result && !*p; /* Must have reached EOS */ 1241 return result; 1242 1243} 1244 1245typedef struct { 1246 unsigned short min; 1247 unsigned short max; 1248 wchar_t version[MAX_VERSION_SIZE]; 1249} PYC_MAGIC; 1250 1251static PYC_MAGIC magic_values[] = { 1252 { 50823, 50823, L"2.0" }, 1253 { 60202, 60202, L"2.1" }, 1254 { 60717, 60717, L"2.2" }, 1255 { 62011, 62021, L"2.3" }, 1256 { 62041, 62061, L"2.4" }, 1257 { 62071, 62131, L"2.5" }, 1258 { 62151, 62161, L"2.6" }, 1259 { 62171, 62211, L"2.7" }, 1260 { 3000, 3131, L"3.0" }, 1261 { 3141, 3151, L"3.1" }, 1262 { 3160, 3180, L"3.2" }, 1263 { 3190, 3230, L"3.3" }, 1264 { 3250, 3310, L"3.4" }, 1265 { 3320, 3351, L"3.5" }, 1266 { 3360, 3379, L"3.6" }, 1267 { 3390, 3399, L"3.7" }, 1268 { 3400, 3419, L"3.8" }, 1269 { 3420, 3429, L"3.9" }, 1270 { 3430, 3449, L"3.10" }, 1271 /* Allow 50 magic numbers per version from here on */ 1272 { 3450, 3499, L"3.11" }, 1273 { 3500, 3549, L"3.12" }, 1274 { 0 } 1275}; 1276 1277static INSTALLED_PYTHON * 1278find_by_magic(unsigned short magic) 1279{ 1280 INSTALLED_PYTHON * result = NULL; 1281 PYC_MAGIC * mp; 1282 1283 for (mp = magic_values; mp->min; mp++) { 1284 if ((magic >= mp->min) && (magic <= mp->max)) { 1285 result = locate_python(mp->version, FALSE); 1286 if (result != NULL) 1287 break; 1288 } 1289 } 1290 return result; 1291} 1292 1293static void 1294maybe_handle_shebang(wchar_t ** argv, wchar_t * cmdline) 1295{ 1296/* 1297 * Look for a shebang line in the first argument. If found 1298 * and we spawn a child process, this never returns. If it 1299 * does return then we process the args "normally". 1300 * 1301 * argv[0] might be a filename with a shebang. 1302 */ 1303 FILE * fp; 1304 errno_t rc = _wfopen_s(&fp, *argv, L"rb"); 1305 char buffer[BUFSIZE]; 1306 wchar_t shebang_line[BUFSIZE + 1]; 1307 size_t read; 1308 char *p; 1309 char * start; 1310 char * shebang_alias = (char *) shebang_line; 1311 BOM* bom; 1312 int i, j, nchars = 0; 1313 int header_len; 1314 BOOL is_virt; 1315 BOOL search; 1316 wchar_t * command; 1317 wchar_t * suffix; 1318 COMMAND *cmd = NULL; 1319 INSTALLED_PYTHON * ip; 1320 1321 if (rc == 0) { 1322 read = fread(buffer, sizeof(char), BUFSIZE, fp); 1323 debug(L"maybe_handle_shebang: read %zd bytes\n", read); 1324 fclose(fp); 1325 1326 if ((read >= 4) && (buffer[3] == '\n') && (buffer[2] == '\r')) { 1327 ip = find_by_magic((((unsigned char)buffer[1]) << 8 | 1328 (unsigned char)buffer[0]) & 0xFFFF); 1329 if (ip != NULL) { 1330 debug(L"script file is compiled against Python %ls\n", 1331 ip->version); 1332 invoke_child(ip->executable, NULL, cmdline); 1333 } 1334 } 1335 /* Look for BOM */ 1336 bom = find_BOM(buffer); 1337 if (bom == NULL) { 1338 start = buffer; 1339 debug(L"maybe_handle_shebang: BOM not found, using UTF-8\n"); 1340 bom = BOMs; /* points to UTF-8 entry - the default */ 1341 } 1342 else { 1343 debug(L"maybe_handle_shebang: BOM found, code page %u\n", 1344 bom->code_page); 1345 start = &buffer[bom->length]; 1346 } 1347 p = find_terminator(start, BUFSIZE, bom); 1348 /* 1349 * If no CR or LF was found in the heading, 1350 * we assume it's not a shebang file. 1351 */ 1352 if (p == NULL) { 1353 debug(L"maybe_handle_shebang: No line terminator found\n"); 1354 } 1355 else { 1356 /* 1357 * Found line terminator - parse the shebang. 1358 * 1359 * Strictly, we don't need to handle UTF-16 anf UTF-32, 1360 * since Python itself doesn't. 1361 * Never mind, one day it might. 1362 */ 1363 header_len = (int) (p - start); 1364 switch(bom->code_page) { 1365 case CP_UTF8: 1366 nchars = MultiByteToWideChar(bom->code_page, 1367 0, 1368 start, header_len, shebang_line, 1369 BUFSIZE); 1370 break; 1371 case CP_UTF16BE: 1372 if (header_len % 2 != 0) { 1373 debug(L"maybe_handle_shebang: UTF-16BE, but an odd number \ 1374of bytes: %d\n", header_len); 1375 /* nchars = 0; Not needed - initialised to 0. */ 1376 } 1377 else { 1378 for (i = header_len; i > 0; i -= 2) { 1379 shebang_alias[i - 1] = start[i - 2]; 1380 shebang_alias[i - 2] = start[i - 1]; 1381 } 1382 nchars = header_len / sizeof(wchar_t); 1383 } 1384 break; 1385 case CP_UTF16LE: 1386 if ((header_len % 2) != 0) { 1387 debug(L"UTF-16LE, but an odd number of bytes: %d\n", 1388 header_len); 1389 /* nchars = 0; Not needed - initialised to 0. */ 1390 } 1391 else { 1392 /* no actual conversion needed. */ 1393 memcpy(shebang_line, start, header_len); 1394 nchars = header_len / sizeof(wchar_t); 1395 } 1396 break; 1397 case CP_UTF32BE: 1398 if (header_len % 4 != 0) { 1399 debug(L"UTF-32BE, but not divisible by 4: %d\n", 1400 header_len); 1401 /* nchars = 0; Not needed - initialised to 0. */ 1402 } 1403 else { 1404 for (i = header_len, j = header_len / 2; i > 0; i -= 4, 1405 j -= 2) { 1406 shebang_alias[j - 1] = start[i - 2]; 1407 shebang_alias[j - 2] = start[i - 1]; 1408 } 1409 nchars = header_len / sizeof(wchar_t); 1410 } 1411 break; 1412 case CP_UTF32LE: 1413 if (header_len % 4 != 0) { 1414 debug(L"UTF-32LE, but not divisible by 4: %d\n", 1415 header_len); 1416 /* nchars = 0; Not needed - initialised to 0. */ 1417 } 1418 else { 1419 for (i = header_len, j = header_len / 2; i > 0; i -= 4, 1420 j -= 2) { 1421 shebang_alias[j - 1] = start[i - 3]; 1422 shebang_alias[j - 2] = start[i - 4]; 1423 } 1424 nchars = header_len / sizeof(wchar_t); 1425 } 1426 break; 1427 } 1428 if (nchars > 0) { 1429 shebang_line[--nchars] = L'\0'; 1430 is_virt = parse_shebang(shebang_line, nchars, &command, 1431 &suffix, &search); 1432 if (command != NULL) { 1433 debug(L"parse_shebang: found command: %ls\n", command); 1434 if (!is_virt) { 1435 invoke_child(command, suffix, cmdline); 1436 } 1437 else { 1438 suffix = wcschr(command, L' '); 1439 if (suffix != NULL) { 1440 *suffix++ = L'\0'; 1441 suffix = skip_whitespace(suffix); 1442 } 1443 if (wcsncmp(command, L"python", 6)) 1444 error(RC_BAD_VIRTUAL_PATH, L"Unknown virtual \ 1445path '%ls'", command); 1446 command += 6; /* skip past "python" */ 1447 if (search && ((*command == L'\0') || isspace(*command))) { 1448 /* Command is eligible for path search, and there 1449 * is no version specification. 1450 */ 1451 debug(L"searching PATH for python executable\n"); 1452 cmd = find_on_path(PYTHON_EXECUTABLE); 1453 debug(L"Python on path: %ls\n", cmd ? cmd->value : L"<not found>"); 1454 if (cmd) { 1455 debug(L"located python on PATH: %ls\n", cmd->value); 1456 invoke_child(cmd->value, suffix, cmdline); 1457 /* Exit here, as we have found the command */ 1458 return; 1459 } 1460 /* FALL THROUGH: No python found on PATH, so fall 1461 * back to locating the correct installed python. 1462 */ 1463 } 1464 if (*command && !validate_version(command)) 1465 error(RC_BAD_VIRTUAL_PATH, L"Invalid version \ 1466specification: '%ls'.\nIn the first line of the script, 'python' needs to be \ 1467followed by a valid version specifier.\nPlease check the documentation.", 1468 command); 1469 /* TODO could call validate_version(command) */ 1470 ip = locate_python(command, TRUE); 1471 if (ip == NULL) { 1472 error(RC_NO_PYTHON, L"Requested Python version \ 1473(%ls) is not installed", command); 1474 } 1475 else { 1476 invoke_child(ip->executable, suffix, cmdline); 1477 } 1478 } 1479 } 1480 } 1481 } 1482 } 1483} 1484 1485static wchar_t * 1486skip_me(wchar_t * cmdline) 1487{ 1488 BOOL quoted; 1489 wchar_t c; 1490 wchar_t * result = cmdline; 1491 1492 quoted = cmdline[0] == L'\"'; 1493 if (!quoted) 1494 c = L' '; 1495 else { 1496 c = L'\"'; 1497 ++result; 1498 } 1499 result = wcschr(result, c); 1500 if (result == NULL) /* when, for example, just exe name on command line */ 1501 result = L""; 1502 else { 1503 ++result; /* skip past space or closing quote */ 1504 result = skip_whitespace(result); 1505 } 1506 return result; 1507} 1508 1509static DWORD version_high = 0; 1510static DWORD version_low = 0; 1511 1512static void 1513get_version_info(wchar_t * version_text, size_t size) 1514{ 1515 WORD maj, min, rel, bld; 1516 1517 if (!version_high && !version_low) 1518 wcsncpy_s(version_text, size, L"0.1", _TRUNCATE); /* fallback */ 1519 else { 1520 maj = HIWORD(version_high); 1521 min = LOWORD(version_high); 1522 rel = HIWORD(version_low); 1523 bld = LOWORD(version_low); 1524 _snwprintf_s(version_text, size, _TRUNCATE, L"%d.%d.%d.%d", maj, 1525 min, rel, bld); 1526 } 1527} 1528 1529static void 1530show_help_text(wchar_t ** argv) 1531{ 1532 wchar_t version_text [MAX_PATH]; 1533#if defined(_M_X64) 1534 BOOL canDo64bit = TRUE; 1535#else 1536 /* If we are a 32bit process on a 64bit Windows, first hit the 64bit keys. */ 1537 BOOL canDo64bit = FALSE; 1538 IsWow64Process(GetCurrentProcess(), &canDo64bit); 1539#endif 1540 1541 get_version_info(version_text, MAX_PATH); 1542 fwprintf(stdout, L"\ 1543Python Launcher for Windows Version %ls\n\n", version_text); 1544 fwprintf(stdout, L"\ 1545usage:\n\ 1546%ls [launcher-args] [python-args] [script [script-args]]\n\n", argv[0]); 1547 fputws(L"\ 1548Launcher arguments:\n\n\ 1549-2 : Launch the latest Python 2.x version\n\ 1550-3 : Launch the latest Python 3.x version\n\ 1551-X.Y : Launch the specified Python version\n", stdout); 1552 if (canDo64bit) { 1553 fputws(L"\ 1554 The above all default to 64 bit if a matching 64 bit python is present.\n\ 1555-X.Y-32: Launch the specified 32bit Python version\n\ 1556-X-32 : Launch the latest 32bit Python X version\n\ 1557-X.Y-64: Launch the specified 64bit Python version\n\ 1558-X-64 : Launch the latest 64bit Python X version", stdout); 1559 } 1560 fputws(L"\n-0 --list : List the available pythons", stdout); 1561 fputws(L"\n-0p --list-paths : List with paths", stdout); 1562 fputws(L"\n\n If no script is specified the specified interpreter is opened.", stdout); 1563 fputws(L"\nIf an exact version is not given, using the latest version can be overridden by", stdout); 1564 fputws(L"\nany of the following, (in priority order):", stdout); 1565 fputws(L"\n An active virtual environment", stdout); 1566 fputws(L"\n A shebang line in the script (if present)", stdout); 1567 fputws(L"\n With -2 or -3 flag a matching PY_PYTHON2 or PY_PYTHON3 Environment variable", stdout); 1568 fputws(L"\n A PY_PYTHON Environment variable", stdout); 1569 fputws(L"\n From [defaults] in py.ini in your %LOCALAPPDATA%\\py.ini", stdout); 1570 fputws(L"\n From [defaults] in py.ini beside py.exe (use `where py` to locate)", stdout); 1571 fputws(L"\n\nThe following help text is from Python:\n\n", stdout); 1572 fflush(stdout); 1573} 1574 1575static BOOL 1576show_python_list(wchar_t ** argv) 1577{ 1578 /* 1579 * Display options -0 1580 */ 1581 INSTALLED_PYTHON * result = NULL; 1582 INSTALLED_PYTHON * ip = installed_pythons; /* List of installed pythons */ 1583 INSTALLED_PYTHON * defpy = locate_python(L"", FALSE); 1584 size_t i = 0; 1585 wchar_t *p = argv[1]; 1586 wchar_t *ver_fmt = L"-%ls-%d"; 1587 wchar_t *fmt = L"\n %ls"; 1588 wchar_t *defind = L" *"; /* Default indicator */ 1589 1590 /* 1591 * Output informational messages to stderr to keep output 1592 * clean for use in pipes, etc. 1593 */ 1594 fwprintf(stderr, 1595 L"Installed Pythons found by %s Launcher for Windows", argv[0]); 1596 if (!_wcsicmp(p, L"-0p") || !_wcsicmp(p, L"--list-paths")) 1597 fmt = L"\n %-15ls%ls"; /* include path */ 1598 1599 if (num_installed_pythons == 0) /* We have somehow got here without searching for pythons */ 1600 locate_all_pythons(); /* Find them, Populates installed_pythons */ 1601 1602 if (num_installed_pythons == 0) /* No pythons found */ 1603 fwprintf(stderr, L"\nNo Installed Pythons Found!"); 1604 else 1605 { 1606 for (i = 0; i < num_installed_pythons; i++, ip++) { 1607 wchar_t version[BUFSIZ]; 1608 if (wcscmp(ip->version, L"venv") == 0) { 1609 wcscpy_s(version, BUFSIZ, L"(venv)"); 1610 } 1611 else { 1612 swprintf_s(version, BUFSIZ, ver_fmt, ip->version, ip->bits); 1613 } 1614 1615 if (ip->exe_display[0]) { 1616 fwprintf(stdout, fmt, version, ip->exe_display); 1617 } 1618 else { 1619 fwprintf(stdout, fmt, version, ip->executable); 1620 } 1621 /* If there is a default indicate it */ 1622 if (defpy == ip) 1623 fwprintf(stderr, defind); 1624 } 1625 } 1626 1627 if ((defpy == NULL) && (num_installed_pythons > 0)) 1628 /* We have pythons but none is the default */ 1629 fwprintf(stderr, L"\n\nCan't find a Default Python.\n\n"); 1630 else 1631 fwprintf(stderr, L"\n\n"); /* End with a blank line */ 1632 return FALSE; /* If this has been called we cannot continue */ 1633} 1634 1635#if defined(VENV_REDIRECT) 1636 1637static int 1638find_home_value(const char *buffer, const char **start, DWORD *length) 1639{ 1640 for (const char *s = strstr(buffer, "home"); s; s = strstr(s + 1, "\nhome")) { 1641 if (*s == '\n') { 1642 ++s; 1643 } 1644 for (int i = 4; i > 0 && *s; --i, ++s); 1645 1646 while (*s && iswspace(*s)) { 1647 ++s; 1648 } 1649 if (*s != L'=') { 1650 continue; 1651 } 1652 1653 do { 1654 ++s; 1655 } while (*s && iswspace(*s)); 1656 1657 *start = s; 1658 char *nl = strchr(s, '\n'); 1659 if (nl) { 1660 *length = (DWORD)((ptrdiff_t)nl - (ptrdiff_t)s); 1661 } else { 1662 *length = (DWORD)strlen(s); 1663 } 1664 return 1; 1665 } 1666 return 0; 1667} 1668#endif 1669 1670static wchar_t * 1671wcsdup_pad(const wchar_t *s, int padding, int *newlen) 1672{ 1673 size_t len = wcslen(s); 1674 len += 1 + padding; 1675 wchar_t *r = (wchar_t *)malloc(len * sizeof(wchar_t)); 1676 if (!r) { 1677 return NULL; 1678 } 1679 if (wcscpy_s(r, len, s)) { 1680 free(r); 1681 return NULL; 1682 } 1683 *newlen = len < MAXINT ? (int)len : MAXINT; 1684 return r; 1685} 1686 1687static wchar_t * 1688get_process_name() 1689{ 1690 DWORD bufferLen = MAX_PATH; 1691 DWORD len = bufferLen; 1692 wchar_t *r = NULL; 1693 1694 while (!r) { 1695 r = (wchar_t *)malloc(bufferLen * sizeof(wchar_t)); 1696 if (!r) { 1697 error(RC_NO_MEMORY, L"out of memory"); 1698 return NULL; 1699 } 1700 len = GetModuleFileNameW(NULL, r, bufferLen); 1701 if (len == 0) { 1702 free(r); 1703 error(0, L"Failed to get module name"); 1704 return NULL; 1705 } else if (len == bufferLen && 1706 GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 1707 free(r); 1708 r = NULL; 1709 bufferLen *= 2; 1710 } 1711 } 1712 1713 return r; 1714} 1715 1716static int 1717process(int argc, wchar_t ** argv) 1718{ 1719 wchar_t * wp; 1720 wchar_t * command; 1721 wchar_t * executable; 1722 wchar_t * p; 1723 wchar_t * argv0; 1724 int rc = 0; 1725 INSTALLED_PYTHON * ip; 1726 BOOL valid; 1727 DWORD size, attrs; 1728 wchar_t message[MSGSIZE]; 1729 void * version_data; 1730 VS_FIXEDFILEINFO * file_info; 1731 UINT block_size; 1732#if defined(VENV_REDIRECT) 1733 wchar_t * venv_cfg_path; 1734 int newlen; 1735#elif defined(SCRIPT_WRAPPER) 1736 wchar_t * newcommand; 1737 wchar_t * av[2]; 1738 int newlen; 1739 HRESULT hr; 1740 int index; 1741#else 1742 HRESULT hr; 1743 int index; 1744#endif 1745 1746 setvbuf(stderr, (char *)NULL, _IONBF, 0); 1747 wp = get_env(L"PYLAUNCH_DEBUG"); 1748 if ((wp != NULL) && (*wp != L'\0')) 1749 log_fp = stderr; 1750 1751#if defined(_M_X64) 1752 debug(L"launcher build: 64bit\n"); 1753#else 1754 debug(L"launcher build: 32bit\n"); 1755#endif 1756#if defined(_WINDOWS) 1757 debug(L"launcher executable: Windows\n"); 1758#else 1759 debug(L"launcher executable: Console\n"); 1760#endif 1761#if !defined(VENV_REDIRECT) 1762 /* Get the local appdata folder (non-roaming) */ 1763 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, 1764 NULL, 0, appdata_ini_path); 1765 if (hr != S_OK) { 1766 debug(L"SHGetFolderPath failed: %X\n", hr); 1767 appdata_ini_path[0] = L'\0'; 1768 } 1769 else { 1770 wcsncat_s(appdata_ini_path, MAX_PATH, L"\\py.ini", _TRUNCATE); 1771 attrs = GetFileAttributesW(appdata_ini_path); 1772 if (attrs == INVALID_FILE_ATTRIBUTES) { 1773 debug(L"File '%ls' non-existent\n", appdata_ini_path); 1774 appdata_ini_path[0] = L'\0'; 1775 } else { 1776 debug(L"Using local configuration file '%ls'\n", appdata_ini_path); 1777 } 1778 } 1779#endif 1780 argv0 = get_process_name(); 1781 size = GetFileVersionInfoSizeW(argv0, &size); 1782 if (size == 0) { 1783 winerror(GetLastError(), message, MSGSIZE); 1784 debug(L"GetFileVersionInfoSize failed: %ls\n", message); 1785 } 1786 else { 1787 version_data = malloc(size); 1788 if (version_data) { 1789 valid = GetFileVersionInfoW(argv0, 0, size, 1790 version_data); 1791 if (!valid) 1792 debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); 1793 else { 1794 valid = VerQueryValueW(version_data, L"\\", 1795 (LPVOID *) &file_info, &block_size); 1796 if (!valid) 1797 debug(L"VerQueryValue failed: %X\n", GetLastError()); 1798 else { 1799 version_high = file_info->dwFileVersionMS; 1800 version_low = file_info->dwFileVersionLS; 1801 } 1802 } 1803 free(version_data); 1804 } 1805 } 1806 1807#if defined(VENV_REDIRECT) 1808 /* Allocate some extra space for new filenames */ 1809 venv_cfg_path = wcsdup_pad(argv0, 32, &newlen); 1810 if (!venv_cfg_path) { 1811 error(RC_NO_MEMORY, L"Failed to copy module name"); 1812 } 1813 p = wcsrchr(venv_cfg_path, L'\\'); 1814 1815 if (p == NULL) { 1816 error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); 1817 } 1818 p[0] = L'\0'; 1819 wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); 1820 attrs = GetFileAttributesW(venv_cfg_path); 1821 if (attrs == INVALID_FILE_ATTRIBUTES) { 1822 debug(L"File '%ls' non-existent\n", venv_cfg_path); 1823 p[0] = '\0'; 1824 p = wcsrchr(venv_cfg_path, L'\\'); 1825 if (p != NULL) { 1826 p[0] = '\0'; 1827 wcscat_s(venv_cfg_path, newlen, L"\\pyvenv.cfg"); 1828 attrs = GetFileAttributesW(venv_cfg_path); 1829 if (attrs == INVALID_FILE_ATTRIBUTES) { 1830 debug(L"File '%ls' non-existent\n", venv_cfg_path); 1831 error(RC_NO_VENV_CFG, L"No pyvenv.cfg file"); 1832 } 1833 } 1834 } 1835 debug(L"Using venv configuration file '%ls'\n", venv_cfg_path); 1836#else 1837 /* Allocate some extra space for new filenames */ 1838 if (wcscpy_s(launcher_ini_path, MAX_PATH, argv0)) { 1839 error(RC_NO_MEMORY, L"Failed to copy module name"); 1840 } 1841 p = wcsrchr(launcher_ini_path, L'\\'); 1842 1843 if (p == NULL) { 1844 debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", 1845 launcher_ini_path); 1846 launcher_ini_path[0] = L'\0'; 1847 } 1848 else { 1849 p[0] = L'\0'; 1850 wcscat_s(launcher_ini_path, MAX_PATH, L"\\py.ini"); 1851 attrs = GetFileAttributesW(launcher_ini_path); 1852 if (attrs == INVALID_FILE_ATTRIBUTES) { 1853 debug(L"File '%ls' non-existent\n", launcher_ini_path); 1854 launcher_ini_path[0] = L'\0'; 1855 } else { 1856 debug(L"Using global configuration file '%ls'\n", launcher_ini_path); 1857 } 1858 } 1859#endif 1860 1861 command = skip_me(GetCommandLineW()); 1862 debug(L"Called with command line: %ls\n", command); 1863 1864#if !defined(VENV_REDIRECT) 1865 /* bpo-35811: The __PYVENV_LAUNCHER__ variable is used to 1866 * override sys.executable and locate the original prefix path. 1867 * However, if it is silently inherited by a non-venv Python 1868 * process, that process will believe it is running in the venv 1869 * still. This is the only place where *we* can clear it (that is, 1870 * when py.exe is being used to launch Python), so we do. 1871 */ 1872 SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", NULL); 1873#endif 1874 1875#if defined(SCRIPT_WRAPPER) 1876 /* The launcher is being used in "script wrapper" mode. 1877 * There should therefore be a Python script named <exename>-script.py in 1878 * the same directory as the launcher executable. 1879 * Put the script name into argv as the first (script name) argument. 1880 */ 1881 1882 /* Get the wrapped script name - if the script is not present, this will 1883 * terminate the program with an error. 1884 */ 1885 locate_wrapped_script(); 1886 1887 /* Add the wrapped script to the start of command */ 1888 newlen = wcslen(wrapped_script_path) + wcslen(command) + 2; /* ' ' + NUL */ 1889 newcommand = malloc(sizeof(wchar_t) * newlen); 1890 if (!newcommand) { 1891 error(RC_NO_MEMORY, L"Could not allocate new command line"); 1892 } 1893 else { 1894 wcscpy_s(newcommand, newlen, wrapped_script_path); 1895 wcscat_s(newcommand, newlen, L" "); 1896 wcscat_s(newcommand, newlen, command); 1897 debug(L"Running wrapped script with command line '%ls'\n", newcommand); 1898 read_commands(); 1899 av[0] = wrapped_script_path; 1900 av[1] = NULL; 1901 maybe_handle_shebang(av, newcommand); 1902 /* Returns if no shebang line - pass to default processing */ 1903 command = newcommand; 1904 valid = FALSE; 1905 } 1906#elif defined(VENV_REDIRECT) 1907 { 1908 FILE *f; 1909 char buffer[4096]; /* 4KB should be enough for anybody */ 1910 char *start; 1911 DWORD len, cch, cch_actual; 1912 size_t cb; 1913 if (_wfopen_s(&f, venv_cfg_path, L"r")) { 1914 error(RC_BAD_VENV_CFG, L"Cannot read '%ls'", venv_cfg_path); 1915 } 1916 cb = fread_s(buffer, sizeof(buffer), sizeof(buffer[0]), 1917 sizeof(buffer) / sizeof(buffer[0]), f); 1918 fclose(f); 1919 1920 if (!find_home_value(buffer, &start, &len)) { 1921 error(RC_BAD_VENV_CFG, L"Cannot find home in '%ls'", 1922 venv_cfg_path); 1923 } 1924 1925 cch = MultiByteToWideChar(CP_UTF8, 0, start, len, NULL, 0); 1926 if (!cch) { 1927 error(0, L"Cannot determine memory for home path"); 1928 } 1929 cch += (DWORD)wcslen(PYTHON_EXECUTABLE) + 4; /* include sep, null and quotes */ 1930 executable = (wchar_t *)malloc(cch * sizeof(wchar_t)); 1931 if (executable == NULL) { 1932 error(RC_NO_MEMORY, L"A memory allocation failed"); 1933 } 1934 /* start with a quote - we'll skip this ahead, but want it for the final string */ 1935 executable[0] = L'"'; 1936 cch_actual = MultiByteToWideChar(CP_UTF8, 0, start, len, &executable[1], cch - 1); 1937 if (!cch_actual) { 1938 error(RC_BAD_VENV_CFG, L"Cannot decode home path in '%ls'", 1939 venv_cfg_path); 1940 } 1941 cch_actual += 1; /* account for the first quote */ 1942 executable[cch_actual] = L'\0'; 1943 if (executable[cch_actual - 1] != L'\\') { 1944 executable[cch_actual++] = L'\\'; 1945 executable[cch_actual] = L'\0'; 1946 } 1947 if (wcscat_s(&executable[1], cch - 1, PYTHON_EXECUTABLE)) { 1948 error(RC_BAD_VENV_CFG, L"Cannot create executable path from '%ls'", 1949 venv_cfg_path); 1950 } 1951 /* there's no trailing quote, so we only have to skip one character for the test */ 1952 if (GetFileAttributesW(&executable[1]) == INVALID_FILE_ATTRIBUTES) { 1953 error(RC_NO_PYTHON, L"No Python at '%ls'", executable); 1954 } 1955 /* now append the final quote */ 1956 wcscat_s(executable, cch, L"\""); 1957 /* smuggle our original path through */ 1958 if (!SetEnvironmentVariableW(L"__PYVENV_LAUNCHER__", argv0)) { 1959 error(0, L"Failed to set launcher environment"); 1960 } 1961 valid = 1; 1962 } 1963#else 1964 if (argc <= 1) { 1965 valid = FALSE; 1966 p = NULL; 1967 } 1968 else { 1969 p = argv[1]; 1970 if ((argc == 2) && // list version args 1971 (!wcsncmp(p, L"-0", wcslen(L"-0")) || 1972 !wcsncmp(p, L"--list", wcslen(L"--list")))) 1973 { 1974 show_python_list(argv); 1975 return rc; 1976 } 1977 valid = valid && (*p == L'-') && validate_version(&p[1]); 1978 if (valid) { 1979 ip = locate_python(&p[1], FALSE); 1980 if (ip == NULL) 1981 { 1982 fwprintf(stdout, \ 1983 L"Python %ls not found!\n", &p[1]); 1984 valid = show_python_list(argv); 1985 error(RC_NO_PYTHON, L"Requested Python version (%ls) not \ 1986installed, use -0 for available pythons", &p[1]); 1987 } 1988 executable = ip->executable; 1989 command += wcslen(p); 1990 command = skip_whitespace(command); 1991 } 1992 else { 1993 for (index = 1; index < argc; ++index) { 1994 if (*argv[index] != L'-') 1995 break; 1996 } 1997 if (index < argc) { 1998 read_commands(); 1999 maybe_handle_shebang(&argv[index], command); 2000 } 2001 } 2002 } 2003#endif 2004 2005 if (!valid) { 2006 if ((argc == 2) && (!_wcsicmp(p, L"-h") || !_wcsicmp(p, L"--help"))) 2007 show_help_text(argv); 2008 if ((argc == 2) && 2009 (!_wcsicmp(p, L"-0") || !_wcsicmp(p, L"--list") || 2010 !_wcsicmp(p, L"-0p") || !_wcsicmp(p, L"--list-paths"))) 2011 { 2012 executable = NULL; /* Info call only */ 2013 } 2014 else { 2015 /* look for the default Python */ 2016 ip = locate_python(L"", FALSE); 2017 if (ip == NULL) 2018 error(RC_NO_PYTHON, L"Can't find a default Python."); 2019 executable = ip->executable; 2020 } 2021 } 2022 if (executable != NULL) 2023 invoke_child(executable, NULL, command); 2024 else 2025 rc = RC_NO_PYTHON; 2026 return rc; 2027} 2028 2029#if defined(_WINDOWS) 2030 2031int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 2032 LPWSTR lpstrCmd, int nShow) 2033{ 2034 return process(__argc, __wargv); 2035} 2036 2037#else 2038 2039int cdecl wmain(int argc, wchar_t ** argv) 2040{ 2041 return process(argc, argv); 2042} 2043 2044#endif 2045