summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@python.org>2022-11-18 14:14:56 (GMT)
committerGitHub <noreply@github.com>2022-11-18 14:14:56 (GMT)
commita220c6d1ee3053895f502b43b47dc3a9c55fa6a3 (patch)
treeb6a7f681156f597a8e1907172e417e24f7209347
parent4f5e1cb00a914692895c1c16e446c8d2ab3efb7e (diff)
downloadcpython-a220c6d1ee3053895f502b43b47dc3a9c55fa6a3.zip
cpython-a220c6d1ee3053895f502b43b47dc3a9c55fa6a3.tar.gz
cpython-a220c6d1ee3053895f502b43b47dc3a9c55fa6a3.tar.bz2
gh-99442: Fix handling in py.exe launcher when argv[0] does not include a file extension (GH-99542)
-rw-r--r--Lib/test/test_launcher.py11
-rw-r--r--Misc/NEWS.d/next/Windows/2022-11-16-19-03-21.gh-issue-99442.6Dgk3Q.rst2
-rw-r--r--PC/launcher2.c82
3 files changed, 44 insertions, 51 deletions
diff --git a/Lib/test/test_launcher.py b/Lib/test/test_launcher.py
index 6ad85dc..47152d4 100644
--- a/Lib/test/test_launcher.py
+++ b/Lib/test/test_launcher.py
@@ -173,7 +173,7 @@ class RunPyMixin:
errors="ignore",
) as p:
p.stdin.close()
- version = next(p.stdout).splitlines()[0].rpartition(" ")[2]
+ version = next(p.stdout, "\n").splitlines()[0].rpartition(" ")[2]
p.stdout.read()
p.wait(10)
if not sys.version.startswith(version):
@@ -467,6 +467,15 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
self.assertEqual("3.100-arm64", data["SearchInfo.tag"])
self.assertEqual("X.Y-arm64.exe -X fake_arg_for_test -arg", data["stdout"].strip())
+ def test_py_default_short_argv0(self):
+ with self.py_ini(TEST_PY_COMMANDS):
+ for argv0 in ['"py.exe"', 'py.exe', '"py"', 'py']:
+ with self.subTest(argv0):
+ data = self.run_py(["--version"], argv=f'{argv0} --version')
+ self.assertEqual("PythonTestSuite", data["SearchInfo.company"])
+ self.assertEqual("3.100", data["SearchInfo.tag"])
+ self.assertEqual(f'X.Y.exe --version', data["stdout"].strip())
+
def test_py_default_in_list(self):
data = self.run_py(["-0"], env=TEST_PY_ENV)
default = None
diff --git a/Misc/NEWS.d/next/Windows/2022-11-16-19-03-21.gh-issue-99442.6Dgk3Q.rst b/Misc/NEWS.d/next/Windows/2022-11-16-19-03-21.gh-issue-99442.6Dgk3Q.rst
new file mode 100644
index 0000000..8e19366
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2022-11-16-19-03-21.gh-issue-99442.6Dgk3Q.rst
@@ -0,0 +1,2 @@
+Fix handling in :ref:`launcher` when ``argv[0]`` does not include a file
+extension.
diff --git a/PC/launcher2.c b/PC/launcher2.c
index 5bcd2ba..9b3db04 100644
--- a/PC/launcher2.c
+++ b/PC/launcher2.c
@@ -491,62 +491,39 @@ dumpSearchInfo(SearchInfo *search)
int
-findArgumentLength(const wchar_t *buffer, int bufferLength)
+findArgv0Length(const wchar_t *buffer, int bufferLength)
{
- if (bufferLength < 0) {
- bufferLength = (int)wcsnlen_s(buffer, MAXLEN);
- }
- if (bufferLength == 0) {
- return 0;
- }
- const wchar_t *end;
- int i;
-
- if (buffer[0] != L'"') {
- end = wcschr(buffer, L' ');
- if (!end) {
- return bufferLength;
- }
- i = (int)(end - buffer);
- return i < bufferLength ? i : bufferLength;
- }
-
- i = 0;
- while (i < bufferLength) {
- end = wcschr(&buffer[i + 1], L'"');
- if (!end) {
- return bufferLength;
- }
-
- i = (int)(end - buffer);
- if (i >= bufferLength) {
- return bufferLength;
- }
-
- int j = i;
- while (j > 1 && buffer[--j] == L'\\') {
- if (j > 0 && buffer[--j] == L'\\') {
- // Even number, so back up and keep counting
- } else {
- // Odd number, so it's escaped and we want to keep searching
- continue;
+ // Note: this implements semantics that are only valid for argv0.
+ // Specifically, there is no escaping of quotes, and quotes within
+ // the argument have no effect. A quoted argv0 must start and end
+ // with a double quote character; otherwise, it ends at the first
+ // ' ' or '\t'.
+ int quoted = buffer[0] == L'"';
+ for (int i = 1; bufferLength < 0 || i < bufferLength; ++i) {
+ switch (buffer[i]) {
+ case L'\0':
+ return i;
+ case L' ':
+ case L'\t':
+ if (!quoted) {
+ return i;
}
- }
-
- // Non-escaped quote with space after it - end of the argument!
- if (i + 1 >= bufferLength || isspace(buffer[i + 1])) {
- return i + 1;
+ break;
+ case L'"':
+ if (quoted) {
+ return i + 1;
+ }
+ break;
}
}
-
return bufferLength;
}
const wchar_t *
-findArgumentEnd(const wchar_t *buffer, int bufferLength)
+findArgv0End(const wchar_t *buffer, int bufferLength)
{
- return &buffer[findArgumentLength(buffer, bufferLength)];
+ return &buffer[findArgv0Length(buffer, bufferLength)];
}
@@ -562,11 +539,16 @@ parseCommandLine(SearchInfo *search)
return RC_NO_COMMANDLINE;
}
- const wchar_t *tail = findArgumentEnd(search->originalCmdLine, -1);
- const wchar_t *end = tail;
- search->restOfCmdLine = tail;
+ const wchar_t *argv0End = findArgv0End(search->originalCmdLine, -1);
+ const wchar_t *tail = argv0End; // will be start of the executable name
+ const wchar_t *end = argv0End; // will be end of the executable name
+ search->restOfCmdLine = argv0End; // will be first space after argv0
while (--tail != search->originalCmdLine) {
- if (*tail == L'.' && end == search->restOfCmdLine) {
+ if (*tail == L'"' && end == argv0End) {
+ // Move the "end" up to the quote, so we also allow moving for
+ // a period later on.
+ end = argv0End = tail;
+ } else if (*tail == L'.' && end == argv0End) {
end = tail;
} else if (*tail == L'\\' || *tail == L'/') {
++tail;