Windows: sh using utf16; simply argument quoting

This commit is contained in:
vylsaz 2025-01-07 03:02:16 +00:00 committed by dzaima
parent 580f4a3a19
commit 67c2850e38
2 changed files with 47 additions and 84 deletions

View File

@ -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

View File

@ -1,48 +1,5 @@
#include <windows.h>
// 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