Implement •SH

This commit is contained in:
vylsaz 2024-02-03 01:45:56 +00:00
parent 1e8c390e6a
commit 74d3620839
2 changed files with 289 additions and 0 deletions

View File

@ -1169,6 +1169,100 @@ B sh_c2(B t, B w, B x) {
: -1;
return m_hvec3(m_i32(code), s_outObj, s_errObj);
}
#elif defined(_WIN32) || defined(_WIN64)
#define HAS_SH 1
#include "../windows/winError.c"
#include "../windows/sh.c"
B sh_c2(B t, B w, B x) {
// parse options
B inObj = bi_N;
bool raw = false;
if (!q_N(w)) {
if (!isNsp(w)) thrM("•SH: 𝕨 must be a namespace");
inObj = ns_getC(w, "stdin");
if (!q_N(inObj) && !isArr(inObj)) thrM("•SH: Invalid stdin value");
B rawObj = ns_getC(w, "raw");
if (!q_N(rawObj)) raw = o2b(rawObj);
}
u64 iLen = q_N(inObj)? 0 : (raw? IA(inObj) : utf8lenB(inObj));
// allocate args
if (isAtm(x) || RNK(x)>1) thrM("•SH: 𝕩 must be a vector of strings");
usz xia = IA(x);
if (xia==0) thrM("•SH: 𝕩 must have at least one item");
u64 arglen = 0;
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 vector 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);
}
// allocate stdin
char* iBuf;
CharBuf iBufRaw;
if (iLen>0) {
if (raw) {
iBufRaw = get_chars(inObj);
iBuf = iBufRaw.data;
} else {
iBuf = TALLOCP(char, iLen);
toUTF8(inObj, iBuf);
}
} else iBuf = "";
DWORD code = -1;
u64 oLen = 0; char* oBuf;
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)
if (dwResult != ERROR_SUCCESS) {
thrF("•SH: Failed to run command: %S", winErrorEx(dwResult));
}
u8* op;
B s_out = m_c8arrv(&op, oLen);
if (oLen > 0 && oBuf != NULL) {
memcpy(op, oBuf, oLen*sizeof(char)); free(oBuf);
}
u8* ep;
B s_err = m_c8arrv(&ep, eLen);
if (eLen > 0 && eBuf != NULL) {
memcpy(ep, eBuf, eLen*sizeof(char)); free(eBuf);
}
dec(w); dec(x);
B s_outRaw = toC8Any(s_out);
B s_errRaw = toC8Any(s_err);
B s_outObj;
B s_errObj;
if (raw) {
s_outObj = s_outRaw;
s_errObj = s_errRaw;
} else {
s_outObj = utf8Decode((char*)c8any_ptr(s_outRaw), IA(s_outRaw)); dec(s_outRaw);
s_errObj = utf8Decode((char*)c8any_ptr(s_errRaw), IA(s_errRaw)); dec(s_errRaw);
}
return m_hvec3(m_i32((i32)code), s_outObj, s_errObj);
}
#else
#if FOR_BUILD
#if __ANDROID__

195
src/windows/sh.c Normal file
View File

@ -0,0 +1,195 @@
#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;
}
char *dataInp, *dataOut, *dataErr;
u64 sizeInp, sizeOut, sizeErr;
static DWORD WINAPI winThreadWriteIn(LPVOID arg) {
DWORD dwResult = ERROR_SUCCESS;
HANDLE hndl = (HANDLE)arg;
char *wBuf = dataInp;
DWORD dwToWrite = sizeInp, dwWritten = 0, dwOff = 0;
for (;;) {
BOOL bOk = WriteFile(hndl, &wBuf[dwOff], dwToWrite-dwOff, &dwWritten, NULL);
if (!bOk) { dwResult = GetLastError(); break; }
dwOff += dwWritten;
if (dwOff >= dwToWrite) { break; }
}
CloseHandle(hndl);
return dwResult;
}
#define winThreadRead(D) \
static DWORD WINAPI winThreadRead##D(LPVOID arg) { \
DWORD dwResult = ERROR_SUCCESS; \
HANDLE hndl = (HANDLE)arg; \
u8 buf[1024] = {0}; \
const usz bufSize = sizeof(buf)/sizeof(u8); \
DWORD dwRead = 0, dwHasRead = 0; \
char *rBuf = NULL; \
for (;;) { \
ZeroMemory(buf, bufSize); \
BOOL bOk = ReadFile(hndl, buf, bufSize, &dwRead, NULL); \
if (!bOk) { \
DWORD dwErr = GetLastError(); \
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; \
} \
data##D = rBuf; \
size##D = dwHasRead; \
CloseHandle(hndl); \
return dwResult; \
}
winThreadRead(Out)
winThreadRead(Err)
#undef winThreadRead
static DWORD winCmd(char* arg,
u64 iLen, char* iBuf,
DWORD* code,
u64* oLen, char** oBuf,
u64* eLen, char** eBuf) {
DWORD dwResult = ERROR_SUCCESS;
HANDLE hInpR, hInpW;
HANDLE hOutR, hOutW;
HANDLE hErrR, hErrW;
// Create pipes
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
CreatePipe(&hInpR, &hInpW, &sa, 0);
CreatePipe(&hOutR, &hOutW, &sa, 0);
CreatePipe(&hErrR, &hErrW, &sa, 0);
SetHandleInformation(hInpW, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hOutR, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hErrR, HANDLE_FLAG_INHERIT, 0);
// Set up
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdInput = hInpR;
si.hStdOutput = hOutW;
si.hStdError = hErrW;
si.dwFlags |= STARTF_USESTDHANDLES | STARTF_UNTRUSTEDSOURCE;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
// Create the child process
BOOL bSuccess = CreateProcessA(NULL, arg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
if (!bSuccess) { return GetLastError(); }
// Close the unneeded handles
CloseHandle(hInpR);
CloseHandle(hOutW);
CloseHandle(hErrW);
// Now we are dealing with the globals and threads
dataInp = iBuf;
sizeInp = iLen;
DWORD exitCode = -1;
HANDLE lpThreads[3];
lpThreads[0] = CreateThread(NULL, 0, winThreadWriteIn, (LPVOID)hInpW, 0, NULL);
lpThreads[1] = CreateThread(NULL, 0, winThreadReadOut, (LPVOID)hOutR, 0, NULL);
lpThreads[2] = CreateThread(NULL, 0, winThreadReadErr, (LPVOID)hErrR, 0, NULL);
for (int i = 0; i < 3; ++i) {
if (lpThreads[i] == NULL) { dwResult = GetLastError(); goto error; }
}
// Wait for the threads
if (WaitForMultipleObjects(3, lpThreads, TRUE, INFINITE) == WAIT_FAILED) {
dwResult = GetLastError(); goto error;
}
for (int i = 0; i < 3; ++i) {
GetExitCodeThread(lpThreads[i], &exitCode);
if (exitCode != ERROR_SUCCESS) { dwResult = exitCode; goto error; }
}
// Get the exit code
if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) {
dwResult = GetLastError(); goto error;
}
exitCode = -1;
GetExitCodeProcess(pi.hProcess, &exitCode);
// Give outputs
*code = exitCode;
*oLen = sizeOut; *oBuf = dataOut;
*eLen = sizeErr; *eBuf = dataErr;
error:
// Close handles
for (int i = 0; i < 3; ++i) { CloseHandle(lpThreads[i]); }
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return dwResult;
}