blob: 455f42b617c01cafac70058ec98efaf6b4f31625 [file] [log] [blame]
Brian Curtin07165f72012-06-20 15:36:14 -05001/*
Vinay Sajip22c039b2013-06-07 15:37:28 +01002 * Copyright (C) 2011-2013 Vinay Sajip.
Martin v. Löwis6a8ca3e2012-06-21 19:29:37 +02003 * Licensed to PSF under a contributor agreement.
Brian Curtin07165f72012-06-20 15:36:14 -05004 *
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
Vinay Sajip22c039b2013-06-07 15:37:28 +010021#define SEARCH_PATH
Brian Curtin07165f72012-06-20 15:36:14 -050022
Vinay Sajipc985d082013-07-25 11:20:55 +010023/* 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
Brian Curtin07165f72012-06-20 15:36:14 -050044/* Just for now - static definition */
45
46static FILE * log_fp = NULL;
47
48static wchar_t *
49skip_whitespace(wchar_t * p)
50{
51 while (*p && isspace(*p))
52 ++p;
53 return p;
54}
55
Brian Curtin07165f72012-06-20 15:36:14 -050056static void
57debug(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);
Zackery Spytz3a6d7522018-06-15 16:39:04 -060064 va_end(va);
Brian Curtin07165f72012-06-20 15:36:14 -050065 }
66}
67
68static void
69winerror(int rc, wchar_t * message, int size)
70{
71 FormatMessageW(
72 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
73 NULL, rc, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
74 message, size, NULL);
75}
76
77static void
78error(int rc, wchar_t * format, ... )
79{
80 va_list va;
81 wchar_t message[MSGSIZE];
82 wchar_t win_message[MSGSIZE];
83 int len;
84
85 va_start(va, format);
86 len = _vsnwprintf_s(message, MSGSIZE, _TRUNCATE, format, va);
Zackery Spytz3a6d7522018-06-15 16:39:04 -060087 va_end(va);
Brian Curtin07165f72012-06-20 15:36:14 -050088
89 if (rc == 0) { /* a Windows error */
90 winerror(GetLastError(), win_message, MSGSIZE);
91 if (len >= 0) {
Steve Dower84bcfb32015-01-02 18:07:46 -080092 _snwprintf_s(&message[len], MSGSIZE - len, _TRUNCATE, L": %ls",
Brian Curtin07165f72012-06-20 15:36:14 -050093 win_message);
94 }
95 }
96
97#if !defined(_WINDOWS)
Steve Dower84bcfb32015-01-02 18:07:46 -080098 fwprintf(stderr, L"%ls\n", message);
Brian Curtin07165f72012-06-20 15:36:14 -050099#else
Vinay Sajipc985d082013-07-25 11:20:55 +0100100 MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."),
Serhiy Storchaka009b8112015-03-18 21:53:15 +0200101 MB_OK);
Brian Curtin07165f72012-06-20 15:36:14 -0500102#endif
Vinay Sajipaab9f462015-12-26 12:35:47 +0000103 exit(rc);
Brian Curtin07165f72012-06-20 15:36:14 -0500104}
105
Vinay Sajipc985d082013-07-25 11:20:55 +0100106/*
107 * This function is here to simplify memory management
108 * and to treat blank values as if they are absent.
109 */
110static wchar_t * get_env(wchar_t * key)
111{
112 /* This is not thread-safe, just like getenv */
113 static wchar_t buf[BUFSIZE];
114 DWORD result = GetEnvironmentVariableW(key, buf, BUFSIZE);
115
116 if (result >= BUFSIZE) {
117 /* Large environment variable. Accept some leakage */
118 wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1));
Vinay Sajipabeb6472015-12-13 09:41:29 +0000119 if (buf2 == NULL) {
Vinay Sajipc985d082013-07-25 11:20:55 +0100120 error(RC_NO_MEMORY, L"Could not allocate environment buffer");
121 }
122 GetEnvironmentVariableW(key, buf2, result);
123 return buf2;
124 }
125
126 if (result == 0)
127 /* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND,
128 or an empty environment variable. */
129 return NULL;
130
131 return buf;
132}
133
Brian Curtin07165f72012-06-20 15:36:14 -0500134#if defined(_WINDOWS)
135
136#define PYTHON_EXECUTABLE L"pythonw.exe"
137
138#else
139
140#define PYTHON_EXECUTABLE L"python.exe"
141
142#endif
143
Brian Curtin07165f72012-06-20 15:36:14 -0500144#define MAX_VERSION_SIZE 4
145
146typedef struct {
147 wchar_t version[MAX_VERSION_SIZE]; /* m.n */
148 int bits; /* 32 or 64 */
149 wchar_t executable[MAX_PATH];
150} INSTALLED_PYTHON;
151
152/*
153 * To avoid messing about with heap allocations, just assume we can allocate
154 * statically and never have to deal with more versions than this.
155 */
156#define MAX_INSTALLED_PYTHONS 100
157
158static INSTALLED_PYTHON installed_pythons[MAX_INSTALLED_PYTHONS];
159
160static size_t num_installed_pythons = 0;
161
Steve Dowerbb240872015-02-05 22:08:48 -0800162/*
163 * To hold SOFTWARE\Python\PythonCore\X.Y...\InstallPath
164 * The version name can be longer than MAX_VERSION_SIZE, but will be
165 * truncated to just X.Y for comparisons.
166 */
Brian Curtin07165f72012-06-20 15:36:14 -0500167#define IP_BASE_SIZE 40
Steve Dowerbb240872015-02-05 22:08:48 -0800168#define IP_VERSION_SIZE 8
169#define IP_SIZE (IP_BASE_SIZE + IP_VERSION_SIZE)
Brian Curtin07165f72012-06-20 15:36:14 -0500170#define CORE_PATH L"SOFTWARE\\Python\\PythonCore"
171
172static wchar_t * location_checks[] = {
173 L"\\",
Stefan Grönkef1502d02017-09-25 18:58:10 +0200174 L"\\PCbuild\\win32\\",
175 L"\\PCbuild\\amd64\\",
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100176 /* To support early 32bit versions of Python that stuck the build binaries
Stefan Grönkef1502d02017-09-25 18:58:10 +0200177 * directly in PCbuild... */
178 L"\\PCbuild\\",
Brian Curtin07165f72012-06-20 15:36:14 -0500179 NULL
180};
181
182static INSTALLED_PYTHON *
183find_existing_python(wchar_t * path)
184{
185 INSTALLED_PYTHON * result = NULL;
186 size_t i;
187 INSTALLED_PYTHON * ip;
188
189 for (i = 0, ip = installed_pythons; i < num_installed_pythons; i++, ip++) {
190 if (_wcsicmp(path, ip->executable) == 0) {
191 result = ip;
192 break;
193 }
194 }
195 return result;
196}
197
198static void
199locate_pythons_for_key(HKEY root, REGSAM flags)
200{
201 HKEY core_root, ip_key;
202 LSTATUS status = RegOpenKeyExW(root, CORE_PATH, 0, flags, &core_root);
203 wchar_t message[MSGSIZE];
204 DWORD i;
205 size_t n;
206 BOOL ok;
207 DWORD type, data_size, attrs;
208 INSTALLED_PYTHON * ip, * pip;
Steve Dowerbb240872015-02-05 22:08:48 -0800209 wchar_t ip_version[IP_VERSION_SIZE];
Brian Curtin07165f72012-06-20 15:36:14 -0500210 wchar_t ip_path[IP_SIZE];
211 wchar_t * check;
212 wchar_t ** checkp;
213 wchar_t *key_name = (root == HKEY_LOCAL_MACHINE) ? L"HKLM" : L"HKCU";
214
215 if (status != ERROR_SUCCESS)
Steve Dower84bcfb32015-01-02 18:07:46 -0800216 debug(L"locate_pythons_for_key: unable to open PythonCore key in %ls\n",
Brian Curtin07165f72012-06-20 15:36:14 -0500217 key_name);
218 else {
219 ip = &installed_pythons[num_installed_pythons];
220 for (i = 0; num_installed_pythons < MAX_INSTALLED_PYTHONS; i++) {
Steve Dowerbb240872015-02-05 22:08:48 -0800221 status = RegEnumKeyW(core_root, i, ip_version, IP_VERSION_SIZE);
Brian Curtin07165f72012-06-20 15:36:14 -0500222 if (status != ERROR_SUCCESS) {
223 if (status != ERROR_NO_MORE_ITEMS) {
224 /* unexpected error */
225 winerror(status, message, MSGSIZE);
Steve Dower84bcfb32015-01-02 18:07:46 -0800226 debug(L"Can't enumerate registry key for version %ls: %ls\n",
Steve Dowerbb240872015-02-05 22:08:48 -0800227 ip_version, message);
Brian Curtin07165f72012-06-20 15:36:14 -0500228 }
229 break;
230 }
231 else {
Steve Dowerbb240872015-02-05 22:08:48 -0800232 wcsncpy_s(ip->version, MAX_VERSION_SIZE, ip_version,
233 MAX_VERSION_SIZE-1);
Brian Curtin07165f72012-06-20 15:36:14 -0500234 _snwprintf_s(ip_path, IP_SIZE, _TRUNCATE,
Steve Dowerbb240872015-02-05 22:08:48 -0800235 L"%ls\\%ls\\InstallPath", CORE_PATH, ip_version);
Brian Curtin07165f72012-06-20 15:36:14 -0500236 status = RegOpenKeyExW(root, ip_path, 0, flags, &ip_key);
237 if (status != ERROR_SUCCESS) {
238 winerror(status, message, MSGSIZE);
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100239 /* Note: 'message' already has a trailing \n*/
Steve Dower84bcfb32015-01-02 18:07:46 -0800240 debug(L"%ls\\%ls: %ls", key_name, ip_path, message);
Brian Curtin07165f72012-06-20 15:36:14 -0500241 continue;
242 }
243 data_size = sizeof(ip->executable) - 1;
Martin v. Löwisaf21ebb2012-06-21 18:15:54 +0200244 status = RegQueryValueExW(ip_key, NULL, NULL, &type,
245 (LPBYTE)ip->executable, &data_size);
Brian Curtin07165f72012-06-20 15:36:14 -0500246 RegCloseKey(ip_key);
247 if (status != ERROR_SUCCESS) {
248 winerror(status, message, MSGSIZE);
Steve Dower84bcfb32015-01-02 18:07:46 -0800249 debug(L"%ls\\%ls: %ls\n", key_name, ip_path, message);
Brian Curtin07165f72012-06-20 15:36:14 -0500250 continue;
251 }
252 if (type == REG_SZ) {
253 data_size = data_size / sizeof(wchar_t) - 1; /* for NUL */
254 if (ip->executable[data_size - 1] == L'\\')
255 --data_size; /* reg value ended in a backslash */
256 /* ip->executable is data_size long */
257 for (checkp = location_checks; *checkp; ++checkp) {
258 check = *checkp;
259 _snwprintf_s(&ip->executable[data_size],
260 MAX_PATH - data_size,
261 MAX_PATH - data_size,
Steve Dower84bcfb32015-01-02 18:07:46 -0800262 L"%ls%ls", check, PYTHON_EXECUTABLE);
Brian Curtin07165f72012-06-20 15:36:14 -0500263 attrs = GetFileAttributesW(ip->executable);
264 if (attrs == INVALID_FILE_ATTRIBUTES) {
265 winerror(GetLastError(), message, MSGSIZE);
Steve Dower84bcfb32015-01-02 18:07:46 -0800266 debug(L"locate_pythons_for_key: %ls: %ls",
Brian Curtin07165f72012-06-20 15:36:14 -0500267 ip->executable, message);
268 }
269 else if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800270 debug(L"locate_pythons_for_key: '%ls' is a \
Brian Curtin07165f72012-06-20 15:36:14 -0500271directory\n",
272 ip->executable, attrs);
273 }
274 else if (find_existing_python(ip->executable)) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800275 debug(L"locate_pythons_for_key: %ls: already \
Steve Dower13be8c22015-03-10 19:38:25 -0700276found\n", ip->executable);
Brian Curtin07165f72012-06-20 15:36:14 -0500277 }
278 else {
279 /* check the executable type. */
280 ok = GetBinaryTypeW(ip->executable, &attrs);
281 if (!ok) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800282 debug(L"Failure getting binary type: %ls\n",
Brian Curtin07165f72012-06-20 15:36:14 -0500283 ip->executable);
284 }
285 else {
286 if (attrs == SCS_64BIT_BINARY)
287 ip->bits = 64;
288 else if (attrs == SCS_32BIT_BINARY)
289 ip->bits = 32;
290 else
291 ip->bits = 0;
292 if (ip->bits == 0) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800293 debug(L"locate_pythons_for_key: %ls: \
Brian Curtin07165f72012-06-20 15:36:14 -0500294invalid binary type: %X\n",
295 ip->executable, attrs);
296 }
297 else {
298 if (wcschr(ip->executable, L' ') != NULL) {
299 /* has spaces, so quote */
300 n = wcslen(ip->executable);
301 memmove(&ip->executable[1],
302 ip->executable, n * sizeof(wchar_t));
303 ip->executable[0] = L'\"';
304 ip->executable[n + 1] = L'\"';
305 ip->executable[n + 2] = L'\0';
306 }
Steve Dower84bcfb32015-01-02 18:07:46 -0800307 debug(L"locate_pythons_for_key: %ls \
Brian Curtin07165f72012-06-20 15:36:14 -0500308is a %dbit executable\n",
309 ip->executable, ip->bits);
310 ++num_installed_pythons;
311 pip = ip++;
312 if (num_installed_pythons >=
313 MAX_INSTALLED_PYTHONS)
314 break;
315 /* Copy over the attributes for the next */
316 *ip = *pip;
317 }
318 }
319 }
320 }
321 }
322 }
323 }
324 RegCloseKey(core_root);
325 }
326}
327
328static int
329compare_pythons(const void * p1, const void * p2)
330{
331 INSTALLED_PYTHON * ip1 = (INSTALLED_PYTHON *) p1;
332 INSTALLED_PYTHON * ip2 = (INSTALLED_PYTHON *) p2;
333 /* note reverse sorting on version */
334 int result = wcscmp(ip2->version, ip1->version);
335
336 if (result == 0)
337 result = ip2->bits - ip1->bits; /* 64 before 32 */
338 return result;
339}
340
341static void
342locate_all_pythons()
343{
344#if defined(_M_X64)
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100345 /* If we are a 64bit process, first hit the 32bit keys. */
Brian Curtin07165f72012-06-20 15:36:14 -0500346 debug(L"locating Pythons in 32bit registry\n");
347 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_32KEY);
348 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_32KEY);
349#else
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100350 /* If we are a 32bit process on a 64bit Windows, first hit the 64bit keys.*/
Brian Curtin07165f72012-06-20 15:36:14 -0500351 BOOL f64 = FALSE;
352 if (IsWow64Process(GetCurrentProcess(), &f64) && f64) {
353 debug(L"locating Pythons in 64bit registry\n");
354 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ | KEY_WOW64_64KEY);
355 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ | KEY_WOW64_64KEY);
356 }
Serhiy Storchaka009b8112015-03-18 21:53:15 +0200357#endif
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100358 /* now hit the "native" key for this process bittedness. */
Brian Curtin07165f72012-06-20 15:36:14 -0500359 debug(L"locating Pythons in native registry\n");
360 locate_pythons_for_key(HKEY_CURRENT_USER, KEY_READ);
361 locate_pythons_for_key(HKEY_LOCAL_MACHINE, KEY_READ);
362 qsort(installed_pythons, num_installed_pythons, sizeof(INSTALLED_PYTHON),
363 compare_pythons);
364}
365
366static INSTALLED_PYTHON *
367find_python_by_version(wchar_t const * wanted_ver)
368{
369 INSTALLED_PYTHON * result = NULL;
370 INSTALLED_PYTHON * ip = installed_pythons;
371 size_t i, n;
372 size_t wlen = wcslen(wanted_ver);
373 int bits = 0;
374
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100375 if (wcsstr(wanted_ver, L"-32")) {
Brian Curtin07165f72012-06-20 15:36:14 -0500376 bits = 32;
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100377 wlen -= wcslen(L"-32");
378 }
379 else if (wcsstr(wanted_ver, L"-64")) { /* Added option to select 64 bit explicitly */
380 bits = 64;
381 wlen -= wcslen(L"-64");
382 }
Brian Curtin07165f72012-06-20 15:36:14 -0500383 for (i = 0; i < num_installed_pythons; i++, ip++) {
384 n = wcslen(ip->version);
385 if (n > wlen)
386 n = wlen;
387 if ((wcsncmp(ip->version, wanted_ver, n) == 0) &&
388 /* bits == 0 => don't care */
389 ((bits == 0) || (ip->bits == bits))) {
390 result = ip;
391 break;
392 }
393 }
394 return result;
395}
396
397
Steve Dower76998fe2015-02-26 14:25:33 -0800398static wchar_t *
399find_python_by_venv()
400{
401 static wchar_t venv_python[MAX_PATH];
402 wchar_t *virtual_env = get_env(L"VIRTUAL_ENV");
403 DWORD attrs;
404
405 /* Check for VIRTUAL_ENV environment variable */
406 if (virtual_env == NULL || virtual_env[0] == L'\0') {
407 return NULL;
408 }
409
410 /* Check for a python executable in the venv */
411 debug(L"Checking for Python executable in virtual env '%ls'\n", virtual_env);
412 _snwprintf_s(venv_python, MAX_PATH, _TRUNCATE,
413 L"%ls\\Scripts\\%ls", virtual_env, PYTHON_EXECUTABLE);
414 attrs = GetFileAttributesW(venv_python);
415 if (attrs == INVALID_FILE_ATTRIBUTES) {
416 debug(L"Python executable %ls missing from virtual env\n", venv_python);
417 return NULL;
418 }
419
420 return venv_python;
421}
422
Brian Curtin07165f72012-06-20 15:36:14 -0500423static wchar_t appdata_ini_path[MAX_PATH];
424static wchar_t launcher_ini_path[MAX_PATH];
425
426/*
427 * Get a value either from the environment or a configuration file.
428 * The key passed in will either be "python", "python2" or "python3".
429 */
430static wchar_t *
431get_configured_value(wchar_t * key)
432{
433/*
434 * Note: this static value is used to return a configured value
435 * obtained either from the environment or configuration file.
436 * This should be OK since there wouldn't be any concurrent calls.
437 */
438 static wchar_t configured_value[MSGSIZE];
439 wchar_t * result = NULL;
440 wchar_t * found_in = L"environment";
441 DWORD size;
442
443 /* First, search the environment. */
Steve Dower84bcfb32015-01-02 18:07:46 -0800444 _snwprintf_s(configured_value, MSGSIZE, _TRUNCATE, L"py_%ls", key);
Brian Curtin07165f72012-06-20 15:36:14 -0500445 result = get_env(configured_value);
446 if (result == NULL && appdata_ini_path[0]) {
447 /* Not in environment: check local configuration. */
448 size = GetPrivateProfileStringW(L"defaults", key, NULL,
449 configured_value, MSGSIZE,
450 appdata_ini_path);
451 if (size > 0) {
452 result = configured_value;
453 found_in = appdata_ini_path;
454 }
455 }
456 if (result == NULL && launcher_ini_path[0]) {
457 /* Not in environment or local: check global configuration. */
458 size = GetPrivateProfileStringW(L"defaults", key, NULL,
459 configured_value, MSGSIZE,
460 launcher_ini_path);
461 if (size > 0) {
462 result = configured_value;
463 found_in = launcher_ini_path;
464 }
465 }
466 if (result) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800467 debug(L"found configured value '%ls=%ls' in %ls\n",
Brian Curtin07165f72012-06-20 15:36:14 -0500468 key, result, found_in ? found_in : L"(unknown)");
469 } else {
Steve Dower84bcfb32015-01-02 18:07:46 -0800470 debug(L"found no configured value for '%ls'\n", key);
Brian Curtin07165f72012-06-20 15:36:14 -0500471 }
472 return result;
473}
474
475static INSTALLED_PYTHON *
Paul Moore835416c2016-05-22 12:28:41 +0100476locate_python(wchar_t * wanted_ver, BOOL from_shebang)
Brian Curtin07165f72012-06-20 15:36:14 -0500477{
478 static wchar_t config_key [] = { L"pythonX" };
479 static wchar_t * last_char = &config_key[sizeof(config_key) /
480 sizeof(wchar_t) - 2];
481 INSTALLED_PYTHON * result = NULL;
482 size_t n = wcslen(wanted_ver);
483 wchar_t * configured_value;
484
485 if (num_installed_pythons == 0)
486 locate_all_pythons();
487
488 if (n == 1) { /* just major version specified */
489 *last_char = *wanted_ver;
490 configured_value = get_configured_value(config_key);
491 if (configured_value != NULL)
492 wanted_ver = configured_value;
493 }
494 if (*wanted_ver) {
495 result = find_python_by_version(wanted_ver);
Steve Dower84bcfb32015-01-02 18:07:46 -0800496 debug(L"search for Python version '%ls' found ", wanted_ver);
Brian Curtin07165f72012-06-20 15:36:14 -0500497 if (result) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800498 debug(L"'%ls'\n", result->executable);
Brian Curtin07165f72012-06-20 15:36:14 -0500499 } else {
500 debug(L"no interpreter\n");
501 }
502 }
503 else {
504 *last_char = L'\0'; /* look for an overall default */
505 configured_value = get_configured_value(config_key);
506 if (configured_value)
507 result = find_python_by_version(configured_value);
Paul Moore835416c2016-05-22 12:28:41 +0100508 /* Not found a value yet - try by major version.
509 * If we're looking for an interpreter specified in a shebang line,
510 * we want to try Python 2 first, then Python 3 (for Unix and backward
511 * compatibility). If we're being called interactively, assume the user
512 * wants the latest version available, so try Python 3 first, then
513 * Python 2.
514 */
Brian Curtin07165f72012-06-20 15:36:14 -0500515 if (result == NULL)
Paul Moore835416c2016-05-22 12:28:41 +0100516 result = find_python_by_version(from_shebang ? L"2" : L"3");
Brian Curtin07165f72012-06-20 15:36:14 -0500517 if (result == NULL)
Paul Moore835416c2016-05-22 12:28:41 +0100518 result = find_python_by_version(from_shebang ? L"3" : L"2");
Brian Curtin07165f72012-06-20 15:36:14 -0500519 debug(L"search for default Python found ");
520 if (result) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800521 debug(L"version %ls at '%ls'\n",
Brian Curtin07165f72012-06-20 15:36:14 -0500522 result->version, result->executable);
523 } else {
524 debug(L"no interpreter\n");
525 }
526 }
527 return result;
528}
529
Vinay Sajipc985d082013-07-25 11:20:55 +0100530#if defined(SCRIPT_WRAPPER)
531/*
532 * Check for a script located alongside the executable
533 */
534
535#if defined(_WINDOWS)
536#define SCRIPT_SUFFIX L"-script.pyw"
537#else
538#define SCRIPT_SUFFIX L"-script.py"
539#endif
540
541static wchar_t wrapped_script_path[MAX_PATH];
542
543/* Locate the script being wrapped.
544 *
545 * This code should store the name of the wrapped script in
546 * wrapped_script_path, or terminate the program with an error if there is no
547 * valid wrapped script file.
548 */
549static void
550locate_wrapped_script()
551{
552 wchar_t * p;
553 size_t plen;
554 DWORD attrs;
555
556 plen = GetModuleFileNameW(NULL, wrapped_script_path, MAX_PATH);
557 p = wcsrchr(wrapped_script_path, L'.');
558 if (p == NULL) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800559 debug(L"GetModuleFileNameW returned value has no extension: %ls\n",
Vinay Sajipc985d082013-07-25 11:20:55 +0100560 wrapped_script_path);
Steve Dower84bcfb32015-01-02 18:07:46 -0800561 error(RC_NO_SCRIPT, L"Wrapper name '%ls' is not valid.", wrapped_script_path);
Vinay Sajipc985d082013-07-25 11:20:55 +0100562 }
563
564 wcsncpy_s(p, MAX_PATH - (p - wrapped_script_path) + 1, SCRIPT_SUFFIX, _TRUNCATE);
565 attrs = GetFileAttributesW(wrapped_script_path);
566 if (attrs == INVALID_FILE_ATTRIBUTES) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800567 debug(L"File '%ls' non-existent\n", wrapped_script_path);
568 error(RC_NO_SCRIPT, L"Script file '%ls' is not present.", wrapped_script_path);
Vinay Sajipc985d082013-07-25 11:20:55 +0100569 }
570
Steve Dower84bcfb32015-01-02 18:07:46 -0800571 debug(L"Using wrapped script file '%ls'\n", wrapped_script_path);
Vinay Sajipc985d082013-07-25 11:20:55 +0100572}
573#endif
574
Brian Curtin07165f72012-06-20 15:36:14 -0500575/*
576 * Process creation code
577 */
578
579static BOOL
580safe_duplicate_handle(HANDLE in, HANDLE * pout)
581{
582 BOOL ok;
583 HANDLE process = GetCurrentProcess();
584 DWORD rc;
585
586 *pout = NULL;
587 ok = DuplicateHandle(process, in, process, pout, 0, TRUE,
588 DUPLICATE_SAME_ACCESS);
589 if (!ok) {
590 rc = GetLastError();
591 if (rc == ERROR_INVALID_HANDLE) {
592 debug(L"DuplicateHandle returned ERROR_INVALID_HANDLE\n");
593 ok = TRUE;
594 }
595 else {
596 debug(L"DuplicateHandle returned %d\n", rc);
597 }
598 }
599 return ok;
600}
601
602static BOOL WINAPI
603ctrl_c_handler(DWORD code)
604{
605 return TRUE; /* We just ignore all control events. */
606}
607
608static void
609run_child(wchar_t * cmdline)
610{
611 HANDLE job;
612 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
613 DWORD rc;
614 BOOL ok;
615 STARTUPINFOW si;
616 PROCESS_INFORMATION pi;
617
Vinay Sajip66fef9f2013-02-26 16:29:06 +0000618#if defined(_WINDOWS)
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +0100619 /*
620 When explorer launches a Windows (GUI) application, it displays
621 the "app starting" (the "pointer + hourglass") cursor for a number
622 of seconds, or until the app does something UI-ish (eg, creating a
623 window, or fetching a message). As this launcher doesn't do this
624 directly, that cursor remains even after the child process does these
625 things. We avoid that by doing a simple post+get message.
626 See http://bugs.python.org/issue17290 and
627 https://bitbucket.org/vinay.sajip/pylauncher/issue/20/busy-cursor-for-a-long-time-when-running
628 */
Vinay Sajip66fef9f2013-02-26 16:29:06 +0000629 MSG msg;
630
631 PostMessage(0, 0, 0, 0);
632 GetMessage(&msg, 0, 0, 0);
633#endif
634
Steve Dower84bcfb32015-01-02 18:07:46 -0800635 debug(L"run_child: about to run '%ls'\n", cmdline);
Brian Curtin07165f72012-06-20 15:36:14 -0500636 job = CreateJobObject(NULL, NULL);
637 ok = QueryInformationJobObject(job, JobObjectExtendedLimitInformation,
638 &info, sizeof(info), &rc);
639 if (!ok || (rc != sizeof(info)) || !job)
640 error(RC_CREATE_PROCESS, L"Job information querying failed");
641 info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
642 JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
643 ok = SetInformationJobObject(job, JobObjectExtendedLimitInformation, &info,
644 sizeof(info));
645 if (!ok)
646 error(RC_CREATE_PROCESS, L"Job information setting failed");
647 memset(&si, 0, sizeof(si));
648 si.cb = sizeof(si);
649 ok = safe_duplicate_handle(GetStdHandle(STD_INPUT_HANDLE), &si.hStdInput);
650 if (!ok)
651 error(RC_NO_STD_HANDLES, L"stdin duplication failed");
652 ok = safe_duplicate_handle(GetStdHandle(STD_OUTPUT_HANDLE), &si.hStdOutput);
653 if (!ok)
654 error(RC_NO_STD_HANDLES, L"stdout duplication failed");
655 ok = safe_duplicate_handle(GetStdHandle(STD_ERROR_HANDLE), &si.hStdError);
656 if (!ok)
657 error(RC_NO_STD_HANDLES, L"stderr duplication failed");
658
659 ok = SetConsoleCtrlHandler(ctrl_c_handler, TRUE);
660 if (!ok)
661 error(RC_CREATE_PROCESS, L"control handler setting failed");
662
663 si.dwFlags = STARTF_USESTDHANDLES;
664 ok = CreateProcessW(NULL, cmdline, NULL, NULL, TRUE,
665 0, NULL, NULL, &si, &pi);
666 if (!ok)
Steve Dower84bcfb32015-01-02 18:07:46 -0800667 error(RC_CREATE_PROCESS, L"Unable to create process using '%ls'", cmdline);
Brian Curtin07165f72012-06-20 15:36:14 -0500668 AssignProcessToJobObject(job, pi.hProcess);
669 CloseHandle(pi.hThread);
Martin v. Löwisb26a9b12013-01-25 14:25:48 +0100670 WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE);
Brian Curtin07165f72012-06-20 15:36:14 -0500671 ok = GetExitCodeProcess(pi.hProcess, &rc);
672 if (!ok)
673 error(RC_CREATE_PROCESS, L"Failed to get exit code of process");
674 debug(L"child process exit code: %d\n", rc);
Vinay Sajipaab9f462015-12-26 12:35:47 +0000675 exit(rc);
Brian Curtin07165f72012-06-20 15:36:14 -0500676}
677
678static void
679invoke_child(wchar_t * executable, wchar_t * suffix, wchar_t * cmdline)
680{
681 wchar_t * child_command;
682 size_t child_command_size;
683 BOOL no_suffix = (suffix == NULL) || (*suffix == L'\0');
684 BOOL no_cmdline = (*cmdline == L'\0');
685
686 if (no_suffix && no_cmdline)
687 run_child(executable);
688 else {
689 if (no_suffix) {
690 /* add 2 for space separator + terminating NUL. */
691 child_command_size = wcslen(executable) + wcslen(cmdline) + 2;
692 }
693 else {
694 /* add 3 for 2 space separators + terminating NUL. */
695 child_command_size = wcslen(executable) + wcslen(suffix) +
696 wcslen(cmdline) + 3;
697 }
698 child_command = calloc(child_command_size, sizeof(wchar_t));
699 if (child_command == NULL)
700 error(RC_CREATE_PROCESS, L"unable to allocate %d bytes for child command.",
701 child_command_size);
702 if (no_suffix)
703 _snwprintf_s(child_command, child_command_size,
Steve Dower84bcfb32015-01-02 18:07:46 -0800704 child_command_size - 1, L"%ls %ls",
Brian Curtin07165f72012-06-20 15:36:14 -0500705 executable, cmdline);
706 else
707 _snwprintf_s(child_command, child_command_size,
Steve Dower84bcfb32015-01-02 18:07:46 -0800708 child_command_size - 1, L"%ls %ls %ls",
Brian Curtin07165f72012-06-20 15:36:14 -0500709 executable, suffix, cmdline);
710 run_child(child_command);
711 free(child_command);
712 }
713}
714
Vinay Sajip22c039b2013-06-07 15:37:28 +0100715typedef struct {
716 wchar_t *shebang;
717 BOOL search;
718} SHEBANG;
719
720static SHEBANG builtin_virtual_paths [] = {
721 { L"/usr/bin/env python", TRUE },
722 { L"/usr/bin/python", FALSE },
723 { L"/usr/local/bin/python", FALSE },
724 { L"python", FALSE },
725 { NULL, FALSE },
Brian Curtin07165f72012-06-20 15:36:14 -0500726};
727
728/* For now, a static array of commands. */
729
730#define MAX_COMMANDS 100
731
732typedef struct {
733 wchar_t key[MAX_PATH];
734 wchar_t value[MSGSIZE];
735} COMMAND;
736
737static COMMAND commands[MAX_COMMANDS];
738static int num_commands = 0;
739
740#if defined(SKIP_PREFIX)
741
742static wchar_t * builtin_prefixes [] = {
743 /* These must be in an order that the longest matches should be found,
744 * i.e. if the prefix is "/usr/bin/env ", it should match that entry
745 * *before* matching "/usr/bin/".
746 */
747 L"/usr/bin/env ",
748 L"/usr/bin/",
749 L"/usr/local/bin/",
750 NULL
751};
752
753static wchar_t * skip_prefix(wchar_t * name)
754{
755 wchar_t ** pp = builtin_prefixes;
756 wchar_t * result = name;
757 wchar_t * p;
758 size_t n;
759
760 for (; p = *pp; pp++) {
761 n = wcslen(p);
762 if (_wcsnicmp(p, name, n) == 0) {
763 result += n; /* skip the prefix */
764 if (p[n - 1] == L' ') /* No empty strings in table, so n > 1 */
765 result = skip_whitespace(result);
766 break;
767 }
768 }
769 return result;
770}
771
772#endif
773
774#if defined(SEARCH_PATH)
775
776static COMMAND path_command;
777
778static COMMAND * find_on_path(wchar_t * name)
779{
780 wchar_t * pathext;
781 size_t varsize;
782 wchar_t * context = NULL;
783 wchar_t * extension;
784 COMMAND * result = NULL;
785 DWORD len;
786 errno_t rc;
787
788 wcscpy_s(path_command.key, MAX_PATH, name);
789 if (wcschr(name, L'.') != NULL) {
790 /* assume it has an extension. */
791 len = SearchPathW(NULL, name, NULL, MSGSIZE, path_command.value, NULL);
792 if (len) {
793 result = &path_command;
794 }
795 }
796 else {
797 /* No extension - search using registered extensions. */
798 rc = _wdupenv_s(&pathext, &varsize, L"PATHEXT");
799 if (rc == 0) {
800 extension = wcstok_s(pathext, L";", &context);
801 while (extension) {
802 len = SearchPathW(NULL, name, extension, MSGSIZE, path_command.value, NULL);
803 if (len) {
804 result = &path_command;
805 break;
806 }
807 extension = wcstok_s(NULL, L";", &context);
808 }
809 free(pathext);
810 }
811 }
812 return result;
813}
814
815#endif
816
817static COMMAND * find_command(wchar_t * name)
818{
819 COMMAND * result = NULL;
820 COMMAND * cp = commands;
821 int i;
822
823 for (i = 0; i < num_commands; i++, cp++) {
824 if (_wcsicmp(cp->key, name) == 0) {
825 result = cp;
826 break;
827 }
828 }
829#if defined(SEARCH_PATH)
830 if (result == NULL)
831 result = find_on_path(name);
832#endif
833 return result;
834}
835
836static void
837update_command(COMMAND * cp, wchar_t * name, wchar_t * cmdline)
838{
839 wcsncpy_s(cp->key, MAX_PATH, name, _TRUNCATE);
840 wcsncpy_s(cp->value, MSGSIZE, cmdline, _TRUNCATE);
841}
842
843static void
844add_command(wchar_t * name, wchar_t * cmdline)
845{
846 if (num_commands >= MAX_COMMANDS) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800847 debug(L"can't add %ls = '%ls': no room\n", name, cmdline);
Brian Curtin07165f72012-06-20 15:36:14 -0500848 }
849 else {
850 COMMAND * cp = &commands[num_commands++];
851
852 update_command(cp, name, cmdline);
853 }
854}
855
856static void
857read_config_file(wchar_t * config_path)
858{
859 wchar_t keynames[MSGSIZE];
860 wchar_t value[MSGSIZE];
861 DWORD read;
862 wchar_t * key;
863 COMMAND * cp;
864 wchar_t * cmdp;
865
866 read = GetPrivateProfileStringW(L"commands", NULL, NULL, keynames, MSGSIZE,
867 config_path);
868 if (read == MSGSIZE - 1) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800869 debug(L"read_commands: %ls: not enough space for names\n", config_path);
Brian Curtin07165f72012-06-20 15:36:14 -0500870 }
871 key = keynames;
872 while (*key) {
873 read = GetPrivateProfileStringW(L"commands", key, NULL, value, MSGSIZE,
874 config_path);
875 if (read == MSGSIZE - 1) {
Steve Dower84bcfb32015-01-02 18:07:46 -0800876 debug(L"read_commands: %ls: not enough space for %ls\n",
Brian Curtin07165f72012-06-20 15:36:14 -0500877 config_path, key);
878 }
879 cmdp = skip_whitespace(value);
880 if (*cmdp) {
881 cp = find_command(key);
882 if (cp == NULL)
883 add_command(key, value);
884 else
885 update_command(cp, key, value);
886 }
887 key += wcslen(key) + 1;
888 }
889}
890
891static void read_commands()
892{
893 if (launcher_ini_path[0])
894 read_config_file(launcher_ini_path);
895 if (appdata_ini_path[0])
896 read_config_file(appdata_ini_path);
897}
898
899static BOOL
900parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command,
Vinay Sajip22c039b2013-06-07 15:37:28 +0100901 wchar_t ** suffix, BOOL *search)
Brian Curtin07165f72012-06-20 15:36:14 -0500902{
903 BOOL rc = FALSE;
Vinay Sajip22c039b2013-06-07 15:37:28 +0100904 SHEBANG * vpp;
Brian Curtin07165f72012-06-20 15:36:14 -0500905 size_t plen;
906 wchar_t * p;
907 wchar_t zapped;
908 wchar_t * endp = shebang_line + nchars - 1;
909 COMMAND * cp;
910 wchar_t * skipped;
911
912 *command = NULL; /* failure return */
913 *suffix = NULL;
Vinay Sajip22c039b2013-06-07 15:37:28 +0100914 *search = FALSE;
Brian Curtin07165f72012-06-20 15:36:14 -0500915
916 if ((*shebang_line++ == L'#') && (*shebang_line++ == L'!')) {
917 shebang_line = skip_whitespace(shebang_line);
918 if (*shebang_line) {
919 *command = shebang_line;
Vinay Sajip22c039b2013-06-07 15:37:28 +0100920 for (vpp = builtin_virtual_paths; vpp->shebang; ++vpp) {
921 plen = wcslen(vpp->shebang);
922 if (wcsncmp(shebang_line, vpp->shebang, plen) == 0) {
Brian Curtin07165f72012-06-20 15:36:14 -0500923 rc = TRUE;
Vinay Sajip22c039b2013-06-07 15:37:28 +0100924 *search = vpp->search;
Brian Curtin07165f72012-06-20 15:36:14 -0500925 /* We can do this because all builtin commands contain
926 * "python".
927 */
928 *command = wcsstr(shebang_line, L"python");
929 break;
930 }
931 }
Vinay Sajip22c039b2013-06-07 15:37:28 +0100932 if (vpp->shebang == NULL) {
Brian Curtin07165f72012-06-20 15:36:14 -0500933 /*
Vinay Sajip9c10d6b2013-11-15 20:58:13 +0000934 * Not found in builtins - look in customized commands.
Brian Curtin07165f72012-06-20 15:36:14 -0500935 *
936 * We can't permanently modify the shebang line in case
Vinay Sajip9c10d6b2013-11-15 20:58:13 +0000937 * it's not a customized command, but we can temporarily
Brian Curtin07165f72012-06-20 15:36:14 -0500938 * stick a NUL after the command while searching for it,
939 * then put back the char we zapped.
940 */
941#if defined(SKIP_PREFIX)
942 skipped = skip_prefix(shebang_line);
943#else
944 skipped = shebang_line;
945#endif
946 p = wcspbrk(skipped, L" \t\r\n");
947 if (p != NULL) {
948 zapped = *p;
949 *p = L'\0';
950 }
951 cp = find_command(skipped);
952 if (p != NULL)
953 *p = zapped;
954 if (cp != NULL) {
955 *command = cp->value;
956 if (p != NULL)
957 *suffix = skip_whitespace(p);
958 }
959 }
960 /* remove trailing whitespace */
961 while ((endp > shebang_line) && isspace(*endp))
962 --endp;
963 if (endp > shebang_line)
964 endp[1] = L'\0';
965 }
966 }
967 return rc;
968}
969
970/* #define CP_UTF8 65001 defined in winnls.h */
971#define CP_UTF16LE 1200
972#define CP_UTF16BE 1201
973#define CP_UTF32LE 12000
974#define CP_UTF32BE 12001
975
976typedef struct {
977 int length;
978 char sequence[4];
979 UINT code_page;
980} BOM;
981
982/*
Vinay Sajipc985d082013-07-25 11:20:55 +0100983 * Strictly, we don't need to handle UTF-16 and UTF-32, since Python itself
Brian Curtin07165f72012-06-20 15:36:14 -0500984 * doesn't. Never mind, one day it might - there's no harm leaving it in.
985 */
986static BOM BOMs[] = {
987 { 3, { 0xEF, 0xBB, 0xBF }, CP_UTF8 }, /* UTF-8 - keep first */
Serhiy Storchaka29e2aa62015-12-18 10:23:09 +0200988 /* Test UTF-32LE before UTF-16LE since UTF-16LE BOM is a prefix
989 * of UTF-32LE BOM. */
Brian Curtin07165f72012-06-20 15:36:14 -0500990 { 4, { 0xFF, 0xFE, 0x00, 0x00 }, CP_UTF32LE }, /* UTF-32LE */
991 { 4, { 0x00, 0x00, 0xFE, 0xFF }, CP_UTF32BE }, /* UTF-32BE */
Serhiy Storchaka29e2aa62015-12-18 10:23:09 +0200992 { 2, { 0xFF, 0xFE }, CP_UTF16LE }, /* UTF-16LE */
993 { 2, { 0xFE, 0xFF }, CP_UTF16BE }, /* UTF-16BE */
Brian Curtin07165f72012-06-20 15:36:14 -0500994 { 0 } /* sentinel */
995};
996
997static BOM *
998find_BOM(char * buffer)
999{
1000/*
1001 * Look for a BOM in the input and return a pointer to the
1002 * corresponding structure, or NULL if not found.
1003 */
1004 BOM * result = NULL;
1005 BOM *bom;
1006
1007 for (bom = BOMs; bom->length; bom++) {
1008 if (strncmp(bom->sequence, buffer, bom->length) == 0) {
1009 result = bom;
1010 break;
1011 }
1012 }
1013 return result;
1014}
1015
1016static char *
1017find_terminator(char * buffer, int len, BOM *bom)
1018{
1019 char * result = NULL;
1020 char * end = buffer + len;
1021 char * p;
1022 char c;
1023 int cp;
1024
1025 for (p = buffer; p < end; p++) {
1026 c = *p;
1027 if (c == '\r') {
1028 result = p;
1029 break;
1030 }
1031 if (c == '\n') {
1032 result = p;
1033 break;
1034 }
1035 }
1036 if (result != NULL) {
1037 cp = bom->code_page;
1038
1039 /* adjustments to include all bytes of the char */
1040 /* no adjustment needed for UTF-8 or big endian */
1041 if (cp == CP_UTF16LE)
1042 ++result;
1043 else if (cp == CP_UTF32LE)
1044 result += 3;
1045 ++result; /* point just past terminator */
1046 }
1047 return result;
1048}
1049
1050static BOOL
1051validate_version(wchar_t * p)
1052{
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +01001053 /*
1054 Version information should start with one of 2 or 3,
1055 Optionally followed by a period and a minor version,
1056 Optionally followed by a minus and one of 32 or 64.
1057 Valid examples:
1058 2
1059 3
1060 2.7
1061 3.6
1062 2.7-32
1063 The intent is to add to the valid patterns:
1064 3.10
1065 3-32
1066 3.6-64
1067 3-64
1068 */
1069 BOOL result = (p != NULL); /* Default to False if null pointer. */
Brian Curtin07165f72012-06-20 15:36:14 -05001070
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +01001071 result = result && iswdigit(*p); /* Result = False if fist string element is not a digit. */
1072
1073 while (result && iswdigit(*p)) /* Require a major version */
1074 ++p; /* Skip all leading digit(s) */
1075 if (result && (*p == L'.')) /* Allow . for major minor separator.*/
1076 {
1077 result = iswdigit(*++p); /* Must be at least one digit */
1078 while (result && iswdigit(*++p)) ; /* Skip any more Digits */
1079 }
1080 if (result && (*p == L'-')) { /* Allow - for Bits Separator */
1081 switch(*++p){
1082 case L'3': /* 3 is OK */
1083 result = (*++p == L'2') && !*++p; /* only if followed by 2 and ended.*/
1084 break;
1085 case L'6': /* 6 is OK */
1086 result = (*++p == L'4') && !*++p; /* only if followed by 4 and ended.*/
1087 break;
1088 default:
Brian Curtin07165f72012-06-20 15:36:14 -05001089 result = FALSE;
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +01001090 break;
Brian Curtin07165f72012-06-20 15:36:14 -05001091 }
1092 }
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +01001093 result = result && !*p; /* Must have reached EOS */
Brian Curtin07165f72012-06-20 15:36:14 -05001094 return result;
Steve (Gadget) Barnes870f6a12017-05-13 00:21:26 +01001095
Brian Curtin07165f72012-06-20 15:36:14 -05001096}
1097
1098typedef struct {
1099 unsigned short min;
1100 unsigned short max;
1101 wchar_t version[MAX_VERSION_SIZE];
1102} PYC_MAGIC;
1103
1104static PYC_MAGIC magic_values[] = {
Steve Dower7ae61af2016-05-16 09:34:20 -07001105 { 50823, 50823, L"2.0" },
1106 { 60202, 60202, L"2.1" },
1107 { 60717, 60717, L"2.2" },
1108 { 62011, 62021, L"2.3" },
1109 { 62041, 62061, L"2.4" },
1110 { 62071, 62131, L"2.5" },
1111 { 62151, 62161, L"2.6" },
1112 { 62171, 62211, L"2.7" },
1113 { 3000, 3131, L"3.0" },
1114 { 3141, 3151, L"3.1" },
1115 { 3160, 3180, L"3.2" },
1116 { 3190, 3230, L"3.3" },
1117 { 3250, 3310, L"3.4" },
Serhiy Storchaka3c317e72016-06-12 09:22:01 +03001118 { 3320, 3351, L"3.5" },
Nick Coghlan944368e2016-09-11 14:45:49 +10001119 { 3360, 3379, L"3.6" },
Yury Selivanovf2392132016-12-13 19:03:51 -05001120 { 3390, 3399, L"3.7" },
Serhiy Storchaka520b7ae2018-02-22 23:33:30 +02001121 { 3400, 3409, L"3.8" },
Brian Curtin07165f72012-06-20 15:36:14 -05001122 { 0 }
1123};
1124
1125static INSTALLED_PYTHON *
1126find_by_magic(unsigned short magic)
1127{
1128 INSTALLED_PYTHON * result = NULL;
1129 PYC_MAGIC * mp;
1130
1131 for (mp = magic_values; mp->min; mp++) {
1132 if ((magic >= mp->min) && (magic <= mp->max)) {
Paul Moore835416c2016-05-22 12:28:41 +01001133 result = locate_python(mp->version, FALSE);
Brian Curtin07165f72012-06-20 15:36:14 -05001134 if (result != NULL)
1135 break;
1136 }
1137 }
1138 return result;
1139}
1140
1141static void
1142maybe_handle_shebang(wchar_t ** argv, wchar_t * cmdline)
1143{
1144/*
1145 * Look for a shebang line in the first argument. If found
1146 * and we spawn a child process, this never returns. If it
1147 * does return then we process the args "normally".
1148 *
1149 * argv[0] might be a filename with a shebang.
1150 */
1151 FILE * fp;
1152 errno_t rc = _wfopen_s(&fp, *argv, L"rb");
Serhiy Storchakaf8ed0042015-12-18 10:19:30 +02001153 char buffer[BUFSIZE];
Brian Curtin07165f72012-06-20 15:36:14 -05001154 wchar_t shebang_line[BUFSIZE + 1];
1155 size_t read;
1156 char *p;
1157 char * start;
1158 char * shebang_alias = (char *) shebang_line;
1159 BOM* bom;
1160 int i, j, nchars = 0;
1161 int header_len;
1162 BOOL is_virt;
Vinay Sajip22c039b2013-06-07 15:37:28 +01001163 BOOL search;
Brian Curtin07165f72012-06-20 15:36:14 -05001164 wchar_t * command;
1165 wchar_t * suffix;
Vinay Sajip22c039b2013-06-07 15:37:28 +01001166 COMMAND *cmd = NULL;
Brian Curtin07165f72012-06-20 15:36:14 -05001167 INSTALLED_PYTHON * ip;
1168
1169 if (rc == 0) {
1170 read = fread(buffer, sizeof(char), BUFSIZE, fp);
1171 debug(L"maybe_handle_shebang: read %d bytes\n", read);
1172 fclose(fp);
1173
1174 if ((read >= 4) && (buffer[3] == '\n') && (buffer[2] == '\r')) {
Serhiy Storchakaf8ed0042015-12-18 10:19:30 +02001175 ip = find_by_magic((((unsigned char)buffer[1]) << 8 |
1176 (unsigned char)buffer[0]) & 0xFFFF);
Brian Curtin07165f72012-06-20 15:36:14 -05001177 if (ip != NULL) {
Steve Dower84bcfb32015-01-02 18:07:46 -08001178 debug(L"script file is compiled against Python %ls\n",
Brian Curtin07165f72012-06-20 15:36:14 -05001179 ip->version);
1180 invoke_child(ip->executable, NULL, cmdline);
1181 }
1182 }
1183 /* Look for BOM */
1184 bom = find_BOM(buffer);
1185 if (bom == NULL) {
1186 start = buffer;
1187 debug(L"maybe_handle_shebang: BOM not found, using UTF-8\n");
1188 bom = BOMs; /* points to UTF-8 entry - the default */
1189 }
1190 else {
1191 debug(L"maybe_handle_shebang: BOM found, code page %d\n",
1192 bom->code_page);
1193 start = &buffer[bom->length];
1194 }
1195 p = find_terminator(start, BUFSIZE, bom);
1196 /*
1197 * If no CR or LF was found in the heading,
1198 * we assume it's not a shebang file.
1199 */
1200 if (p == NULL) {
1201 debug(L"maybe_handle_shebang: No line terminator found\n");
1202 }
1203 else {
1204 /*
1205 * Found line terminator - parse the shebang.
1206 *
1207 * Strictly, we don't need to handle UTF-16 anf UTF-32,
1208 * since Python itself doesn't.
1209 * Never mind, one day it might.
1210 */
1211 header_len = (int) (p - start);
1212 switch(bom->code_page) {
1213 case CP_UTF8:
1214 nchars = MultiByteToWideChar(bom->code_page,
1215 0,
1216 start, header_len, shebang_line,
1217 BUFSIZE);
1218 break;
1219 case CP_UTF16BE:
1220 if (header_len % 2 != 0) {
1221 debug(L"maybe_handle_shebang: UTF-16BE, but an odd number \
1222of bytes: %d\n", header_len);
1223 /* nchars = 0; Not needed - initialised to 0. */
1224 }
1225 else {
1226 for (i = header_len; i > 0; i -= 2) {
1227 shebang_alias[i - 1] = start[i - 2];
1228 shebang_alias[i - 2] = start[i - 1];
1229 }
1230 nchars = header_len / sizeof(wchar_t);
1231 }
1232 break;
1233 case CP_UTF16LE:
1234 if ((header_len % 2) != 0) {
1235 debug(L"UTF-16LE, but an odd number of bytes: %d\n",
1236 header_len);
1237 /* nchars = 0; Not needed - initialised to 0. */
1238 }
1239 else {
1240 /* no actual conversion needed. */
1241 memcpy(shebang_line, start, header_len);
1242 nchars = header_len / sizeof(wchar_t);
1243 }
1244 break;
1245 case CP_UTF32BE:
1246 if (header_len % 4 != 0) {
1247 debug(L"UTF-32BE, but not divisible by 4: %d\n",
1248 header_len);
1249 /* nchars = 0; Not needed - initialised to 0. */
1250 }
1251 else {
1252 for (i = header_len, j = header_len / 2; i > 0; i -= 4,
1253 j -= 2) {
1254 shebang_alias[j - 1] = start[i - 2];
1255 shebang_alias[j - 2] = start[i - 1];
1256 }
1257 nchars = header_len / sizeof(wchar_t);
1258 }
1259 break;
1260 case CP_UTF32LE:
1261 if (header_len % 4 != 0) {
1262 debug(L"UTF-32LE, but not divisible by 4: %d\n",
1263 header_len);
1264 /* nchars = 0; Not needed - initialised to 0. */
1265 }
1266 else {
1267 for (i = header_len, j = header_len / 2; i > 0; i -= 4,
1268 j -= 2) {
1269 shebang_alias[j - 1] = start[i - 3];
1270 shebang_alias[j - 2] = start[i - 4];
1271 }
1272 nchars = header_len / sizeof(wchar_t);
1273 }
1274 break;
1275 }
1276 if (nchars > 0) {
1277 shebang_line[--nchars] = L'\0';
1278 is_virt = parse_shebang(shebang_line, nchars, &command,
Vinay Sajip22c039b2013-06-07 15:37:28 +01001279 &suffix, &search);
Brian Curtin07165f72012-06-20 15:36:14 -05001280 if (command != NULL) {
Steve Dower84bcfb32015-01-02 18:07:46 -08001281 debug(L"parse_shebang: found command: %ls\n", command);
Brian Curtin07165f72012-06-20 15:36:14 -05001282 if (!is_virt) {
1283 invoke_child(command, suffix, cmdline);
1284 }
1285 else {
1286 suffix = wcschr(command, L' ');
1287 if (suffix != NULL) {
1288 *suffix++ = L'\0';
1289 suffix = skip_whitespace(suffix);
1290 }
1291 if (wcsncmp(command, L"python", 6))
1292 error(RC_BAD_VIRTUAL_PATH, L"Unknown virtual \
Steve Dower84bcfb32015-01-02 18:07:46 -08001293path '%ls'", command);
Brian Curtin07165f72012-06-20 15:36:14 -05001294 command += 6; /* skip past "python" */
Vinay Sajip22c039b2013-06-07 15:37:28 +01001295 if (search && ((*command == L'\0') || isspace(*command))) {
1296 /* Command is eligible for path search, and there
1297 * is no version specification.
1298 */
1299 debug(L"searching PATH for python executable\n");
Vinay Sajipa5892ab2015-12-26 13:10:51 +00001300 cmd = find_on_path(PYTHON_EXECUTABLE);
Steve Dower84bcfb32015-01-02 18:07:46 -08001301 debug(L"Python on path: %ls\n", cmd ? cmd->value : L"<not found>");
Vinay Sajip22c039b2013-06-07 15:37:28 +01001302 if (cmd) {
Steve Dower84bcfb32015-01-02 18:07:46 -08001303 debug(L"located python on PATH: %ls\n", cmd->value);
Vinay Sajip22c039b2013-06-07 15:37:28 +01001304 invoke_child(cmd->value, suffix, cmdline);
1305 /* Exit here, as we have found the command */
1306 return;
1307 }
1308 /* FALL THROUGH: No python found on PATH, so fall
1309 * back to locating the correct installed python.
1310 */
1311 }
Brian Curtin07165f72012-06-20 15:36:14 -05001312 if (*command && !validate_version(command))
1313 error(RC_BAD_VIRTUAL_PATH, L"Invalid version \
Steve Dower84bcfb32015-01-02 18:07:46 -08001314specification: '%ls'.\nIn the first line of the script, 'python' needs to be \
Brian Curtin07165f72012-06-20 15:36:14 -05001315followed by a valid version specifier.\nPlease check the documentation.",
1316 command);
1317 /* TODO could call validate_version(command) */
Paul Moore835416c2016-05-22 12:28:41 +01001318 ip = locate_python(command, TRUE);
Brian Curtin07165f72012-06-20 15:36:14 -05001319 if (ip == NULL) {
1320 error(RC_NO_PYTHON, L"Requested Python version \
Steve Dower84bcfb32015-01-02 18:07:46 -08001321(%ls) is not installed", command);
Brian Curtin07165f72012-06-20 15:36:14 -05001322 }
1323 else {
1324 invoke_child(ip->executable, suffix, cmdline);
1325 }
1326 }
1327 }
1328 }
1329 }
1330 }
1331}
1332
1333static wchar_t *
1334skip_me(wchar_t * cmdline)
1335{
1336 BOOL quoted;
1337 wchar_t c;
1338 wchar_t * result = cmdline;
1339
1340 quoted = cmdline[0] == L'\"';
1341 if (!quoted)
1342 c = L' ';
1343 else {
1344 c = L'\"';
1345 ++result;
1346 }
1347 result = wcschr(result, c);
1348 if (result == NULL) /* when, for example, just exe name on command line */
1349 result = L"";
1350 else {
1351 ++result; /* skip past space or closing quote */
1352 result = skip_whitespace(result);
1353 }
1354 return result;
1355}
1356
1357static DWORD version_high = 0;
1358static DWORD version_low = 0;
1359
1360static void
1361get_version_info(wchar_t * version_text, size_t size)
1362{
1363 WORD maj, min, rel, bld;
1364
1365 if (!version_high && !version_low)
1366 wcsncpy_s(version_text, size, L"0.1", _TRUNCATE); /* fallback */
1367 else {
1368 maj = HIWORD(version_high);
1369 min = LOWORD(version_high);
1370 rel = HIWORD(version_low);
1371 bld = LOWORD(version_low);
1372 _snwprintf_s(version_text, size, _TRUNCATE, L"%d.%d.%d.%d", maj,
1373 min, rel, bld);
1374 }
1375}
1376
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001377static void
1378show_help_text(wchar_t ** argv)
1379{
1380 wchar_t version_text [MAX_PATH];
1381#if defined(_M_X64)
1382 BOOL canDo64bit = TRUE;
1383#else
1384 /* If we are a 32bit process on a 64bit Windows, first hit the 64bit keys. */
1385 BOOL canDo64bit = FALSE;
1386 IsWow64Process(GetCurrentProcess(), &canDo64bit);
1387#endif
1388
1389 get_version_info(version_text, MAX_PATH);
1390 fwprintf(stdout, L"\
1391Python Launcher for Windows Version %ls\n\n", version_text);
1392 fwprintf(stdout, L"\
1393usage:\n\
1394%ls [launcher-args] [python-args] script [script-args]\n\n", argv[0]);
1395 fputws(L"\
1396Launcher arguments:\n\n\
1397-2 : Launch the latest Python 2.x version\n\
1398-3 : Launch the latest Python 3.x version\n\
1399-X.Y : Launch the specified Python version\n", stdout);
1400 if (canDo64bit) {
1401 fputws(L"\
1402 The above all default to 64 bit if a matching 64 bit python is present.\n\
1403-X.Y-32: Launch the specified 32bit Python version\n\
1404-X-32 : Launch the latest 32bit Python X version\n\
1405-X.Y-64: Launch the specified 64bit Python version\n\
1406-X-64 : Launch the latest 64bit Python X version", stdout);
1407 }
1408 fputws(L"\n-0 --list : List the available pythons", stdout);
1409 fputws(L"\n-0p --list-paths : List with paths", stdout);
1410 fputws(L"\n\nThe following help text is from Python:\n\n", stdout);
1411 fflush(stdout);
1412}
1413
1414static BOOL
1415show_python_list(wchar_t ** argv)
1416{
1417 /*
1418 * Display options -0
1419 */
1420 INSTALLED_PYTHON * result = NULL;
1421 INSTALLED_PYTHON * ip = installed_pythons; /* List of installed pythons */
1422 INSTALLED_PYTHON * defpy = locate_python(L"", FALSE);
1423 size_t i = 0;
1424 wchar_t *p = argv[1];
1425 wchar_t *fmt = L"\n -%ls-%d"; /* print VER-BITS */
1426 wchar_t *defind = L" *"; /* Default indicator */
1427
1428 /*
1429 * Output informational messages to stderr to keep output
1430 * clean for use in pipes, etc.
1431 */
1432 fwprintf(stderr,
1433 L"Installed Pythons found by %s Launcher for Windows", argv[0]);
1434 if (!_wcsicmp(p, L"-0p") || !_wcsicmp(p, L"--list-paths")) /* Show path? */
1435 fmt = L"\n -%ls-%d\t%ls"; /* print VER-BITS path */
1436
1437 if (num_installed_pythons == 0) /* We have somehow got here without searching for pythons */
1438 locate_all_pythons(); /* Find them, Populates installed_pythons */
1439
1440 if (num_installed_pythons == 0) /* No pythons found */
1441 fwprintf(stderr, L"\nNo Installed Pythons Found!");
1442 else
1443 {
1444 for (i = 0; i < num_installed_pythons; i++, ip++) {
1445 fwprintf(stdout, fmt, ip->version, ip->bits, ip->executable);
1446 /* If there is a default indicate it */
1447 if ((defpy != NULL) && !_wcsicmp(ip->executable, defpy->executable))
1448 fwprintf(stderr, defind);
1449 }
1450 }
1451
1452 if ((defpy == NULL) && (num_installed_pythons > 0))
1453 /* We have pythons but none is the default */
1454 fwprintf(stderr, L"\n\nCan't find a Default Python.\n\n");
1455 else
1456 fwprintf(stderr, L"\n\n"); /* End with a blank line */
1457 return(FALSE); /* If this has been called we cannot continue */
1458}
1459
Brian Curtin07165f72012-06-20 15:36:14 -05001460static int
1461process(int argc, wchar_t ** argv)
1462{
1463 wchar_t * wp;
1464 wchar_t * command;
Steve Dower76998fe2015-02-26 14:25:33 -08001465 wchar_t * executable;
Brian Curtin07165f72012-06-20 15:36:14 -05001466 wchar_t * p;
1467 int rc = 0;
1468 size_t plen;
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001469 size_t slen;
Brian Curtin07165f72012-06-20 15:36:14 -05001470 INSTALLED_PYTHON * ip;
1471 BOOL valid;
1472 DWORD size, attrs;
1473 HRESULT hr;
1474 wchar_t message[MSGSIZE];
Brian Curtin07165f72012-06-20 15:36:14 -05001475 void * version_data;
1476 VS_FIXEDFILEINFO * file_info;
1477 UINT block_size;
Vinay Sajip2ae8c632013-01-29 22:29:25 +00001478 int index;
Vinay Sajipc985d082013-07-25 11:20:55 +01001479#if defined(SCRIPT_WRAPPER)
1480 int newlen;
1481 wchar_t * newcommand;
1482 wchar_t * av[2];
1483#endif
Brian Curtin07165f72012-06-20 15:36:14 -05001484
Vinay Sajipaab9f462015-12-26 12:35:47 +00001485 setvbuf(stderr, (char *)NULL, _IONBF, 0);
Brian Curtin07165f72012-06-20 15:36:14 -05001486 wp = get_env(L"PYLAUNCH_DEBUG");
1487 if ((wp != NULL) && (*wp != L'\0'))
1488 log_fp = stderr;
1489
1490#if defined(_M_X64)
1491 debug(L"launcher build: 64bit\n");
1492#else
1493 debug(L"launcher build: 32bit\n");
1494#endif
1495#if defined(_WINDOWS)
1496 debug(L"launcher executable: Windows\n");
1497#else
1498 debug(L"launcher executable: Console\n");
1499#endif
1500 /* Get the local appdata folder (non-roaming) */
1501 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA,
1502 NULL, 0, appdata_ini_path);
1503 if (hr != S_OK) {
1504 debug(L"SHGetFolderPath failed: %X\n", hr);
1505 appdata_ini_path[0] = L'\0';
1506 }
1507 else {
1508 plen = wcslen(appdata_ini_path);
1509 p = &appdata_ini_path[plen];
1510 wcsncpy_s(p, MAX_PATH - plen, L"\\py.ini", _TRUNCATE);
1511 attrs = GetFileAttributesW(appdata_ini_path);
1512 if (attrs == INVALID_FILE_ATTRIBUTES) {
Steve Dower84bcfb32015-01-02 18:07:46 -08001513 debug(L"File '%ls' non-existent\n", appdata_ini_path);
Brian Curtin07165f72012-06-20 15:36:14 -05001514 appdata_ini_path[0] = L'\0';
1515 } else {
Steve Dower84bcfb32015-01-02 18:07:46 -08001516 debug(L"Using local configuration file '%ls'\n", appdata_ini_path);
Brian Curtin07165f72012-06-20 15:36:14 -05001517 }
1518 }
1519 plen = GetModuleFileNameW(NULL, launcher_ini_path, MAX_PATH);
1520 size = GetFileVersionInfoSizeW(launcher_ini_path, &size);
1521 if (size == 0) {
1522 winerror(GetLastError(), message, MSGSIZE);
Steve Dower84bcfb32015-01-02 18:07:46 -08001523 debug(L"GetFileVersionInfoSize failed: %ls\n", message);
Brian Curtin07165f72012-06-20 15:36:14 -05001524 }
1525 else {
1526 version_data = malloc(size);
1527 if (version_data) {
1528 valid = GetFileVersionInfoW(launcher_ini_path, 0, size,
1529 version_data);
1530 if (!valid)
1531 debug(L"GetFileVersionInfo failed: %X\n", GetLastError());
1532 else {
Vinay Sajip404229b2013-01-29 22:52:57 +00001533 valid = VerQueryValueW(version_data, L"\\",
1534 (LPVOID *) &file_info, &block_size);
Brian Curtin07165f72012-06-20 15:36:14 -05001535 if (!valid)
1536 debug(L"VerQueryValue failed: %X\n", GetLastError());
1537 else {
1538 version_high = file_info->dwFileVersionMS;
1539 version_low = file_info->dwFileVersionLS;
1540 }
1541 }
1542 free(version_data);
1543 }
1544 }
1545 p = wcsrchr(launcher_ini_path, L'\\');
1546 if (p == NULL) {
Steve Dower84bcfb32015-01-02 18:07:46 -08001547 debug(L"GetModuleFileNameW returned value has no backslash: %ls\n",
Brian Curtin07165f72012-06-20 15:36:14 -05001548 launcher_ini_path);
1549 launcher_ini_path[0] = L'\0';
1550 }
1551 else {
1552 wcsncpy_s(p, MAX_PATH - (p - launcher_ini_path), L"\\py.ini",
1553 _TRUNCATE);
1554 attrs = GetFileAttributesW(launcher_ini_path);
1555 if (attrs == INVALID_FILE_ATTRIBUTES) {
Steve Dower84bcfb32015-01-02 18:07:46 -08001556 debug(L"File '%ls' non-existent\n", launcher_ini_path);
Brian Curtin07165f72012-06-20 15:36:14 -05001557 launcher_ini_path[0] = L'\0';
1558 } else {
Steve Dower84bcfb32015-01-02 18:07:46 -08001559 debug(L"Using global configuration file '%ls'\n", launcher_ini_path);
Brian Curtin07165f72012-06-20 15:36:14 -05001560 }
1561 }
1562
1563 command = skip_me(GetCommandLineW());
Steve Dower84bcfb32015-01-02 18:07:46 -08001564 debug(L"Called with command line: %ls\n", command);
Vinay Sajipc985d082013-07-25 11:20:55 +01001565
1566#if defined(SCRIPT_WRAPPER)
1567 /* The launcher is being used in "script wrapper" mode.
1568 * There should therefore be a Python script named <exename>-script.py in
1569 * the same directory as the launcher executable.
1570 * Put the script name into argv as the first (script name) argument.
1571 */
1572
1573 /* Get the wrapped script name - if the script is not present, this will
1574 * terminate the program with an error.
1575 */
1576 locate_wrapped_script();
1577
1578 /* Add the wrapped script to the start of command */
1579 newlen = wcslen(wrapped_script_path) + wcslen(command) + 2; /* ' ' + NUL */
1580 newcommand = malloc(sizeof(wchar_t) * newlen);
1581 if (!newcommand) {
1582 error(RC_NO_MEMORY, L"Could not allocate new command line");
1583 }
1584 else {
1585 wcscpy_s(newcommand, newlen, wrapped_script_path);
1586 wcscat_s(newcommand, newlen, L" ");
1587 wcscat_s(newcommand, newlen, command);
Steve Dower84bcfb32015-01-02 18:07:46 -08001588 debug(L"Running wrapped script with command line '%ls'\n", newcommand);
Vinay Sajipc985d082013-07-25 11:20:55 +01001589 read_commands();
1590 av[0] = wrapped_script_path;
1591 av[1] = NULL;
1592 maybe_handle_shebang(av, newcommand);
1593 /* Returns if no shebang line - pass to default processing */
1594 command = newcommand;
1595 valid = FALSE;
1596 }
1597#else
Brian Curtin07165f72012-06-20 15:36:14 -05001598 if (argc <= 1) {
1599 valid = FALSE;
1600 p = NULL;
1601 }
1602 else {
1603 p = argv[1];
1604 plen = wcslen(p);
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001605 if (argc == 2) {
1606 slen = wcslen(L"-0");
1607 if(!wcsncmp(p, L"-0", slen)) /* Starts with -0 */
1608 valid = show_python_list(argv); /* Check for -0 FIRST */
1609 }
1610 valid = valid && (*p == L'-') && validate_version(&p[1]);
Brian Curtin07165f72012-06-20 15:36:14 -05001611 if (valid) {
Paul Moore835416c2016-05-22 12:28:41 +01001612 ip = locate_python(&p[1], FALSE);
Brian Curtin07165f72012-06-20 15:36:14 -05001613 if (ip == NULL)
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001614 {
1615 fwprintf(stdout, \
1616 L"Python %ls not found!\n", &p[1]);
1617 valid = show_python_list(argv);
Steve Dower84bcfb32015-01-02 18:07:46 -08001618 error(RC_NO_PYTHON, L"Requested Python version (%ls) not \
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001619installed, use -0 for available pythons", &p[1]);
1620 }
Steve Dower76998fe2015-02-26 14:25:33 -08001621 executable = ip->executable;
Brian Curtin07165f72012-06-20 15:36:14 -05001622 command += wcslen(p);
1623 command = skip_whitespace(command);
1624 }
Vinay Sajip2ae8c632013-01-29 22:29:25 +00001625 else {
1626 for (index = 1; index < argc; ++index) {
1627 if (*argv[index] != L'-')
1628 break;
1629 }
1630 if (index < argc) {
1631 read_commands();
1632 maybe_handle_shebang(&argv[index], command);
1633 }
1634 }
Brian Curtin07165f72012-06-20 15:36:14 -05001635 }
Vinay Sajipc985d082013-07-25 11:20:55 +01001636#endif
1637
Brian Curtin07165f72012-06-20 15:36:14 -05001638 if (!valid) {
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001639 if ((argc == 2) && (!_wcsicmp(p, L"-h") || !_wcsicmp(p, L"--help")))
1640 show_help_text(argv);
1641 if ((argc == 2) && (!_wcsicmp(p, L"-0") || !_wcsicmp(p, L"-0p")))
1642 executable = NULL; /* Info call only */
1643 else
1644 {
1645 /* Look for an active virtualenv */
1646 executable = find_python_by_venv();
Steve Dower76998fe2015-02-26 14:25:33 -08001647
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001648 /* If we didn't find one, look for the default Python */
1649 if (executable == NULL) {
1650 ip = locate_python(L"", FALSE);
1651 if (ip == NULL)
1652 error(RC_NO_PYTHON, L"Can't find a default Python.");
1653 executable = ip->executable;
Brian Curtin07165f72012-06-20 15:36:14 -05001654 }
Brian Curtin07165f72012-06-20 15:36:14 -05001655 }
1656 }
Steve (Gadget) Barnes5b8f9722017-06-28 20:14:52 +01001657 if (executable != NULL)
1658 invoke_child(executable, NULL, command);
1659 else
1660 rc = RC_NO_PYTHON;
Brian Curtin07165f72012-06-20 15:36:14 -05001661 return rc;
1662}
1663
1664#if defined(_WINDOWS)
1665
1666int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
1667 LPWSTR lpstrCmd, int nShow)
1668{
1669 return process(__argc, __wargv);
1670}
1671
1672#else
1673
1674int cdecl wmain(int argc, wchar_t ** argv)
1675{
1676 return process(argc, argv);
1677}
1678
Vinay Sajip2ae8c632013-01-29 22:29:25 +00001679#endif