From 701564de1927c230604ff02eb994a78bcba4295d Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 8 Jun 2022 18:17:08 -0700 Subject: [PATCH] Perform better fixups of NT paths in execve() This change ensures we do a better job translating /c/foo.bar paths into c:/foo.bar paths on Windows when generating the CreateProcess() cmd line thus fixing a regression that happened in the last two months when using the help() feature of Actually Portable Python in the CMD.EXE shell. --- libc/calls/mkntcmdline.c | 27 +++++++++++++++++++++++++-- libc/calls/mkntenvblock.c | 6 ++++-- libc/intrin/describestat.greg.c | 6 ++++-- test/libc/calls/mkntcmdline_test.c | 21 +++++++++++++++++---- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index 372c392eb..c09b5ea42 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -41,6 +41,10 @@ static bool NeedsQuotes(const char *s) { return false; } +static inline int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + /** * Converts System V argv to Windows-style command line. * @@ -86,8 +90,27 @@ textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], const char *prog, } } if (!x) break; - if (!i && x == '/') { - x = '\\'; + if (x == '/' || x == '\\') { + if (!i) { + // turn / into \ for first arg + x = '\\'; + // turn \c\... into c:\ for first arg + if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') { + cmdline[0] = cmdline[1]; + cmdline[1] = ':'; + } + } else { + // turn stuff like `less /c/...` + // into `less c:/...` + // turn stuff like `more <\\\"/c/...\\\"` + // into `more <\\\"c:/...\\\"` + if (k > 3 && IsAlpha(cmdline[k - 1]) && + (cmdline[k - 2] == '/' || cmdline[k - 2] == '\\') && + (cmdline[k - 3] == '"' || cmdline[k - 3] == ' ')) { + cmdline[k - 2] = cmdline[k - 1]; + cmdline[k - 1] = ':'; + } + } } if (x == '\\') { ++slashes; diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index e076efbe5..f68ad045b 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -72,12 +72,14 @@ static textwindows void FixPath(char *path) { // turn \c\... into c:\... p = path; - if (p[0] == '/' && IsAlpha(p[1]) && p[2] == '/') { + if ((p[0] == '/' | p[0] == '\\') && IsAlpha(p[1]) && + (p[2] == '/' || p[2] == '\\')) { p[0] = p[1]; p[1] = ':'; } for (; *p; ++p) { - if (p[0] == ';' && p[1] == '/' && IsAlpha(p[2]) && p[3] == '/') { + if (p[0] == ';' && (p[1] == '/' || p[1] == '\\') && IsAlpha(p[2]) && + (p[3] == '/' || p[3] == '\\')) { p[1] = p[2]; p[2] = ':'; } diff --git a/libc/intrin/describestat.greg.c b/libc/intrin/describestat.greg.c index cb2b8e08b..d2c1190f9 100644 --- a/libc/intrin/describestat.greg.c +++ b/libc/intrin/describestat.greg.c @@ -79,8 +79,10 @@ const char *DescribeStat(int rc, const struct stat *st) { i += ksnprintf(buf + i, n - i, ", .st_%s=%'lu", "blksize", st->st_blksize); } - buf[i++] = '}'; - buf[i] = 0; + if (n - i >= 2) { + buf[i + 0] = '}'; + buf[i + 1] = 0; + } return buf; } diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index db1b4afe8..bfd07a782 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -78,16 +78,29 @@ TEST(mkntcmdline, testUnicode) { cmdline); } -TEST(mkntcmdline, fix) { +TEST(mkntcmdline, fixAsBestAsWeCanForNow1) { char *argv1[] = { - "C:/WINDOWS/system32/cmd.exe", + "/C/WINDOWS/system32/cmd.exe", "/C", - "more < \"C:\\Users\\jart\\AppData\\Local\\Temp\\tmplquaa_d6\"", + "more < \"/C/Users/jart/AppData/Local/Temp/tmplquaa_d6\"", NULL, }; EXPECT_NE(-1, mkntcmdline(cmdline, argv1[0], argv1)); EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"more < " - u"\\\"C:\\Users\\jart\\AppData\\Local\\Temp\\tmplquaa_d6\\\"\"", + u"\\\"C:/Users/jart/AppData/Local/Temp/tmplquaa_d6\\\"\"", + cmdline); +} + +TEST(mkntcmdline, fixAsBestAsWeCanForNow2) { + char *argv1[] = { + "/C/WINDOWS/system32/cmd.exe", + "/C", + "less /C/Users/jart/AppData/Local/Temp/tmplquaa_d6", + NULL, + }; + EXPECT_NE(-1, mkntcmdline(cmdline, argv1[0], argv1)); + EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"less " + u"C:/Users/jart/AppData/Local/Temp/tmplquaa_d6\"", cmdline); }