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 between two 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 /* Just for now - static definition */ 45 46 static FILE * log_fp = NULL; 47 48 static wchar_t * 49 skip_whitespace(wchar_t * p) 50 { 51 while (*p && isspace(*p)) 52 ++p; 53 return p; 54 } 55 56 static void 57 debug(wchar_t * format, ...) 58 { 59 va_list va; 60 61 if (log_fp != NULL) { 62 va_start(va, format); 63 vfwprintf_s(log_fp, format, va); 64 } 65 } 66 67 static void 68 winerror(int rc, wchar_t * message, int size) 69 { 70 FormatMessageW( 71 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 72 NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 73 message, size, NULL); 74 } 75 76 static void 77 error(int rc, wchar_t * format, ... ) 78 { 79 va_list va; 80 wchar_t message[MSGSIZE]; 81 wchar_t win_message[MSGSIZE]; 82 int len; 83 84 va_start(va, format); 85 len = _vsnwprintf_s(message, MSGSIZE, _TRUNCATE, format, va); 86 87 if (rc == 0) { /* a Windows error */ 88 winerror(GetLastError(), win_message, MSGSIZE); 89 if (len >= 0) { 90 _snwprintf_s(&message[len], MSGSIZE - len, _TRUNCATE, L": %ls", 91 win_message); 92 } 93 } 94 95 #if !defined(_WINDOWS) 96 fwprintf(stderr, L"%ls\n", message); 97 #else 98 MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), 99 MB_OK); 100 #endif 101 exit(rc); 102 } 103 104 /* 105 * This function is here to simplify memory management 106 * and to treat blank values as if they are absent. 107 */ 108 static wchar_t * get_env(wchar_t * key) 109 { 110 /* This is not thread-safe, just like getenv */ 111 static wchar_t buf[BUFSIZE]; 112 DWORD result = GetEnvironmentVariableW(key, buf, BUFSIZE); 113 114 if (result >= BUFSIZE) { 115 /* Large environment variable. Accept some leakage */ 116 wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1)); 117 if (buf2 == NULL) { 118 error(RC_NO_MEMORY, L"Could not allocate environment buffer"); 119 } 120 GetEnvironmentVariableW(key, buf2, result); 121 return buf2; 122 } 123 124 if (result == 0) 125 /* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND, 126 or an empty environment variable. */ 127 return NULL; 128 129 return buf; 130 } 131 132 #if defined(_WINDOWS) 133 134 #define PYTHON_EXECUTABLE L"pythonw.exe" 135 136 #else 137 138 #define PYTHON_EXECUTABLE L"python.exe" 139 140 #endif 141 142 #define MAX_VERSION_SIZE 4 143 144 typedef struct { 145 wchar_t version[MAX_VERSION_SIZE]; /* m.n */ 146 int bits; /* 32 or 64 */ 147 wchar_t executable[MAX_PATH]; 148 } INSTALLED_PYTHON; 149 150 /* 151 * To avoid messing about with heap allocations, just assume we can allocate 152 * statically and never have to deal with more versions than this. 153 */ 154 #define MAX_INSTALLED_PYTHONS 100 155 156 static INSTALLED_PYTHON installed_pythons[MAX_INSTALLED_PYTHONS]; 157 158 static size_t num_installed_pythons = 0; 159 160 /* 161 * To hold SOFTWARE\Python\PythonCore\X.Y...\InstallPath 162 * The version name can be longer than MAX_VERSION_SIZE, but will be 163 * truncated to just X.Y for comparisons. 164 */ 165 #define IP_BASE_SIZE 40 166 #define IP_VERSION_SIZE 8 167 #define IP_SIZE (IP_BASE_SIZE + IP_VERSION_SIZE) 168 #define CORE_PATH L"SOFTWARE\\Python\\PythonCore" 169 170 static wchar_t * location_checks[] = { 171 L"\\", 172 L"\\PCBuild\\win32\\", 173 L"\\PCBuild\\amd64\\", 174 // To support early 32bit versions of Python that stuck the build binaries 175 // directly in PCBuild... 176 L"\\PCBuild\\", 177 NULL 178 }; 179 180 static INSTALLED_PYTHON * 181 find_existing_python(wchar_t * path) 182 { 183 INSTALLED_PYTHON * result = NULL; 184 size_t i; 185 INSTALLED_PYTHON * ip; 186 187 for (i = 0, ip = installed_pythons; i < num_installed_pythons; i++, ip++) { 188 if (_wcsicmp(path, ip->executable) == 0) { 189 result = ip; 190 break; 191 } 192 } 193 return result; 194 } 195 196 static void 197 locate_pythons_for_key(HKEY root, REGSAM flags) 198 { 199 HKEY core_root, ip_key; 200 LSTATUS status = RegOpenKeyExW(root, CORE_PATH, 0, flags, &core_root); 201 wchar_t message[MSGSIZE]; 202 DWORD i; 203 size_t n; 204 BOOL ok; 205 DWORD type, data_size, attrs; 206 INSTALLED_PYTHON * ip, * pip; 207 wchar_t ip_version[IP_VERSION_SIZE]; 208 wchar_t ip_path[IP_SIZE]; 209 wchar_t * check; 210 wchar_t ** checkp; 211 wchar_t *key_name = (root == HKEY_LOCAL_MACHINE) ? L"HKLM" : L"HKCU"; 212 213 if (status != ERROR_SUCCESS) 214 debug(L"locate_pythons_for_key: unable to open PythonCore key in %ls\n", 215 key_name); 216 else { 217 ip = &installed_pythons[num_installed_pythons]; 218 for (i = 0; num_installed_pythons < MAX_INSTALLED_PYTHONS; i++) { 219 status = RegEnumKeyW(core_root, i, ip_version, IP_VERSION_SIZE); 220 if (status != ERROR_SUCCESS) { 221 if (status != ERROR_NO_MORE_ITEMS) { 222 /* unexpected error */ 223 winerror(status, message, MSGSIZE); 224 debug(L"Can't enumerate registry key for version %ls: %ls\n", 225 ip_version, message); 226 } 227 break; 228 } 229 else { 230 wcsncpy_s(ip->version, MAX_VERSION_SIZE, ip_version, 231 MAX_VERSION_SIZE-1); 232 _snwprintf_s(ip_path, IP_SIZE, _TRUNCATE, 233 L"%ls\\%ls\\InstallPath", CORE_PATH, ip_version); 234 status = RegOpenKeyExW(root, ip_path, 0, flags, &ip_key); 235 if (status != ERROR_SUCCESS) { 236 winerror(status, message, MSGSIZE); 237 // Note: 'message' already has a trailing \n 238 debug(L"%ls\\%ls: %ls", key_name, ip_path, message); 239 continue; 240 } 241 data_size = sizeof(ip->executable) - 1; 242 status = RegQueryValueExW(ip_key, NULL, NULL, &type, 243 (LPBYTE)ip->executable, &data_size); 244 RegCloseKey(ip_key); 245 if (status != ERROR_SUCCESS) { 246 winerror(status, message, MSGSIZE); 247 debug(L"%ls\\%ls: %ls\n", key_name, ip_path, message); 248 continue; 249 } 250 if (type == REG_SZ) { 251 data_size = data_size / sizeof(wchar_t) - 1; /* for NUL */ 252 if (ip->executable[data_size - 1] == L'\\') 253 --data_size; /* reg value ended in a backslash */ 254 /* ip->executable is data_size long */ 255 for (checkp = location_checks; *checkp; ++checkp) { 256 check = *checkp; 257 _snwprintf_s(&ip->executable[data_size], 258 MAX_PATH - data_size, 259 MAX_PATH - data_size, 260 L"%ls%ls", check, PYTHON_EXECUTABLE); 261 attrs = GetFileAttributesW(ip->executable); 262 if (attrs == INVALID_FILE_ATTRIBUTES) { 263 winerror(GetLastError(), message, MSGSIZE); 264 debug(L"locate_pythons_for_key: %ls: %ls", 265 ip->executable, message); 266 } 267 else if (attrs & FILE_ATTRIBUTE_DIRECTORY) { 268 debug(L"locate_pythons_for_key: '%ls' is a \ 269 directory\n", 270 ip->executable, attrs); 271 } 272 else if (find_existing_python(ip->executable)) { 273 debug(L"locate_pythons_for_key: %ls: already \ 274 found\n", ip->executable); 275 } 276 else { 277 /* check the executable type. */ 278 ok = GetBinaryTypeW(ip->executable, &attrs); 279 if (!ok) { 280 debug(L"Failure getting binary type: %ls\n", 281 ip->executable); 282 } 283 else { 284 if (attrs == SCS_64BIT_BINARY) 285 ip->bits = 64; 286 else if (attrs == SCS_32BIT_BINARY) 287 ip->bits = 32; 288 else 289 ip->bits = 0; 290 if (ip->bits == 0) { 291 debug(L"locate_pythons_for_key: %ls: \ 292 invalid binary type: %X\n", 293 ip->executable, attrs); 294 } 295 else { 296 if (wcschr(ip->executable, L' ') != NULL) { 297 /* has spaces, so quote */ 298 n = wcslen(ip->executable); 299 memmove(&ip->executable[1], 300 ip->executable, n * sizeof(wchar_t)); 301 ip->executable[0] = L'\"'; 302 ip->executable[n + 1] = L'\"'; 303 ip->executable[n + 2] = L'\0'; 304 } 305 debug(L"locate_pythons_for_key: %ls \ 306 is a %dbit executable\n", 307 ip->executable, ip->bits); 308 ++num_installed_pythons; 309 pip = ip++; 310 if (num_installed_pythons >= 311 MAX_INSTALLED_PYTHONS) 312 break; 313 /* Copy over the attributes for the next */ 314 *ip = *pip; 315 } 316 } 317 } 318 } 319 } 320 } 321 } 322 RegCloseKey(core_root); 323 } 324 } 325 326 static int 327 compare_pythons(const void * p1, const void * p2) 328 { 329 INSTALLED_PYTHON * ip1 = (INSTALLED_PYTHON *) p1; 330 INSTALLED_PYTHON * ip2 = (INSTALLED_PYTHON *) p2; 331 /* note reverse sorting on version */ 332 int result = wcscmp(ip2->version, ip1->version); 333 334 if (result == 0) 335 result = ip2->bits - ip1->bits; /* 64 before 32 */ 336 return result; 337 } 338 339 static void 340 locate_all_pythons() 341 { 342 #if defined(_M_X64) 343 // If we are a 64bit process, first hit the 32bit keys. 344 debug(L"locating Pythons in 32bit registry\n"); 345 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_32KEY); 346 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_32KEY); 347 #else 348 // If we are a 32bit process on a 64bit Windows, first hit the 64bit keys. 349 BOOL f64 = FALSE; 350 if (IsWow64Process(GetCurrentProcess(), &f64) && f64) { 351 debug(L"locating Pythons in 64bit registry\n"); 352 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_64KEY); 353 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_64KEY); 354 } 355 #endif 356 // now hit the "native" key for this process bittedness. 357 debug(L"locating Pythons in native registry\n"); 358 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ); 359 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ); 360 qsort(installed_pythons, num_installed_pythons, sizeof(INSTALLED_PYTHON), 361 compare_pythons); 362 } 363 364 static INSTALLED_PYTHON * 365 find_python_by_version(wchar_t const * wanted_ver) 366 { 367 INSTALLED_PYTHON * result = NULL; 368 INSTALLED_PYTHON * ip = installed_pythons; 369 size_t i, n; 370 size_t wlen = wcslen(wanted_ver); 371 int bits = 0; 372 373 if (wcsstr(wanted_ver, L"-32")) 374 bits = 32; 375 for (i = 0; i < num_installed_pythons; i++, ip++) { 376 n = wcslen(ip->version); 377 if (n > wlen) 378 n = wlen; 379 if ((wcsncmp(ip->version, wanted_ver, n) == 0) && 380 /* bits == 0 => don't care */ 381 ((bits == 0) || (ip->bits == bits))) { 382 result = ip; 383 break; 384 } 385 } 386 return result; 387 } 388 389 390 static wchar_t * 391 find_python_by_venv() 392 { 393 static wchar_t venv_python[MAX_PATH]; 394 wchar_t *virtual_env = get_env(L"VIRTUAL_ENV"); 395 DWORD attrs; 396 397 /* Check for VIRTUAL_ENV environment variable */ 398 if (virtual_env == NULL || virtual_env[0] == L'\0') { 399 return NULL; 400 } 401 402 /* Check for a python executable in the venv */ 403 debug(L"Checking for Python executable in virtual env '%ls'\n", virtual_env); 404 _snwprintf_s(venv_python, MAX_PATH, _TRUNCATE, 405 L"%ls\\Scripts\\%ls", virtual_env, PYTHON_EXECUTABLE); 406 attrs = GetFileAttributesW(venv_python); 407 if (attrs == INVALID_FILE_ATTRIBUTES) { 408 debug(L"Python executable %ls missing from virtual env\n", venv_python); 409 return NULL; 410 } 411 412 return venv_python; 413 } 414 415 static wchar_t appdata_ini_path[MAX_PATH]; 416 static wchar_t launcher_ini_path[MAX_PATH]; 417 418 /* 419 * Get a value either from the environment or a configuration file. 420 * The key passed in will either be "python", "python2" or "python3". 421 */ 422 static wchar_t * 423 get_configured_value(wchar_t * key) 424 { 425 /* 426 * Note: this static value is used to return a configured value 427 * obtained either from the environment or configuration file. 428 * This should be OK since there wouldn't be any concurrent calls. 429 */ 430 static wchar_t configured_value[MSGSIZE]; 431 wchar_t * result = NULL; 432 wchar_t * found_in = L"environment"; 433 DWORD size; 434 435 /* First, search the environment. */ 436 _snwprintf_s(configured_value, MSGSIZE, _TRUNCATE, L"py_%ls", key); 437 result = get_env(configured_value); 438 if (result == NULL && appdata_ini_path[0]) { 439 /* Not in environment: check local configuration. */ 440 size = GetPrivateProfileStringW(L"defaults", key, NULL, 441 configured_value, MSGSIZE, 442 appdata_ini_path); 443 if (size > 0) { 444 result = configured_value; 445 found_in = appdata_ini_path; 446 } 447 } 448 if (result == NULL && launcher_ini_path[0]) { 449 /* Not in environment or local: check global configuration. */ 450 size = GetPrivateProfileStringW(L"defaults", key, NULL, 451 configured_value, MSGSIZE, 452 launcher_ini_path); 453 if (size > 0) { 454 result = configured_value; 455 found_in = launcher_ini_path; 456 } 457 } 458 if (result) { 459 debug(L"found configured value '%ls=%ls' in %ls\n", 460 key, result, found_in ? found_in : L"(unknown)"); 461 } else { 462 debug(L"found no configured value for '%ls'\n", key); 463 } 464 return result; 465 } 466 467 static INSTALLED_PYTHON * 468 locate_python(wchar_t * wanted_ver, BOOL from_shebang) 469 { 470 static wchar_t config_key [] = { L"pythonX" }; 471 static wchar_t * last_char = &config_key[sizeof(config_key) / 472 sizeof(wchar_t) - 2]; 473 INSTALLED_PYTHON * result = NULL; 474 size_t n = wcslen(wanted_ver); 475 wchar_t * configured_value; 476 477 if (num_installed_pythons == 0) 478 locate_all_pythons(); 479 480 if (n == 1) { /* just major version specified */ 481 *last_char = *wanted_ver; 482 configured_value = get_configured_value(config_key); 483 if (configured_value != NULL) 484 wanted_ver = configured_value; 485 } 486 if (*wanted_ver) { 487 result = find_python_by_version(wanted_ver); 488 debug(L"search for Python version '%ls' found ", wanted_ver); 489 if (result) { 490 debug(L"'%ls'\n", result->executable); 491 } else { 492 debug(L"no interpreter\n"); 493 } 494 } 495 else { 496 *last_char = L'\0'; /* look for an overall default */ 497 configured_value = get_configured_value(config_key); 498 if (configured_value) 499 result = find_python_by_version(configured_value); 500 /* Not found a value yet - try by major version. 501 * If we're looking for an interpreter specified in a shebang line, 502 * we want to try Python 2 first, then Python 3 (for Unix and backward 503 * compatibility). If we're being called interactively, assume the user 504 * wants the latest version available, so try Python 3 first, then 505 * Python 2. 506 */ 507 if (result == NULL) 508 result = find_python_by_version(from_shebang ? L"2" : L"3"); 509 if (result == NULL) 510 result = find_python_by_version(from_shebang ? L"3" : L"2"); 511 debug(L"search for default Python found "); 512 if (result) { 513 debug(L"version %ls at '%ls'\n", 514 result->version, result->executable); 515 } else { 516 debug(L"no interpreter\n"); 517 } 518 } 519 return result; 520 } 521 522 #if defined(SCRIPT_WRAPPER) 523 /* 524 * Check for a script located alongside the executable 525 */ 526 527 #if defined(_WINDOWS) 528 #define SCRIPT_SUFFIX L"-script.pyw" 529 #else 530 #define SCRIPT_SUFFIX L"-script.py" 531 #endif 532 533 static wchar_t wrapped_script_path[MAX_PATH]; 534 535 /* Locate the script being wrapped. 536 * 537 * This code should store the name of the wrapped script in 538 * wrapped_script_path, or terminate the program with an error if there is no 539 * valid wrapped script file. 540 */ 541 static void 542 locate_wrapped_script() 543 { 544 wchar_t * p; 545 size_t plen; 546 DWORD attrs; 547 548 plen = GetModuleFileNameW(NULL, wrapped_script_path, MAX_PATH); 549 p = wcsrchr(wrapped_script_path, L'.'); 550 if (p == NULL) { 551 debug(L"GetModuleFileNameW returned value has no extension: %ls\n", 552 wrapped_script_path); 553 error(RC_NO_SCRIPT, L"Wrapper name '%ls' is not valid.", wrapped_script_path); 554 } 555 556 wcsncpy_s(p, MAX_PATH - (p - wrapped_script_path) + 1, SCRIPT_SUFFIX, _TRUNCATE); 557 attrs = GetFileAttributesW(wrapped_script_path); 558 if (attrs == INVALID_FILE_ATTRIBUTES) { 559 debug(L"File '%ls' non-existent\n", wrapped_script_path); 560 error(RC_NO_SCRIPT, L"Script file '%ls' is not present.", wrapped_script_path); 561 } 562 563 debug(L"Using wrapped script file '%ls'\n", wrapped_script_path); 564 } 565 #endif 566 567 /* 568 * Process creation code 569 */ 570 571 static BOOL 572 safe_duplicate_handle(HANDLE in, HANDLE * pout) 573 { 574 BOOL ok; 575 HANDLE process = GetCurrentProcess(); 576 DWORD rc; 577 578 *pout = NULL; 579 ok = DuplicateHandle(process, in, process, pout, 0, TRUE, 580 DUPLICATE_SAME_ACCESS); 581 if (!ok) { 582 rc = GetLastError(); 583 if (rc == ERROR_INVALID_HANDLE) { 584 debug(L"DuplicateHandle returned ERROR_INVALID_HANDLE\n"); 585 ok = TRUE; 586 } 587 else { 588 debug(L"DuplicateHandle returned %d\n", rc); 589 } 590 } 591 return ok; 592 } 593 594 static BOOL WINAPI 595 ctrl_c_handler(DWORD code) 596 { 597 return TRUE; /* We just ignore all control events. */ 598 } 599 600 static void 601 run_child(wchar_t * cmdline) 602 { 603 HANDLE job; 604 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; 605 DWORD rc; 606 BOOL ok; 607 STARTUPINFOW si; 608 PROCESS_INFORMATION pi; 609 610 #if defined(_WINDOWS) 611 // When explorer launches a Windows (GUI) application, it displays 612 // the "app starting" (the "pointer + hourglass") cursor for a number 613 // of seconds, or until the app does something UI-ish (eg, creating a 614 // window, or fetching a message). As this launcher doesn't do this 615 // directly, that cursor remains even after the child process does these 616 // things. We avoid that by doing a simple post+get message. 617 // See http://bugs.python.org/issue17290 and 618 // https://bitbucket.org/vinay.sajip/pylauncher/issue/20/busy-cursor-for-a-long-time-when-running 619 MSG msg; 620 621 PostMessage(0, 0, 0, 0); 622 GetMessage(&msg, 0, 0, 0); 623 #endif 624 625 debug(L"run_child: about to run '%ls'\n", cmdline); 626 job = CreateJobObject(NULL, NULL); 627 ok = QueryInformationJobObject(job, JobObjectExtendedLimitInformation, 628 &info, sizeof(info), &rc); 629 if (!ok || (rc != sizeof(info)) || !job) 630 error(RC_CREATE_PROCESS, L"Job information querying failed"); 631 info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | 632 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK; 633 ok = SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info, 634 sizeof(info)); 635 if (!ok) 636 error(RC_CREATE_PROCESS, L"Job information setting failed"); 637 memset(&si, 0, sizeof(si)); 638 si.cb = sizeof(si); 639 ok = safe_duplicate_handle(GetStdHandle(STD_INPUT_HANDLE), &si.hStdInput); 640 if (!ok) 641 error(RC_NO_STD_HANDLES, L"stdin duplication failed"); 642 ok = safe_duplicate_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si.hStdOutput); 643 if (!ok) 644 error(RC_NO_STD_HANDLES, L"stdout duplication failed"); 645 ok = safe_duplicate_handle(GetStdHandle(STD_ERROR_HANDLE), &si.hStdError); 646 if (!ok) 647 error(RC_NO_STD_HANDLES, L"stderr duplication failed"); 648 649 ok = SetConsoleCtrlHandler(ctrl_c_handler, TRUE); 650 if (!ok) 651 error(RC_CREATE_PROCESS, L"control handler setting failed"); 652 653 si.dwFlags = STARTF_USESTDHANDLES; 654 ok = CreateProcessW(NULL, cmdline, NULL, NULL, TRUE, 655 0, NULL, NULL, &si, &pi); 656 if (!ok) 657 error(RC_CREATE_PROCESS, L"Unable to create process using '%ls'", cmdline); 658 AssignProcessToJobObject(job, pi.hProcess); 659 CloseHandle(pi.hThread); 660 WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE); 661 ok = GetExitCodeProcess(pi.hProcess, &rc); 662 if (!ok) 663 error(RC_CREATE_PROCESS, L"Failed to get exit code of process"); 664 debug(L"child process exit code: %d\n", rc); 665 exit(rc); 666 } 667 668 static void 669 invoke_child(wchar_t * executable, wchar_t * suffix, wchar_t * cmdline) 670 { 671 wchar_t * child_command; 672 size_t child_command_size; 673 BOOL no_suffix = (suffix == NULL) || (*suffix == L'\0'); 674 BOOL no_cmdline = (*cmdline == L'\0'); 675 676 if (no_suffix && no_cmdline) 677 run_child(executable); 678 else { 679 if (no_suffix) { 680 /* add 2 for space separator + terminating NUL. */ 681 child_command_size = wcslen(executable) + wcslen(cmdline) + 2; 682 } 683 else { 684 /* add 3 for 2 space separators + terminating NUL. */ 685 child_command_size = wcslen(executable) + wcslen(suffix) + 686 wcslen(cmdline) + 3; 687 } 688 child_command = calloc(child_command_size, sizeof(wchar_t)); 689 if (child_command == NULL) 690 error(RC_CREATE_PROCESS, L"unable to allocate %d bytes for child command.", 691 child_command_size); 692 if (no_suffix) 693 _snwprintf_s(child_command, child_command_size, 694 child_command_size - 1, L"%ls %ls", 695 executable, cmdline); 696 else 697 _snwprintf_s(child_command, child_command_size, 698 child_command_size - 1, L"%ls %ls %ls", 699 executable, suffix, cmdline); 700 run_child(child_command); 701 free(child_command); 702 } 703 } 704 705 typedef struct { 706 wchar_t *shebang; 707 BOOL search; 708 } SHEBANG; 709 710 static SHEBANG builtin_virtual_paths [] = { 711 { L"/usr/bin/env python", TRUE }, 712 { L"/usr/bin/python", FALSE }, 713 { L"/usr/local/bin/python", FALSE }, 714 { L"python", FALSE }, 715 { NULL, FALSE }, 716 }; 717 718 /* For now, a static array of commands. */ 719 720 #define MAX_COMMANDS 100 721 722 typedef struct { 723 wchar_t key[MAX_PATH]; 724 wchar_t value[MSGSIZE]; 725 } COMMAND; 726 727 static COMMAND commands[MAX_COMMANDS]; 728 static int num_commands = 0; 729 730 #if defined(SKIP_PREFIX) 731 732 static wchar_t * builtin_prefixes [] = { 733 /* These must be in an order that the longest matches should be found, 734 * i.e. if the prefix is "/usr/bin/env ", it should match that entry 735 * *before* matching "/usr/bin/". 736 */ 737 L"/usr/bin/env ", 738 L"/usr/bin/", 739 L"/usr/local/bin/", 740 NULL 741 }; 742 743 static wchar_t * skip_prefix(wchar_t * name) 744 { 745 wchar_t ** pp = builtin_prefixes; 746 wchar_t * result = name; 747 wchar_t * p; 748 size_t n; 749 750 for (; p = *pp; pp++) { 751 n = wcslen(p); 752 if (_wcsnicmp(p, name, n) == 0) { 753 result += n; /* skip the prefix */ 754 if (p[n - 1] == L' ') /* No empty strings in table, so n > 1 */ 755 result = skip_whitespace(result); 756 break; 757 } 758 } 759 return result; 760 } 761 762 #endif 763 764 #if defined(SEARCH_PATH) 765 766 static COMMAND path_command; 767 768 static COMMAND * find_on_path(wchar_t * name) 769 { 770 wchar_t * pathext; 771 size_t varsize; 772 wchar_t * context = NULL; 773 wchar_t * extension; 774 COMMAND * result = NULL; 775 DWORD len; 776 errno_t rc; 777 778 wcscpy_s(path_command.key, MAX_PATH, name); 779 if (wcschr(name, L'.') != NULL) { 780 /* assume it has an extension. */ 781 len = SearchPathW(NULL, name, NULL, MSGSIZE, path_command.value, NULL); 782 if (len) { 783 result = &path_command; 784 } 785 } 786 else { 787 /* No extension - search using registered extensions. */ 788 rc = _wdupenv_s(&pathext, &varsize, L"PATHEXT"); 789 if (rc == 0) { 790 extension = wcstok_s(pathext, L";", &context); 791 while (extension) { 792 len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL); 793 if (len) { 794 result = &path_command; 795 break; 796 } 797 extension = wcstok_s(NULL, L";", &context); 798 } 799 free(pathext); 800 } 801 } 802 return result; 803 } 804 805 #endif 806 807 static COMMAND * find_command(wchar_t * name) 808 { 809 COMMAND * result = NULL; 810 COMMAND * cp = commands; 811 int i; 812 813 for (i = 0; i < num_commands; i++, cp++) { 814 if (_wcsicmp(cp->key, name) == 0) { 815 result = cp; 816 break; 817 } 818 } 819 #if defined(SEARCH_PATH) 820 if (result == NULL) 821 result = find_on_path(name); 822 #endif 823 return result; 824 } 825 826 static void 827 update_command(COMMAND * cp, wchar_t * name, wchar_t * cmdline) 828 { 829 wcsncpy_s(cp->key, MAX_PATH, name, _TRUNCATE); 830 wcsncpy_s(cp->value, MSGSIZE, cmdline, _TRUNCATE); 831 } 832 833 static void 834 add_command(wchar_t * name, wchar_t * cmdline) 835 { 836 if (num_commands >= MAX_COMMANDS) { 837 debug(L"can't add %ls = '%ls': no room\n", name, cmdline); 838 } 839 else { 840 COMMAND * cp = &commands[num_commands++]; 841 842 update_command(cp, name, cmdline); 843 } 844 } 845 846 static void 847 read_config_file(wchar_t * config_path) 848 { 849 wchar_t keynames[MSGSIZE]; 850 wchar_t value[MSGSIZE]; 851 DWORD read; 852 wchar_t * key; 853 COMMAND * cp; 854 wchar_t * cmdp; 855 856 read = GetPrivateProfileStringW(L"commands", NULL, NULL, keynames, MSGSIZE, 857 config_path); 858 if (read == MSGSIZE - 1) { 859 debug(L"read_commands: %ls: not enough space for names\n", config_path); 860 } 861 key = keynames; 862 while (*key) { 863 read = GetPrivateProfileStringW(L"commands", key, NULL, value, MSGSIZE, 864 config_path); 865 if (read == MSGSIZE - 1) { 866 debug(L"read_commands: %ls: not enough space for %ls\n", 867 config_path, key); 868 } 869 cmdp = skip_whitespace(value); 870 if (*cmdp) { 871 cp = find_command(key); 872 if (cp == NULL) 873 add_command(key, value); 874 else 875 update_command(cp, key, value); 876 } 877 key += wcslen(key) + 1; 878 } 879 } 880 881 static void read_commands() 882 { 883 if (launcher_ini_path[0]) 884 read_config_file(launcher_ini_path); 885 if (appdata_ini_path[0]) 886 read_config_file(appdata_ini_path); 887 } 888 889 static BOOL 890 parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, 891 wchar_t ** suffix, BOOL *search) 892 { 893 BOOL rc = FALSE; 894 SHEBANG * vpp; 895 size_t plen; 896 wchar_t * p; 897 wchar_t zapped; 898 wchar_t * endp = shebang_line + nchars - 1; 899 COMMAND * cp; 900 wchar_t * skipped; 901 902 *command = NULL; /* failure return */ 903 *suffix = NULL; 904 *search = FALSE; 905 906 if ((*shebang_line++ == L'#') && (*shebang_line++ == L'!')) { 907 shebang_line = skip_whitespace(shebang_line); 908 if (*shebang_line) { 909 *command = shebang_line; 910 for (vpp = builtin_virtual_paths; vpp->shebang; ++vpp) { 911 plen = wcslen(vpp->shebang); 912 if (wcsncmp(shebang_line, vpp->shebang, plen) == 0) { 913 rc = TRUE; 914 *search = vpp->search; 915 /* We can do this because all builtin commands contain 916 * "python". 917 */ 918 *command = wcsstr(shebang_line, L"python"); 919 break; 920 } 921 } 922 if (vpp->shebang == NULL) { 923 /* 924 * Not found in builtins - look in customized commands. 925 * 926 * We can't permanently modify the shebang line in case 927 * it's not a customized command, but we can temporarily 928 * stick a NUL after the command while searching for it, 929 * then put back the char we zapped. 930 */ 931 #if defined(SKIP_PREFIX) 932 skipped = skip_prefix(shebang_line); 933 #else 934 skipped = shebang_line; 935 #endif 936 p = wcspbrk(skipped, L" \t\r\n"); 937 if (p != NULL) { 938 zapped = *p; 939 *p = L'\0'; 940 } 941 cp = find_command(skipped); 942 if (p != NULL) 943 *p = zapped; 944 if (cp != NULL) { 945 *command = cp->value; 946 if (p != NULL) 947 *suffix = skip_whitespace(p); 948 } 949 } 950 /* remove trailing whitespace */ 951 while ((endp > shebang_line) && isspace(*endp)) 952 --endp; 953 if (endp > shebang_line) 954 endp[1] = L'\0'; 955 } 956 } 957 return rc; 958 } 959 960 /* #define CP_UTF8 65001 defined in winnls.h */ 961 #define CP_UTF16LE 1200 962 #define CP_UTF16BE 1201 963 #define CP_UTF32LE 12000 964 #define CP_UTF32BE 12001 965 966 typedef struct { 967 int length; 968 char sequence[4]; 969 UINT code_page; 970 } BOM; 971 972 /* 973 * Strictly, we don't need to handle UTF-16 and UTF-32, since Python itself 974 * doesn't. Never mind, one day it might - there's no harm leaving it in. 975 */ 976 static BOM BOMs[] = { 977 { 3, { 0xEF, 0xBB, 0xBF }, CP_UTF8 }, /* UTF-8 - keep first */ 978 /* Test UTF-32LE before UTF-16LE since UTF-16LE BOM is a prefix 979 * of UTF-32LE BOM. */ 980 { 4, { 0xFF, 0xFE, 0x00, 0x00 }, CP_UTF32LE }, /* UTF-32LE */ 981 { 4, { 0x00, 0x00, 0xFE, 0xFF }, CP_UTF32BE }, /* UTF-32BE */ 982 { 2, { 0xFF, 0xFE }, CP_UTF16LE }, /* UTF-16LE */ 983 { 2, { 0xFE, 0xFF }, CP_UTF16BE }, /* UTF-16BE */ 984 { 0 } /* sentinel */ 985 }; 986 987 static BOM * 988 find_BOM(char * buffer) 989 { 990 /* 991 * Look for a BOM in the input and return a pointer to the 992 * corresponding structure, or NULL if not found. 993 */ 994 BOM * result = NULL; 995 BOM *bom; 996 997 for (bom = BOMs; bom->length; bom++) { 998 if (strncmp(bom->sequence, buffer, bom->length) == 0) { 999 result = bom; 1000 break; 1001 } 1002 } 1003 return result; 1004 } 1005 1006 static char * 1007 find_terminator(char * buffer, int len, BOM *bom) 1008 { 1009 char * result = NULL; 1010 char * end = buffer + len; 1011 char * p; 1012 char c; 1013 int cp; 1014 1015 for (p = buffer; p < end; p++) { 1016 c = *p; 1017 if (c == '\r') { 1018 result = p; 1019 break; 1020 } 1021 if (c == '\n') { 1022 result = p; 1023 break; 1024 } 1025 } 1026 if (result != NULL) { 1027 cp = bom->code_page; 1028 1029 /* adjustments to include all bytes of the char */ 1030 /* no adjustment needed for UTF-8 or big endian */ 1031 if (cp == CP_UTF16LE) 1032 ++result; 1033 else if (cp == CP_UTF32LE) 1034 result += 3; 1035 ++result; /* point just past terminator */ 1036 } 1037 return result; 1038 } 1039 1040 static BOOL 1041 validate_version(wchar_t * p) 1042 { 1043 BOOL result = TRUE; 1044 1045 if (!isdigit(*p)) /* expect major version */ 1046 result = FALSE; 1047 else if (*++p) { /* more to do */ 1048 if (*p != L'.') /* major/minor separator */ 1049 result = FALSE; 1050 else { 1051 ++p; 1052 if (!isdigit(*p)) /* expect minor version */ 1053 result = FALSE; 1054 else { 1055 ++p; 1056 if (*p) { /* more to do */ 1057 if (*p != L'-') 1058 result = FALSE; 1059 else { 1060 ++p; 1061 if ((*p != '3') && (*++p != '2') && !*++p) 1062 result = FALSE; 1063 } 1064 } 1065 } 1066 } 1067 } 1068 return result; 1069 } 1070 1071 typedef struct { 1072 unsigned short min; 1073 unsigned short max; 1074 wchar_t version[MAX_VERSION_SIZE]; 1075 } PYC_MAGIC; 1076 1077 static PYC_MAGIC magic_values[] = { 1078 { 50823, 50823, L"2.0" }, 1079 { 60202, 60202, L"2.1" }, 1080 { 60717, 60717, L"2.2" }, 1081 { 62011, 62021, L"2.3" }, 1082 { 62041, 62061, L"2.4" }, 1083 { 62071, 62131, L"2.5" }, 1084 { 62151, 62161, L"2.6" }, 1085 { 62171, 62211, L"2.7" }, 1086 { 3000, 3131, L"3.0" }, 1087 { 3141, 3151, L"3.1" }, 1088 { 3160, 3180, L"3.2" }, 1089 { 3190, 3230, L"3.3" }, 1090 { 3250, 3310, L"3.4" }, 1091 { 3320, 3351, L"3.5" }, 1092 { 3360, 3379, L"3.6" }, 1093 { 0 } 1094 }; 1095 1096 static INSTALLED_PYTHON * 1097 find_by_magic(unsigned short magic) 1098 { 1099 INSTALLED_PYTHON * result = NULL; 1100 PYC_MAGIC * mp; 1101 1102 for (mp = magic_values; mp->min; mp++) { 1103 if ((magic >= mp->min) && (magic <= mp->max)) { 1104 result = locate_python(mp->version, FALSE); 1105 if (result != NULL) 1106 break; 1107 } 1108 } 1109 return result; 1110 } 1111 1112 static void 1113 maybe_handle_shebang(wchar_t ** argv, wchar_t * cmdline) 1114 { 1115 /* 1116 * Look for a shebang line in the first argument. If found 1117 * and we spawn a child process, this never returns. If it 1118 * does return then we process the args "normally". 1119 * 1120 * argv[0] might be a filename with a shebang. 1121 */ 1122 FILE * fp; 1123 errno_t rc = _wfopen_s(&fp, *argv, L"rb"); 1124 char buffer[BUFSIZE]; 1125 wchar_t shebang_line[BUFSIZE + 1]; 1126 size_t read; 1127 char *p; 1128 char * start; 1129 char * shebang_alias = (char *) shebang_line; 1130 BOM* bom; 1131 int i, j, nchars = 0; 1132 int header_len; 1133 BOOL is_virt; 1134 BOOL search; 1135 wchar_t * command; 1136 wchar_t * suffix; 1137 COMMAND *cmd = NULL; 1138 INSTALLED_PYTHON * ip; 1139 1140 if (rc == 0) { 1141 read = fread(buffer, sizeof(char), BUFSIZE, fp); 1142 debug(L"maybe_handle_shebang: read %d bytes\n", read); 1143 fclose(fp); 1144 1145 if ((read >= 4) && (buffer[3] == '\n') && (buffer[2] == '\r')) { 1146 ip = find_by_magic((((unsigned char)buffer[1]) << 8 | 1147 (unsigned char)buffer[0]) & 0xFFFF); 1148 if (ip != NULL) { 1149 debug(L"script file is compiled against Python %ls\n", 1150 ip->version); 1151 invoke_child(ip->executable, NULL, cmdline); 1152 } 1153 } 1154 /* Look for BOM */ 1155 bom = find_BOM(buffer); 1156 if (bom == NULL) { 1157 start = buffer; 1158 debug(L"maybe_handle_shebang: BOM not found, using UTF-8\n"); 1159 bom = BOMs; /* points to UTF-8 entry - the default */ 1160 } 1161 else { 1162 debug(L"maybe_handle_shebang: BOM found, code page %d\n", 1163 bom->code_page); 1164 start = &buffer[bom->length]; 1165 } 1166 p = find_terminator(start, BUFSIZE, bom); 1167 /* 1168 * If no CR or LF was found in the heading, 1169 * we assume it's not a shebang file. 1170 */ 1171 if (p == NULL) { 1172 debug(L"maybe_handle_shebang: No line terminator found\n"); 1173 } 1174 else { 1175 /* 1176 * Found line terminator - parse the shebang. 1177 * 1178 * Strictly, we don't need to handle UTF-16 anf UTF-32, 1179 * since Python itself doesn't. 1180 * Never mind, one day it might. 1181 */ 1182 header_len = (int) (p - start); 1183 switch(bom->code_page) { 1184 case CP_UTF8: 1185 nchars = MultiByteToWideChar(bom->code_page, 1186 0, 1187 start, header_len, shebang_line, 1188 BUFSIZE); 1189 break; 1190 case CP_UTF16BE: 1191 if (header_len % 2 != 0) { 1192 debug(L"maybe_handle_shebang: UTF-16BE, but an odd number \ 1193 of bytes: %d\n", header_len); 1194 /* nchars = 0; Not needed - initialised to 0. */ 1195 } 1196 else { 1197 for (i = header_len; i > 0; i -= 2) { 1198 shebang_alias[i - 1] = start[i - 2]; 1199 shebang_alias[i - 2] = start[i - 1]; 1200 } 1201 nchars = header_len / sizeof(wchar_t); 1202 } 1203 break; 1204 case CP_UTF16LE: 1205 if ((header_len % 2) != 0) { 1206 debug(L"UTF-16LE, but an odd number of bytes: %d\n", 1207 header_len); 1208 /* nchars = 0; Not needed - initialised to 0. */ 1209 } 1210 else { 1211 /* no actual conversion needed. */ 1212 memcpy(shebang_line, start, header_len); 1213 nchars = header_len / sizeof(wchar_t); 1214 } 1215 break; 1216 case CP_UTF32BE: 1217 if (header_len % 4 != 0) { 1218 debug(L"UTF-32BE, but not divisible by 4: %d\n", 1219 header_len); 1220 /* nchars = 0; Not needed - initialised to 0. */ 1221 } 1222 else { 1223 for (i = header_len, j = header_len / 2; i > 0; i -= 4, 1224 j -= 2) { 1225 shebang_alias[j - 1] = start[i - 2]; 1226 shebang_alias[j - 2] = start[i - 1]; 1227 } 1228 nchars = header_len / sizeof(wchar_t); 1229 } 1230 break; 1231 case CP_UTF32LE: 1232 if (header_len % 4 != 0) { 1233 debug(L"UTF-32LE, but not divisible by 4: %d\n", 1234 header_len); 1235 /* nchars = 0; Not needed - initialised to 0. */ 1236 } 1237 else { 1238 for (i = header_len, j = header_len / 2; i > 0; i -= 4, 1239 j -= 2) { 1240 shebang_alias[j - 1] = start[i - 3]; 1241 shebang_alias[j - 2] = start[i - 4]; 1242 } 1243 nchars = header_len / sizeof(wchar_t); 1244 } 1245 break; 1246 } 1247 if (nchars > 0) { 1248 shebang_line[--nchars] = L'\0'; 1249 is_virt = parse_shebang(shebang_line, nchars, &command, 1250 &suffix, &search); 1251 if (command != NULL) { 1252 debug(L"parse_shebang: found command: %ls\n", command); 1253 if (!is_virt) { 1254 invoke_child(command, suffix, cmdline); 1255 } 1256 else { 1257 suffix = wcschr(command, L' '); 1258 if (suffix != NULL) { 1259 *suffix++ = L'\0'; 1260 suffix = skip_whitespace(suffix); 1261 } 1262 if (wcsncmp(command, L"python", 6)) 1263 error(RC_BAD_VIRTUAL_PATH, L"Unknown virtual \ 1264 path '%ls'", command); 1265 command += 6; /* skip past "python" */ 1266 if (search && ((*command == L'\0') || isspace(*command))) { 1267 /* Command is eligible for path search, and there 1268 * is no version specification. 1269 */ 1270 debug(L"searching PATH for python executable\n"); 1271 cmd = find_on_path(PYTHON_EXECUTABLE); 1272 debug(L"Python on path: %ls\n", cmd ? cmd->value : L"<not found>"); 1273 if (cmd) { 1274 debug(L"located python on PATH: %ls\n", cmd->value); 1275 invoke_child(cmd->value, suffix, cmdline); 1276 /* Exit here, as we have found the command */ 1277 return; 1278 } 1279 /* FALL THROUGH: No python found on PATH, so fall 1280 * back to locating the correct installed python. 1281 */ 1282 } 1283 if (*command && !validate_version(command)) 1284 error(RC_BAD_VIRTUAL_PATH, L"Invalid version \ 1285 specification: '%ls'.\nIn the first line of the script, 'python' needs to be \ 1286 followed by a valid version specifier.\nPlease check the documentation.", 1287 command); 1288 /* TODO could call validate_version(command) */ 1289 ip = locate_python(command, TRUE); 1290 if (ip == NULL) { 1291 error(RC_NO_PYTHON, L"Requested Python version \ 1292 (%ls) is not installed", command); 1293 } 1294 else { 1295 invoke_child(ip->executable, suffix, cmdline); 1296 } 1297 } 1298 } 1299 } 1300 } 1301 } 1302 } 1303 1304 static wchar_t * 1305 skip_me(wchar_t * cmdline) 1306 { 1307 BOOL quoted; 1308 wchar_t c; 1309 wchar_t * result = cmdline; 1310 1311 quoted = cmdline[0] == L'\"'; 1312 if (!quoted) 1313 c = L' '; 1314 else { 1315 c = L'\"'; 1316 ++result; 1317 } 1318 result = wcschr(result, c); 1319 if (result == NULL) /* when, for example, just exe name on command line */ 1320 result = L""; 1321 else { 1322 ++result; /* skip past space or closing quote */ 1323 result = skip_whitespace(result); 1324 } 1325 return result; 1326 } 1327 1328 static DWORD version_high = 0; 1329 static DWORD version_low = 0; 1330 1331 static void 1332 get_version_info(wchar_t * version_text, size_t size) 1333 { 1334 WORD maj, min, rel, bld; 1335 1336 if (!version_high && !version_low) 1337 wcsncpy_s(version_text, size, L"0.1", _TRUNCATE); /* fallback */ 1338 else { 1339 maj = HIWORD(version_high); 1340 min = LOWORD(version_high); 1341 rel = HIWORD(version_low); 1342 bld = LOWORD(version_low); 1343 _snwprintf_s(version_text, size, _TRUNCATE, L"%d.%d.%d.%d", maj, 1344 min, rel, bld); 1345 } 1346 } 1347 1348 static int 1349 process(int argc, wchar_t ** argv) 1350 { 1351 wchar_t * wp; 1352 wchar_t * command; 1353 wchar_t * executable; 1354 wchar_t * p; 1355 int rc = 0; 1356 size_t plen; 1357 INSTALLED_PYTHON * ip; 1358 BOOL valid; 1359 DWORD size, attrs; 1360 HRESULT hr; 1361 wchar_t message[MSGSIZE]; 1362 wchar_t version_text [MAX_PATH]; 1363 void * version_data; 1364 VS_FIXEDFILEINFO * file_info; 1365 UINT block_size; 1366 int index; 1367 #if defined(SCRIPT_WRAPPER) 1368 int newlen; 1369 wchar_t * newcommand; 1370 wchar_t * av[2]; 1371 #endif 1372 1373 setvbuf(stderr, (char *)NULL, _IONBF, 0); 1374 wp = get_env(L"PYLAUNCH_DEBUG"); 1375 if ((wp != NULL) && (*wp != L'\0')) 1376 log_fp = stderr; 1377 1378 #if defined(_M_X64) 1379 debug(L"launcher build: 64bit\n"); 1380 #else 1381 debug(L"launcher build: 32bit\n"); 1382 #endif 1383 #if defined(_WINDOWS) 1384 debug(L"launcher executable: Windows\n"); 1385 #else 1386 debug(L"launcher executable: Console\n"); 1387 #endif 1388 /* Get the local appdata folder (non-roaming) */ 1389 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, 1390 NULL, 0, appdata_ini_path); 1391 if (hr != S_OK) { 1392 debug(L"SHGetFolderPath failed: %X\n", hr); 1393 appdata_ini_path[0] = L'\0'; 1394 } 1395 else { 1396 plen = wcslen(appdata_ini_path); 1397 p = &appdata_ini_path[plen]; 1398 wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE); 1399 attrs = GetFileAttributesW(appdata_ini_path); 1400 if (attrs == INVALID_FILE_ATTRIBUTES) { 1401 debug(L"File '%ls' non-existent\n", appdata_ini_path); 1402 appdata_ini_path[0] = L'\0'; 1403 } else { 1404 debug(L"Using local configuration file '%ls'\n", appdata_ini_path); 1405 } 1406 } 1407 plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH); 1408 size = GetFileVersionInfoSizeW(launcher_ini_path, &size); 1409 if (size == 0) { 1410 winerror(GetLastError(), message, MSGSIZE); 1411 debug(L"GetFileVersionInfoSize failed: %ls\n", message); 1412 } 1413 else { 1414 version_data = malloc(size); 1415 if (version_data) { 1416 valid = GetFileVersionInfoW(launcher_ini_path, 0, size, 1417 version_data); 1418 if (!valid) 1419 debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); 1420 else { 1421 valid = VerQueryValueW(version_data, L"\\", 1422 (LPVOID *) &file_info, &block_size); 1423 if (!valid) 1424 debug(L"VerQueryValue failed: %X\n", GetLastError()); 1425 else { 1426 version_high = file_info->dwFileVersionMS; 1427 version_low = file_info->dwFileVersionLS; 1428 } 1429 } 1430 free(version_data); 1431 } 1432 } 1433 p = wcsrchr(launcher_ini_path, L'\\'); 1434 if (p == NULL) { 1435 debug(L"GetModuleFileNameW returned value has no backslash: %ls\n", 1436 launcher_ini_path); 1437 launcher_ini_path[0] = L'\0'; 1438 } 1439 else { 1440 wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini", 1441 _TRUNCATE); 1442 attrs = GetFileAttributesW(launcher_ini_path); 1443 if (attrs == INVALID_FILE_ATTRIBUTES) { 1444 debug(L"File '%ls' non-existent\n", launcher_ini_path); 1445 launcher_ini_path[0] = L'\0'; 1446 } else { 1447 debug(L"Using global configuration file '%ls'\n", launcher_ini_path); 1448 } 1449 } 1450 1451 command = skip_me(GetCommandLineW()); 1452 debug(L"Called with command line: %ls\n", command); 1453 1454 #if defined(SCRIPT_WRAPPER) 1455 /* The launcher is being used in "script wrapper" mode. 1456 * There should therefore be a Python script named <exename>-script.py in 1457 * the same directory as the launcher executable. 1458 * Put the script name into argv as the first (script name) argument. 1459 */ 1460 1461 /* Get the wrapped script name - if the script is not present, this will 1462 * terminate the program with an error. 1463 */ 1464 locate_wrapped_script(); 1465 1466 /* Add the wrapped script to the start of command */ 1467 newlen = wcslen(wrapped_script_path) + wcslen(command) + 2; /* ' ' + NUL */ 1468 newcommand = malloc(sizeof(wchar_t) * newlen); 1469 if (!newcommand) { 1470 error(RC_NO_MEMORY, L"Could not allocate new command line"); 1471 } 1472 else { 1473 wcscpy_s(newcommand, newlen, wrapped_script_path); 1474 wcscat_s(newcommand, newlen, L" "); 1475 wcscat_s(newcommand, newlen, command); 1476 debug(L"Running wrapped script with command line '%ls'\n", newcommand); 1477 read_commands(); 1478 av[0] = wrapped_script_path; 1479 av[1] = NULL; 1480 maybe_handle_shebang(av, newcommand); 1481 /* Returns if no shebang line - pass to default processing */ 1482 command = newcommand; 1483 valid = FALSE; 1484 } 1485 #else 1486 if (argc <= 1) { 1487 valid = FALSE; 1488 p = NULL; 1489 } 1490 else { 1491 p = argv[1]; 1492 plen = wcslen(p); 1493 valid = (*p == L'-') && validate_version(&p[1]); 1494 if (valid) { 1495 ip = locate_python(&p[1], FALSE); 1496 if (ip == NULL) 1497 error(RC_NO_PYTHON, L"Requested Python version (%ls) not \ 1498 installed", &p[1]); 1499 executable = ip->executable; 1500 command += wcslen(p); 1501 command = skip_whitespace(command); 1502 } 1503 else { 1504 for (index = 1; index < argc; ++index) { 1505 if (*argv[index] != L'-') 1506 break; 1507 } 1508 if (index < argc) { 1509 read_commands(); 1510 maybe_handle_shebang(&argv[index], command); 1511 } 1512 } 1513 } 1514 #endif 1515 1516 if (!valid) { 1517 /* Look for an active virtualenv */ 1518 executable = find_python_by_venv(); 1519 1520 /* If we didn't find one, look for the default Python */ 1521 if (executable == NULL) { 1522 ip = locate_python(L"", FALSE); 1523 if (ip == NULL) 1524 error(RC_NO_PYTHON, L"Can't find a default Python."); 1525 executable = ip->executable; 1526 } 1527 if ((argc == 2) && (!_wcsicmp(p, L"-h") || !_wcsicmp(p, L"--help"))) { 1528 #if defined(_M_X64) 1529 BOOL canDo64bit = TRUE; 1530 #else 1531 // If we are a 32bit process on a 64bit Windows, first hit the 64bit keys. 1532 BOOL canDo64bit = FALSE; 1533 IsWow64Process(GetCurrentProcess(), &canDo64bit); 1534 #endif 1535 1536 get_version_info(version_text, MAX_PATH); 1537 fwprintf(stdout, L"\ 1538 Python Launcher for Windows Version %ls\n\n", version_text); 1539 fwprintf(stdout, L"\ 1540 usage: %ls [ launcher-arguments ] [ python-arguments ] script [ script-arguments ]\n\n", argv[0]); 1541 fputws(L"\ 1542 Launcher arguments:\n\n\ 1543 -2 : Launch the latest Python 2.x version\n\ 1544 -3 : Launch the latest Python 3.x version\n\ 1545 -X.Y : Launch the specified Python version\n", stdout); 1546 if (canDo64bit) { 1547 fputws(L"\ 1548 -X.Y-32: Launch the specified 32bit Python version", stdout); 1549 } 1550 fputws(L"\n\nThe following help text is from Python:\n\n", stdout); 1551 fflush(stdout); 1552 } 1553 } 1554 invoke_child(executable, NULL, command); 1555 return rc; 1556 } 1557 1558 #if defined(_WINDOWS) 1559 1560 int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 1561 LPWSTR lpstrCmd, int nShow) 1562 { 1563 return process(__argc, __wargv); 1564 } 1565 1566 #else 1567 1568 int cdecl wmain(int argc, wchar_t ** argv) 1569 { 1570 return process(argc, argv); 1571 } 1572 1573 #endif 1574