diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2013-10-08 21:08:48 +0300 |
---|---|---|
committer | Serhiy Storchaka <storchaka@gmail.com> | 2013-10-08 21:08:48 +0300 |
commit | 9d67ec51a65af6408887041d8e841c1bf7d046b7 (patch) | |
tree | c3201ce7b6a3128cb8216a6e04ad208d4d9428d9 /PC/launcher.c | |
parent | c3a0b4fef5f33c776857fe336932d163cd56815d (diff) | |
parent | 3cf0676c2d969a7c795c3ccf152e2dd77008ee14 (diff) | |
download | cpython-9d67ec51a65af6408887041d8e841c1bf7d046b7.tar.gz |
Issue #18037: Do not escape '\u' and '\U' in raw strings.
Diffstat (limited to 'PC/launcher.c')
-rw-r--r-- | PC/launcher.c | 254 |
1 files changed, 194 insertions, 60 deletions
diff --git a/PC/launcher.c b/PC/launcher.c index 445e96e573..c8af197206 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Vinay Sajip. + * Copyright (C) 2011-2013 Vinay Sajip. * Licensed to PSF under a contributor agreement. * * Based on the work of: @@ -18,7 +18,28 @@ /* Build options. */ #define SKIP_PREFIX -/* #define SEARCH_PATH */ +#define SEARCH_PATH + +/* Error codes */ + +#define RC_NO_STD_HANDLES 100 +#define RC_CREATE_PROCESS 101 +#define RC_BAD_VIRTUAL_PATH 102 +#define RC_NO_PYTHON 103 +#define RC_NO_MEMORY 104 +/* + * SCRIPT_WRAPPER is used to choose between two variants of an executable built + * from this source file. If not defined, the PEP 397 Python launcher is built; + * if defined, a script launcher of the type used by setuptools is built, which + * looks for a script name related to the executable name and runs that script + * with the appropriate Python interpreter. + * + * SCRIPT_WRAPPER should be undefined in the source, and defined in a VS project + * which builds the setuptools-style launcher. + */ +#if defined(SCRIPT_WRAPPER) +#define RC_NO_SCRIPT 105 +#endif /* Just for now - static definition */ @@ -32,32 +53,6 @@ skip_whitespace(wchar_t * p) return p; } -/* - * This function is here to simplify memory management - * and to treat blank values as if they are absent. - */ -static wchar_t * get_env(wchar_t * key) -{ - /* This is not thread-safe, just like getenv */ - static wchar_t buf[256]; - DWORD result = GetEnvironmentVariableW(key, buf, 256); - - if (result > 255) { - /* Large environment variable. Accept some leakage */ - wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1)); - GetEnvironmentVariableW(key, buf2, result); - return buf2; - } - - if (result == 0) - /* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND, - or an empty environment variable. */ - return NULL; - - return buf; -} - - static void debug(wchar_t * format, ...) { @@ -100,11 +95,40 @@ error(int rc, wchar_t * format, ... ) #if !defined(_WINDOWS) fwprintf(stderr, L"%s\n", message); #else - MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), MB_OK); + MessageBox(NULL, message, TEXT("Python Launcher is sorry to say ..."), + MB_OK); #endif ExitProcess(rc); } +/* + * This function is here to simplify memory management + * and to treat blank values as if they are absent. + */ +static wchar_t * get_env(wchar_t * key) +{ + /* This is not thread-safe, just like getenv */ + static wchar_t buf[BUFSIZE]; + DWORD result = GetEnvironmentVariableW(key, buf, BUFSIZE); + + if (result >= BUFSIZE) { + /* Large environment variable. Accept some leakage */ + wchar_t *buf2 = (wchar_t*)malloc(sizeof(wchar_t) * (result+1)); + if (buf2 = NULL) { + error(RC_NO_MEMORY, L"Could not allocate environment buffer"); + } + GetEnvironmentVariableW(key, buf2, result); + return buf2; + } + + if (result == 0) + /* Either some error, e.g. ERROR_ENVVAR_NOT_FOUND, + or an empty environment variable. */ + return NULL; + + return buf; +} + #if defined(_WINDOWS) #define PYTHON_EXECUTABLE L"pythonw.exe" @@ -115,11 +139,6 @@ error(int rc, wchar_t * format, ... ) #endif -#define RC_NO_STD_HANDLES 100 -#define RC_CREATE_PROCESS 101 -#define RC_BAD_VIRTUAL_PATH 102 -#define RC_NO_PYTHON 103 - #define MAX_VERSION_SIZE 4 typedef struct { @@ -457,6 +476,51 @@ locate_python(wchar_t * wanted_ver) return result; } +#if defined(SCRIPT_WRAPPER) +/* + * Check for a script located alongside the executable + */ + +#if defined(_WINDOWS) +#define SCRIPT_SUFFIX L"-script.pyw" +#else +#define SCRIPT_SUFFIX L"-script.py" +#endif + +static wchar_t wrapped_script_path[MAX_PATH]; + +/* Locate the script being wrapped. + * + * This code should store the name of the wrapped script in + * wrapped_script_path, or terminate the program with an error if there is no + * valid wrapped script file. + */ +static void +locate_wrapped_script() +{ + wchar_t * p; + size_t plen; + DWORD attrs; + + plen = GetModuleFileNameW(NULL, wrapped_script_path, MAX_PATH); + p = wcsrchr(wrapped_script_path, L'.'); + if (p == NULL) { + debug(L"GetModuleFileNameW returned value has no extension: %s\n", + wrapped_script_path); + error(RC_NO_SCRIPT, L"Wrapper name '%s' is not valid.", wrapped_script_path); + } + + wcsncpy_s(p, MAX_PATH - (p - wrapped_script_path) + 1, SCRIPT_SUFFIX, _TRUNCATE); + attrs = GetFileAttributesW(wrapped_script_path); + if (attrs == INVALID_FILE_ATTRIBUTES) { + debug(L"File '%s' non-existent\n", wrapped_script_path); + error(RC_NO_SCRIPT, L"Script file '%s' is not present.", wrapped_script_path); + } + + debug(L"Using wrapped script file '%s'\n", wrapped_script_path); +} +#endif + /* * Process creation code */ @@ -550,7 +614,7 @@ run_child(wchar_t * cmdline) error(RC_CREATE_PROCESS, L"Unable to create process using '%s'", cmdline); AssignProcessToJobObject(job, pi.hProcess); CloseHandle(pi.hThread); - WaitForSingleObject(pi.hProcess, INFINITE); + WaitForSingleObjectEx(pi.hProcess, INFINITE, FALSE); ok = GetExitCodeProcess(pi.hProcess, &rc); if (!ok) error(RC_CREATE_PROCESS, L"Failed to get exit code of process"); @@ -595,12 +659,17 @@ invoke_child(wchar_t * executable, wchar_t * suffix, wchar_t * cmdline) } } -static wchar_t * builtin_virtual_paths [] = { - L"/usr/bin/env python", - L"/usr/bin/python", - L"/usr/local/bin/python", - L"python", - NULL +typedef struct { + wchar_t *shebang; + BOOL search; +} SHEBANG; + +static SHEBANG builtin_virtual_paths [] = { + { L"/usr/bin/env python", TRUE }, + { L"/usr/bin/python", FALSE }, + { L"/usr/local/bin/python", FALSE }, + { L"python", FALSE }, + { NULL, FALSE }, }; /* For now, a static array of commands. */ @@ -776,10 +845,10 @@ static void read_commands() static BOOL parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, - wchar_t ** suffix) + wchar_t ** suffix, BOOL *search) { BOOL rc = FALSE; - wchar_t ** vpp; + SHEBANG * vpp; size_t plen; wchar_t * p; wchar_t zapped; @@ -789,15 +858,17 @@ parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, *command = NULL; /* failure return */ *suffix = NULL; + *search = FALSE; if ((*shebang_line++ == L'#') && (*shebang_line++ == L'!')) { shebang_line = skip_whitespace(shebang_line); if (*shebang_line) { *command = shebang_line; - for (vpp = builtin_virtual_paths; *vpp; ++vpp) { - plen = wcslen(*vpp); - if (wcsncmp(shebang_line, *vpp, plen) == 0) { + for (vpp = builtin_virtual_paths; vpp->shebang; ++vpp) { + plen = wcslen(vpp->shebang); + if (wcsncmp(shebang_line, vpp->shebang, plen) == 0) { rc = TRUE; + *search = vpp->search; /* We can do this because all builtin commands contain * "python". */ @@ -805,7 +876,7 @@ parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, break; } } - if (*vpp == NULL) { + if (vpp->shebang == NULL) { /* * Not found in builtins - look in customised commands. * @@ -856,7 +927,7 @@ typedef struct { } BOM; /* - * Strictly, we don't need to handle UTF-16 anf UTF-32, since Python itself + * Strictly, we don't need to handle UTF-16 and UTF-32, since Python itself * doesn't. Never mind, one day it might - there's no harm leaving it in. */ static BOM BOMs[] = { @@ -1012,8 +1083,10 @@ maybe_handle_shebang(wchar_t ** argv, wchar_t * cmdline) int i, j, nchars = 0; int header_len; BOOL is_virt; + BOOL search; wchar_t * command; wchar_t * suffix; + COMMAND *cmd = NULL; INSTALLED_PYTHON * ip; if (rc == 0) { @@ -1125,7 +1198,7 @@ of bytes: %d\n", header_len); if (nchars > 0) { shebang_line[--nchars] = L'\0'; is_virt = parse_shebang(shebang_line, nchars, &command, - &suffix); + &suffix, &search); if (command != NULL) { debug(L"parse_shebang: found command: %s\n", command); if (!is_virt) { @@ -1141,6 +1214,23 @@ of bytes: %d\n", header_len); error(RC_BAD_VIRTUAL_PATH, L"Unknown virtual \ path '%s'", command); command += 6; /* skip past "python" */ + if (search && ((*command == L'\0') || isspace(*command))) { + /* Command is eligible for path search, and there + * is no version specification. + */ + debug(L"searching PATH for python executable\n"); + cmd = find_on_path(L"python"); + debug(L"Python on path: %s\n", cmd ? cmd->value : L"<not found>"); + if (cmd) { + debug(L"located python on PATH: %s\n", cmd->value); + invoke_child(cmd->value, suffix, cmdline); + /* Exit here, as we have found the command */ + return; + } + /* FALL THROUGH: No python found on PATH, so fall + * back to locating the correct installed python. + */ + } if (*command && !validate_version(command)) error(RC_BAD_VIRTUAL_PATH, L"Invalid version \ specification: '%s'.\nIn the first line of the script, 'python' needs to be \ @@ -1223,6 +1313,12 @@ process(int argc, wchar_t ** argv) void * version_data; VS_FIXEDFILEINFO * file_info; UINT block_size; + int index; +#if defined(SCRIPT_WRAPPER) + int newlen; + wchar_t * newcommand; + wchar_t * av[2]; +#endif wp = get_env(L"PYLAUNCH_DEBUG"); if ((wp != NULL) && (*wp != L'\0')) @@ -1271,8 +1367,8 @@ process(int argc, wchar_t ** argv) if (!valid) debug(L"GetFileVersionInfo failed: %X\n", GetLastError()); else { - valid = VerQueryValueW(version_data, L"\\", &file_info, - &block_size); + valid = VerQueryValueW(version_data, L"\\", + (LPVOID *) &file_info, &block_size); if (!valid) debug(L"VerQueryValue failed: %X\n", GetLastError()); else { @@ -1302,7 +1398,40 @@ process(int argc, wchar_t ** argv) } command = skip_me(GetCommandLineW()); - debug(L"Called with command line: %s", command); + debug(L"Called with command line: %s\n", command); + +#if defined(SCRIPT_WRAPPER) + /* The launcher is being used in "script wrapper" mode. + * There should therefore be a Python script named <exename>-script.py in + * the same directory as the launcher executable. + * Put the script name into argv as the first (script name) argument. + */ + + /* Get the wrapped script name - if the script is not present, this will + * terminate the program with an error. + */ + locate_wrapped_script(); + + /* Add the wrapped script to the start of command */ + newlen = wcslen(wrapped_script_path) + wcslen(command) + 2; /* ' ' + NUL */ + newcommand = malloc(sizeof(wchar_t) * newlen); + if (!newcommand) { + error(RC_NO_MEMORY, L"Could not allocate new command line"); + } + else { + wcscpy_s(newcommand, newlen, wrapped_script_path); + wcscat_s(newcommand, newlen, L" "); + wcscat_s(newcommand, newlen, command); + debug(L"Running wrapped script with command line '%s'\n", newcommand); + read_commands(); + av[0] = wrapped_script_path; + av[1] = NULL; + maybe_handle_shebang(av, newcommand); + /* Returns if no shebang line - pass to default processing */ + command = newcommand; + valid = FALSE; + } +#else if (argc <= 1) { valid = FALSE; p = NULL; @@ -1310,13 +1439,6 @@ process(int argc, wchar_t ** argv) else { p = argv[1]; plen = wcslen(p); - if (p[0] != L'-') { - read_commands(); - maybe_handle_shebang(&argv[1], command); - } - /* No file with shebang, or an unrecognised shebang. - * Is the first arg a special version qualifier? - */ valid = (*p == L'-') && validate_version(&p[1]); if (valid) { ip = locate_python(&p[1]); @@ -1326,7 +1448,19 @@ installed", &p[1]); command += wcslen(p); command = skip_whitespace(command); } + else { + for (index = 1; index < argc; ++index) { + if (*argv[index] != L'-') + break; + } + if (index < argc) { + read_commands(); + maybe_handle_shebang(&argv[index], command); + } + } } +#endif + if (!valid) { ip = locate_python(L""); if (ip == NULL) @@ -1344,7 +1478,7 @@ installed", &p[1]); fwprintf(stdout, L"\ Python Launcher for Windows Version %s\n\n", version_text); fwprintf(stdout, L"\ -usage: %s [ launcher-arguments ] script [ script-arguments ]\n\n", argv[0]); +usage: %s [ launcher-arguments ] [ python-arguments ] script [ script-arguments ]\n\n", argv[0]); fputws(L"\ Launcher arguments:\n\n\ -2 : Launch the latest Python 2.x version\n\ |