diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index f0cfc0e9d..3352425d4 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -16,11 +16,17 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/limits.h" #include "libc/mem/mem.h" +#include "libc/nt/files.h" #include "libc/proc/ntspawn.h" #include "libc/str/str.h" #include "libc/str/thompike.h" #include "libc/str/utf16.h" +#include "libc/sysv/consts/at.h" #include "libc/sysv/errfuns.h" #define APPEND(c) \ @@ -54,6 +60,12 @@ static inline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } +static bool LooksLikeCosmoDrivePath(const char *s) { + return s[0] == '/' && // + IsAlpha(s[1]) && // + s[2] == '/'; +} + // Converts System V argv to Windows-style command line. // // Escaping is performed and it's designed to round-trip with @@ -68,20 +80,31 @@ static inline int IsAlpha(int c) { // @see libc/runtime/getdosargv.c // @asyncsignalsafe textwindows int mkntcmdline(char16_t cmdline[32767], char *const argv[]) { + char *arg; int slashes, n; bool needsquote; size_t i, j, k, s; + char argbuf[PATH_MAX]; for (k = i = 0; argv[i]; ++i) { if (i) APPEND(u' '); - if ((needsquote = NeedsQuotes(argv[i]))) APPEND(u'"'); + if (LooksLikeCosmoDrivePath(argv[i]) && + strlcpy(argbuf, argv[i], PATH_MAX) < PATH_MAX) { + mungentpath(argbuf); + arg = argbuf; + } else { + arg = argv[i]; + } + if ((needsquote = NeedsQuotes(arg))) { + APPEND(u'"'); + } for (slashes = j = 0;;) { - wint_t x = argv[i][j++] & 255; + wint_t x = arg[j++] & 255; if (x >= 0300) { n = ThomPikeLen(x); x = ThomPikeByte(x); while (--n) { wint_t y; - if ((y = argv[i][j++] & 255)) { + if ((y = arg[j++] & 255)) { x = ThomPikeMerge(x, y); } else { x = 0; @@ -90,28 +113,6 @@ textwindows int mkntcmdline(char16_t cmdline[32767], char *const argv[]) { } } if (!x) break; - if (x == '/' || x == '\\') { - if (!i) { - // turn / into \ for first argv[i] - x = '\\'; - // turn \c\... into c:\ for first argv[i] - 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; } else if (x == '"') { diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index cd679577f..2480205c8 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/intrin/getenv.internal.h" #include "libc/mem/alloca.h" +#include "libc/proc/ntspawn.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/str/str.h" @@ -51,38 +52,6 @@ static textwindows int Compare(const char *l, const char *r) { return a - b; } -static textwindows void FixPath(char *path) { - char *p; - - // turn colon into semicolon - // unless it already looks like a dos path - for (p = path; *p; ++p) { - if (p[0] == ':' && p[1] != '\\') { - p[0] = ';'; - } - } - - // turn /c/... into c:\... - p = path; - if (p[0] == '/' && IsAlpha(p[1]) && p[2] == '/') { - p[0] = p[1]; - p[1] = ':'; - } - for (; *p; ++p) { - if (p[0] == ';' && p[1] == '/' && IsAlpha(p[2]) && p[3] == '/') { - p[1] = p[2]; - p[2] = ':'; - } - } - - // turn slash into backslash - for (p = path; *p; ++p) { - if (*p == '/') { - *p = '\\'; - } - } -} - static textwindows int InsertString(struct EnvBuilder *env, const char *str) { int c, i, cmp; char *var, *path = 0; @@ -101,7 +70,7 @@ static textwindows int InsertString(struct EnvBuilder *env, const char *str) { } while (c); // fixup key=/c/... → key=c:\... - if (path) FixPath(path); + if (path) mungentpath(path); // append key=val to sorted list using insertion sort technique for (i = env->vari;; --i) { diff --git a/libc/calls/mungentpath.c b/libc/calls/mungentpath.c new file mode 100644 index 000000000..790d996e1 --- /dev/null +++ b/libc/calls/mungentpath.c @@ -0,0 +1,55 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/proc/ntspawn.h" + +static inline int IsAlpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); +} + +textwindows void mungentpath(char *path) { + char *p; + + // turn colon into semicolon + // unless it already looks like a dos path + for (p = path; *p; ++p) { + if (p[0] == ':' && p[1] != '\\') { + p[0] = ';'; + } + } + + // turn /c/... into c:\... + p = path; + if (p[0] == '/' && IsAlpha(p[1]) && p[2] == '/') { + p[0] = p[1]; + p[1] = ':'; + } + for (; *p; ++p) { + if (p[0] == ';' && p[1] == '/' && IsAlpha(p[2]) && p[3] == '/') { + p[1] = p[2]; + p[2] = ':'; + } + } + + // turn slash into backslash + for (p = path; *p; ++p) { + if (*p == '/') { + *p = '\\'; + } + } +} diff --git a/libc/proc/ntspawn.h b/libc/proc/ntspawn.h index 3ec1c12b2..9c9d154ac 100644 --- a/libc/proc/ntspawn.h +++ b/libc/proc/ntspawn.h @@ -5,6 +5,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ +void mungentpath(char *); int mkntcmdline(char16_t[32767], char *const[]); int mkntenvblock(char16_t[32767], char *const[], char *const[], char[32767]); int ntspawn(int64_t, const char *, char *const[], char *const[], char *const[], diff --git a/test/libc/calls/mkntcmdline_test.c b/test/libc/calls/mkntcmdline_test.c index 6b923f2f8..3627a38bf 100644 --- a/test/libc/calls/mkntcmdline_test.c +++ b/test/libc/calls/mkntcmdline_test.c @@ -74,30 +74,14 @@ TEST(mkntcmdline, testUnicode) { cmdline); } -TEST(mkntcmdline, fixAsBestAsWeCanForNow1) { +TEST(mkntcmdline, fixit) { char *argv1[] = { - "/C/WINDOWS/system32/cmd.exe", - "/C", - "more <\"/C/Users/jart/AppData/Local/Temp/tmplquaa_d6\"", + "/C/Program Files/doom/doom.exe", + "--version", NULL, }; EXPECT_NE(-1, mkntcmdline(cmdline, argv1)); - EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"more <" - 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)); - EXPECT_STREQ(u"C:\\WINDOWS\\system32\\cmd.exe /C \"less " - u"C:/Users/jart/AppData/Local/Temp/tmplquaa_d6\"", - cmdline); + EXPECT_STREQ(u"\"C:\\Program Files\\doom\\doom.exe\" --version", cmdline); } TEST(mkntcmdline, testWut) {