summaryrefslogtreecommitdiff
path: root/PC/launcher.c
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2013-10-08 21:08:48 +0300
committerSerhiy Storchaka <storchaka@gmail.com>2013-10-08 21:08:48 +0300
commit9d67ec51a65af6408887041d8e841c1bf7d046b7 (patch)
treec3201ce7b6a3128cb8216a6e04ad208d4d9428d9 /PC/launcher.c
parentc3a0b4fef5f33c776857fe336932d163cd56815d (diff)
parent3cf0676c2d969a7c795c3ccf152e2dd77008ee14 (diff)
downloadcpython-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.c254
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\