From 67c2850e38981b2565c7827f3f68b4a14d793ae1 Mon Sep 17 00:00:00 2001 From: vylsaz Date: Tue, 7 Jan 2025 03:02:16 +0000 Subject: [PATCH] Windows: sh using utf16; simply argument quoting --- src/builtins/sysfn.c | 52 ++++++++++++++++++----------- src/windows/sh.c | 79 +++++++++----------------------------------- 2 files changed, 47 insertions(+), 84 deletions(-) diff --git a/src/builtins/sysfn.c b/src/builtins/sysfn.c index 10f758d9..b6e59639 100644 --- a/src/builtins/sysfn.c +++ b/src/builtins/sysfn.c @@ -1154,31 +1154,43 @@ static i32 sh_core(bool raw, B x, usz xia, B inObj, u64 iLen, B* s_outp, B* s_er #elif defined(_WIN32) || defined(_WIN64) #define HAS_SH 1 #include "../windows/winError.c" +#include "../windows/utf16.c" #include "../windows/sh.c" static i32 sh_core(bool raw, B x, usz xia, B inObj, u64 iLen, B* s_outp, B* s_errp) { // allocate args - u64 arglen = 0; + TSALLOC(WCHAR, arg, 8); SGetU(x) for (u64 i = 0; i < xia; i++) { B c = GetU(x, i); if (isAtm(c) || RNK(c)!=1) thrM("•SH: 𝕩 must be a list of strings"); - u64 len = utf8lenB(c); - arglen += 1+2+2*len; - // space or 0, quotes, worst-case scenario (every character needs escaping) - } - TALLOC(char, arg, arglen); - char* pos = arg; - for (u64 i = 0; i < xia; i++) { - B c = GetU(x, i); - u64 len = utf8lenB(c); - TALLOC(char, cstr, len+1); - toUTF8(c, cstr); - cstr[len] = 0; - pos = winQuoteCmdArg(len, cstr, pos); - *(pos++) = (xia==i+1)? '\0' : ' '; - TFREE(cstr) - assert(pos <= arg+arglen); + u64 len = utf16lenB(c); + TALLOC(WCHAR, wstr, len); + toUTF16(c, wstr); + + // https://learn.microsoft.com/en-gb/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way + u64 backslashes = 0; + bool quote = len==0 || NULL!=wcspbrk(wstr, L" \t\n\v\""); + if (quote) { TSADD(arg, L'\"'); } + for (u64 j = 0; j < len; ++j) { + WCHAR x = wstr[j]; + if (x==L'\\') { + backslashes += 1; + } else { + if (x==L'\"') { + for (u64 k = 0; k < 1+backslashes; ++k) { TSADD(arg, L'\\'); } + } + backslashes = 0; + } + TSADD(arg, x); + } + if (quote) { + for (u64 k = 0; k < backslashes; ++k) { TSADD(arg, L'\\'); } + TSADD(arg, L'\"'); + } + + TSADD(arg, (xia==i+1)? L'\0' : L' '); + TFREE(wstr); } // allocate stdin @@ -1200,7 +1212,7 @@ static i32 sh_core(bool raw, B x, usz xia, B inObj, u64 iLen, B* s_outp, B* s_er u64 eLen = 0; char* eBuf; DWORD dwResult = winCmd(arg, iLen, iBuf, &code, &oLen, &oBuf, &eLen, &eBuf); if (iLen>0) { if (raw) free_chars(iBufRaw); else TFREE(iBuf); } // FREE_INPUT - TFREE(arg) + TSFREE(arg); if (dwResult != ERROR_SUCCESS) { thrF("•SH: Failed to run command: %S", winErrorEx(dwResult)); } @@ -1208,8 +1220,8 @@ static i32 sh_core(bool raw, B x, usz xia, B inObj, u64 iLen, B* s_outp, B* s_er // prepare output u8* op; *s_outp = m_c8arrv(&op, oLen); u8* ep; *s_errp = m_c8arrv(&ep, eLen); - if (oLen > 0 && oBuf != NULL) memcpy(op, oBuf, oLen*sizeof(char)); free(oBuf); - if (eLen > 0 && eBuf != NULL) memcpy(ep, eBuf, eLen*sizeof(char)); free(eBuf); + if (oBuf!=NULL) { memcpy(op, oBuf, oLen*sizeof(char)); TFREE(oBuf); } + if (eBuf!=NULL) { memcpy(ep, eBuf, eLen*sizeof(char)); TFREE(eBuf); } return (i32)code; } #else diff --git a/src/windows/sh.c b/src/windows/sh.c index 56654a53..66fbe6a2 100644 --- a/src/windows/sh.c +++ b/src/windows/sh.c @@ -1,48 +1,5 @@ #include - -// https://github.com/libuv/libuv/blob/v1.23.0/src/win/process.c#L454-L524 - -static char* winQuoteCmdArg(u64 len, char* source, char* target) { - if (len == 0) { - // Need double quotation for empty argument - *(target++) = '"'; - *(target++) = '"'; - return target; - } - if (NULL == strpbrk(source, " \t\"")) { - // No quotation needed - memcpy(target, source, len * sizeof(char)); target += len; - return target; - } - if (NULL == strpbrk(source, "\"\\")) { - // No embedded double quotes or backlashes, so I can just wrap - // quote marks around the whole thing. - *(target++) = '"'; - memcpy(target, source, len * sizeof(char)); target += len; - *(target++) = '"'; - return target; - } - - *(target++) = '"'; - char *start = target; - int quote_hit = 1; - - for (u64 i = 0; i < len; ++i) { - *(target++) = source[len - 1 - i]; - - if (quote_hit && source[len - 1 - i] == '\\') { - *(target++) = '\\'; - } else if (source[len - 1 - i] == '"') { - quote_hit = 1; - *(target++) = '\\'; - } else { - quote_hit = 0; - } - } - target[0] = '\0'; _strrev(start); - *(target++) = '"'; - return target; -} +#include "../utils/talloc.h" typedef struct { HANDLE hndl; @@ -74,8 +31,8 @@ static DWORD WINAPI winThreadRead(LPVOID arg0) { HANDLE hndl = arg->hndl; u8 buf[1024] = {0}; const usz bufSize = sizeof(buf)/sizeof(u8); - DWORD dwRead = 0, dwHasRead = 0; - char* rBuf = NULL; + DWORD dwRead = 0; + TSALLOC(char, rBuf, 8); for (;;) { ZeroMemory(buf, bufSize); @@ -85,26 +42,20 @@ static DWORD WINAPI winThreadRead(LPVOID arg0) { if (dwErr == ERROR_BROKEN_PIPE) { break; } else { dwResult = dwErr; break; } } - char* newBuf = (rBuf==NULL)? - calloc(dwHasRead+dwRead, sizeof(char)) : - realloc(rBuf, (dwHasRead+dwRead)*sizeof(char)); - if (newBuf == NULL) { dwResult = GetLastError(); break; } - rBuf = newBuf; - memcpy(&rBuf[dwHasRead], buf, dwRead); - dwHasRead += dwRead; + TSADDA(rBuf, buf, dwRead); } - if (dwResult != ERROR_SUCCESS) { - if (dwHasRead > 0 && rBuf != NULL) { free(rBuf); } - } else { - arg->buf = rBuf; - arg->len = dwHasRead; + if (dwResult == ERROR_SUCCESS) { + arg->len = TSSIZE(rBuf); + arg->buf = TALLOCP(char, arg->len); + memcpy(arg->buf, rBuf, arg->len); } + TSFREE(rBuf); CloseHandle(hndl); return dwResult; } -static DWORD winCmd(char* arg, +static DWORD winCmd(WCHAR* arg, u64 iLen, char* iBuf, DWORD* code, u64* oLen, char** oBuf, @@ -117,7 +68,7 @@ static DWORD winCmd(char* arg, // Create pipes SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; @@ -130,9 +81,9 @@ static DWORD winCmd(char* arg, SetHandleInformation(hErrR, HANDLE_FLAG_INHERIT, 0); // Set up - STARTUPINFO si; - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); + STARTUPINFOW si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); si.hStdInput = hInpR; si.hStdOutput = hOutW; si.hStdError = hErrW; @@ -142,7 +93,7 @@ static DWORD winCmd(char* arg, ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); // Create the child process - BOOL bSuccess = CreateProcessA(NULL, arg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); + BOOL bSuccess = CreateProcessW(NULL, arg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); if (!bSuccess) { return GetLastError(); } // Close the unneeded handles