Major redesign of the Exec API, in order to make linux more uniform with windows and introduce python shell
This commit is contained in:
@@ -85,6 +85,14 @@
|
||||
#define IF_DEBUG(x)
|
||||
#endif
|
||||
|
||||
#if OS_WINDOWS
|
||||
#define IF_OS_WINDOWS_ELSE(x, y) x
|
||||
#define IF_OS_WINDOWS(x) x
|
||||
#else
|
||||
#define IF_OS_WINDOWS_ELSE(x, y) y
|
||||
#define IF_OS_WINDOWS(x)
|
||||
#endif
|
||||
|
||||
#if OS_WINDOWS
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
|
||||
@@ -257,204 +257,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
|
||||
return it;
|
||||
}
|
||||
|
||||
struct UnixProcess {
|
||||
pid_t pid;
|
||||
int child_stdout_read;
|
||||
int stdin_write;
|
||||
};
|
||||
|
||||
API Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Scratch scratch;
|
||||
const int PIPE_READ = 0;
|
||||
const int PIPE_WRITE = 1;
|
||||
bool error = false;
|
||||
|
||||
working_dir = Copy(scratch, working_dir);
|
||||
chdir(working_dir.data);
|
||||
|
||||
Process process = {};
|
||||
UnixProcess *plat = (UnixProcess *)&process.platform;
|
||||
Array<char *> args = SplitCommand(scratch, command_line);
|
||||
Array<char *> env = {scratch};
|
||||
|
||||
For (enviroment) {
|
||||
Add(&env, Copy(scratch, it).data);
|
||||
}
|
||||
|
||||
int stdout_desc[2] = {};
|
||||
int stdin_desc[2] = {};
|
||||
posix_spawn_file_actions_t actions = {};
|
||||
|
||||
if (posix_spawn_file_actions_init(&actions) != 0) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_init, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
};
|
||||
|
||||
if (pipe(stdout_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdout_desc[PIPE_READ]);
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
}
|
||||
};
|
||||
|
||||
if (pipe(stdin_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
close(stdin_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
}
|
||||
};
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdout_desc[PIPE_READ]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDOUT_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDERR_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDIN_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
pid_t process_pid = 0;
|
||||
error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: failed to create process\n, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
plat->child_stdout_read = stdout_desc[PIPE_READ];
|
||||
plat->stdin_write = stdin_desc[PIPE_WRITE];
|
||||
plat->pid = process_pid;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
process.id = process_pid;
|
||||
process.is_valid = true;
|
||||
return process;
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)&process->platform;
|
||||
if (process->is_valid == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0) {
|
||||
pid_t result = waitpid(plat->pid, &status, 0);
|
||||
Assert(result != -1);
|
||||
process->exit_code = WEXITSTATUS(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
kill(plat->pid, SIGKILL);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
|
||||
String result = {};
|
||||
result.data = AllocArray(allocator, char, 16 * 4096);
|
||||
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLIN;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0 || force_read) {
|
||||
result.len = read(plat->child_stdout_read, result.data, 4 * 4096);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
ssize_t size = write(plat->stdin_write, string.data, string.len);
|
||||
|
||||
Assert(size == string.len);
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
close(plat->stdin_write);
|
||||
}
|
||||
|
||||
#elif OS_WINDOWS
|
||||
|
||||
@@ -725,210 +527,6 @@ API MakeDirResult MakeDir(String path) {
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Win32Process {
|
||||
HANDLE handle;
|
||||
HANDLE child_stdout_read;
|
||||
HANDLE child_stdout_write;
|
||||
HANDLE child_stdin_read;
|
||||
HANDLE child_stdin_write;
|
||||
};
|
||||
// static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||
|
||||
static void Win32ReportError(String msg, String cmd) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
defer { LocalFree(lpMsgBuf); };
|
||||
char *buff = (char *)lpMsgBuf;
|
||||
size_t buffLen = strlen((const char *)buff);
|
||||
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
|
||||
Error("%S: %S! %s", msg, cmd, (char *)lpMsgBuf);
|
||||
}
|
||||
|
||||
static void Win32CloseProcess(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
|
||||
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
|
||||
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
|
||||
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
|
||||
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
|
||||
process->is_valid = false;
|
||||
}
|
||||
|
||||
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||
Win32ReportError(msg, cmd);
|
||||
Win32CloseProcess(process);
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD written = 0;
|
||||
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
|
||||
|
||||
Assert(write_error == false);
|
||||
Assert(written == string.len);
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
CloseHandle(p->child_stdin_write);
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Process process = {};
|
||||
Win32Process *p = (Win32Process *)process.platform;
|
||||
|
||||
Scratch scratch;
|
||||
command_line = Format(scratch, "/C %S", command_line);
|
||||
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
SECURITY_ATTRIBUTES security_atrb = {};
|
||||
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_atrb.bInheritHandle = TRUE;
|
||||
|
||||
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
STARTUPINFOW startup = {};
|
||||
startup.cb = sizeof(STARTUPINFO);
|
||||
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
startup.hStdInput = p->child_stdin_read;
|
||||
startup.hStdOutput = p->child_stdout_write;
|
||||
startup.hStdError = p->child_stdout_write;
|
||||
startup.wShowWindow = SW_HIDE;
|
||||
|
||||
String16 cwd = ToString16(scratch, working_dir);
|
||||
String16 cmd = ToString16(scratch, command_line);
|
||||
|
||||
char *env = NULL;
|
||||
if (enviroment.len) {
|
||||
Int size = GetSize(enviroment) + enviroment.len + 1;
|
||||
env = AllocArray(scratch, char, size);
|
||||
Int i = 0;
|
||||
For(enviroment) {
|
||||
MemoryCopy(env + i, it.data, it.len);
|
||||
i += it.len;
|
||||
|
||||
env[i++] = 0;
|
||||
}
|
||||
env[i++] = 0;
|
||||
}
|
||||
|
||||
DWORD dwCreationFlags = 0;
|
||||
BOOL bInheritHandles = TRUE;
|
||||
|
||||
PROCESS_INFORMATION info = {};
|
||||
if (!CreateProcessW(L"c:\\windows\\system32\\cmd.exe", (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
|
||||
Win32ProcessError(&process, "failed to create process", command_line);
|
||||
return process;
|
||||
}
|
||||
|
||||
// Close handles to the stdin and stdout pipes no longer needed by the child process.
|
||||
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
|
||||
CloseHandle(info.hThread);
|
||||
CloseHandle(p->child_stdin_read);
|
||||
CloseHandle(p->child_stdout_write);
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
p->handle = info.hProcess;
|
||||
process.is_valid = true;
|
||||
process.id = (int64_t)p->handle;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
if (process->is_valid == false) return false;
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
|
||||
if (get_exit_code_failed) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
process->exit_code = (int)exit_code;
|
||||
Win32CloseProcess(process);
|
||||
return false;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
|
||||
if (terminate_process_error) {
|
||||
Assert(0);
|
||||
}
|
||||
Win32CloseProcess(process);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
DWORD bytes_avail = 0;
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
if (peek_error) {
|
||||
return {};
|
||||
} else if (bytes_avail == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
|
||||
char *buffer = AllocArray(allocator, char, buffer_size);
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||
if (read_error) {
|
||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||
Dealloc(allocator, buffer);
|
||||
Win32CloseProcess(process);
|
||||
return {};
|
||||
}
|
||||
|
||||
String result = {buffer, bytes_read};
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif OS_WASM
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -1122,54 +720,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
|
||||
return it;
|
||||
}
|
||||
|
||||
API Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
API Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
return {};
|
||||
}
|
||||
|
||||
API bool IsValid(Process *process) {
|
||||
return false;
|
||||
}
|
||||
|
||||
API void KillProcess(Process *process) {
|
||||
}
|
||||
|
||||
API String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
return {};
|
||||
}
|
||||
|
||||
API void WriteStdin(Process *process, String string) {
|
||||
return;
|
||||
}
|
||||
|
||||
API void CloseStdin(Process *process) {
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
API double GetTimeSeconds() {
|
||||
|
||||
@@ -33,22 +33,9 @@ String GetWorkingDir(Allocator arena);
|
||||
bool IsAbsolute(String path);
|
||||
int64_t GetFileModTime(String file);
|
||||
|
||||
struct Process {
|
||||
bool is_valid;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
int64_t id;
|
||||
|
||||
int64_t view_id; // text editor view
|
||||
bool scroll_to_end;
|
||||
};
|
||||
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin = {}, Array<String> enviroment = {});
|
||||
bool IsValid(Process *process);
|
||||
void KillProcess(Process *process);
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read);
|
||||
void WriteStdin(Process *process, String string);
|
||||
void CloseStdin(Process *process);
|
||||
|
||||
double GetTimeMicros(void);
|
||||
|
||||
enum MakeDirResult {
|
||||
|
||||
@@ -34,14 +34,18 @@ void LayoutBuildWindow(Rect2I *rect, int16_t wx, int16_t wy) {
|
||||
|
||||
BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectDirectory) {
|
||||
SaveAll();
|
||||
Scratch scratch;
|
||||
BSet build = GetBSet(BuildWindowID);
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
SelectRange(build.view, Range{});
|
||||
ResetBuffer(build.buffer);
|
||||
if (OS_WINDOWS) {
|
||||
Exec(build.view->id, true, windows_cmd, working_dir);
|
||||
} else {
|
||||
Exec(build.view->id, true, unix_cmd, working_dir);
|
||||
{
|
||||
ExecArgs args = ShellArgs(scratch, build.view->id, windows_cmd, working_dir);
|
||||
args.scroll_to_end = true;
|
||||
if (!OS_WINDOWS) {
|
||||
args.cmd = unix_cmd;
|
||||
}
|
||||
Exec(args);
|
||||
}
|
||||
main.window->active_goto_list = build.view->id;
|
||||
main.window->goto_list_pos = 0;
|
||||
|
||||
@@ -5,7 +5,10 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
String working_dir = ProjectDirectory;
|
||||
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
|
||||
String cmd = Format(scratch, "\"%S\" && set", VCVarsPath);
|
||||
view = ExecHidden(buffer_name, cmd, working_dir);
|
||||
view = OpenBufferView(buffer_name);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
buffer->dont_try_to_save_in_bulk_ops = true;
|
||||
Exec(ShellArgs(scratch, view->id, cmd, working_dir));
|
||||
}
|
||||
for (;;) {
|
||||
if (!ProcessIsActive(view->id)) {
|
||||
@@ -19,6 +22,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
String16 string16 = GetString(buffer);
|
||||
String string = ToString(scratch, string16);
|
||||
Array<String> lines = Split(scratch, string, "\n");
|
||||
ProcessEnviroment.len = 0;
|
||||
For (lines) {
|
||||
String s = Trim(it);
|
||||
Array<String> values = Split(scratch, s, "=");
|
||||
@@ -27,8 +31,6 @@ void Windows_SetupVCVarsall(mco_coro *co) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LoadVCVars() {
|
||||
RemoveCoroutine("Windows_SetupVCVarsall");
|
||||
CCtx *co_data = AddCoroutine(Windows_SetupVCVarsall);
|
||||
|
||||
@@ -2141,7 +2141,9 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
|
||||
String session_name = Format(ctx->arena, "te%llu", GetTimeNanos());
|
||||
String remedy_string = Format(ctx->arena, "%S --servername %S", RemedyBGPath, session_name);
|
||||
ReportConsolef("Remedybg: %S", remedy_string);
|
||||
Exec(LogView->id, true, remedy_string, GetPrimaryDirectory());
|
||||
ExecArgs args = ShellArgs(ctx->arena, LogView->id, remedy_string, GetPrimaryDirectory());
|
||||
args.scroll_to_end = true;
|
||||
Exec(args);
|
||||
MemoryZero(&RDBG_Ctx, sizeof(RDBG_Ctx));
|
||||
RDBG_Ctx.cmd.data = command_buf;
|
||||
RDBG_Ctx.cmd.capacity = sizeof(command_buf);
|
||||
|
||||
@@ -74,7 +74,7 @@ void UpdateStatusWindow() {
|
||||
commands = Format(scratch, "%S :Reopen", commands);
|
||||
}
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == main.view->id.id) {
|
||||
if (it.args.output_view == main.view->id) {
|
||||
commands = Format(scratch, "%S :KillProcess", commands);
|
||||
break;
|
||||
}
|
||||
@@ -82,7 +82,7 @@ void UpdateStatusWindow() {
|
||||
|
||||
String s = Format(scratch, "%S:%lld:%lld %S | :Prev :Next :Close %S", main.buffer->name, (long long)xy.line + 1ll, (long long)xy.col + 1ll, indicators, commands);
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == main.view->id.id) {
|
||||
if (it.args.output_view == main.view->id) {
|
||||
s = Format(scratch, "%S %lld :KillProcess", s, (long long)it.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,20 @@ void CMD_FocusWindow3() {
|
||||
}
|
||||
} RegisterCommand(CMD_FocusWindow3, "ctrl-3", "Select the 3rd window, counting from left");
|
||||
|
||||
void CMD_FocusWindow4() {
|
||||
Window *first = GetOverlappingWindow({0,0});
|
||||
if (first) {
|
||||
Window *second = GetOverlappingWindow(GetSideOfWindow(first, DIR_RIGHT));
|
||||
if (second) {
|
||||
Window *third = GetOverlappingWindow(GetSideOfWindow(second, DIR_RIGHT));
|
||||
if (third) {
|
||||
Window *fourth = GetOverlappingWindow(GetSideOfWindow(third, DIR_RIGHT));
|
||||
if (fourth) NextActiveWindowID = third->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
} RegisterCommand(CMD_FocusWindow4, "ctrl-4", "Select the 4th window, counting from left");
|
||||
|
||||
void CMD_NewWindow() {
|
||||
CreateWind();
|
||||
} RegisterCommand(CMD_NewWindow, "ctrl-backslash", "Creates a new window");
|
||||
|
||||
@@ -1,3 +1,456 @@
|
||||
#if OS_WINDOWS
|
||||
struct Win32Process {
|
||||
HANDLE handle;
|
||||
HANDLE child_stdout_read;
|
||||
HANDLE child_stdout_write;
|
||||
HANDLE child_stdin_read;
|
||||
HANDLE child_stdin_write;
|
||||
};
|
||||
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||
|
||||
static void Win32ReportError(String msg, String cmd) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
defer { LocalFree(lpMsgBuf); };
|
||||
char *buff = (char *)lpMsgBuf;
|
||||
size_t buffLen = strlen((const char *)buff);
|
||||
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
|
||||
Error("%S: %S! %s", msg, cmd, (char *)lpMsgBuf);
|
||||
}
|
||||
|
||||
static void Win32CloseProcess(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
if (p->handle != INVALID_HANDLE_VALUE) CloseHandle(p->handle);
|
||||
if (p->child_stdout_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_write);
|
||||
if (p->child_stdout_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdout_read);
|
||||
if (p->child_stdin_read != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_read);
|
||||
if (p->child_stdin_write != INVALID_HANDLE_VALUE) CloseHandle(p->child_stdin_write);
|
||||
process->is_valid = false;
|
||||
}
|
||||
|
||||
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||
Win32ReportError(msg, cmd);
|
||||
Win32CloseProcess(process);
|
||||
}
|
||||
|
||||
void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
Assert(p->child_stdin_write != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD written = 0;
|
||||
bool write_error = WriteFile(p->child_stdin_write, string.data, (DWORD)string.len, &written, NULL) == 0;
|
||||
|
||||
Assert(write_error == false);
|
||||
Assert(written == string.len);
|
||||
}
|
||||
|
||||
void CloseStdin(Process *process) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
CloseHandle(p->child_stdin_write);
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
Process SpawnProcess(ExecArgs args) {
|
||||
Scratch scratch;
|
||||
Process process = {};
|
||||
process.args = args;
|
||||
Win32Process *p = (Win32Process *)process.platform;
|
||||
|
||||
p->handle = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
p->child_stdout_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_read = INVALID_HANDLE_VALUE;
|
||||
p->child_stdin_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
SECURITY_ATTRIBUTES security_atrb = {};
|
||||
security_atrb.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
security_atrb.bInheritHandle = TRUE;
|
||||
|
||||
if (!CreatePipe(&p->child_stdout_read, &p->child_stdout_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdout_read, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (args.open_stdin) {
|
||||
if (!CreatePipe(&p->child_stdin_read, &p->child_stdin_write, &security_atrb, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
if (!SetHandleInformation(p->child_stdin_write, HANDLE_FLAG_INHERIT, 0)) {
|
||||
Win32ProcessError(&process, "Failed to create process at create pipe stage", args.cmd);
|
||||
return process;
|
||||
}
|
||||
}
|
||||
|
||||
STARTUPINFOW startup = {};
|
||||
startup.cb = sizeof(STARTUPINFO);
|
||||
startup.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
startup.hStdOutput = p->child_stdout_write;
|
||||
startup.hStdError = p->child_stdout_write;
|
||||
startup.wShowWindow = SW_HIDE;
|
||||
if (args.open_stdin) {
|
||||
startup.hStdInput = p->child_stdin_read;
|
||||
}
|
||||
|
||||
String16 cwd = ToString16(scratch, args.cwd);
|
||||
String16 cmd = ToString16(scratch, args.cmd);
|
||||
String16 exe = ToString16(scratch, args.exe);
|
||||
|
||||
char *env = NULL;
|
||||
if (args.env.len) {
|
||||
Int size = GetSize(args.env) + args.env.len + 1;
|
||||
env = AllocArray(scratch, char, size);
|
||||
Int i = 0;
|
||||
For (args.env) {
|
||||
MemoryCopy(env + i, it.data, it.len);
|
||||
i += it.len;
|
||||
|
||||
env[i++] = 0;
|
||||
}
|
||||
env[i++] = 0;
|
||||
}
|
||||
|
||||
DWORD dwCreationFlags = 0;
|
||||
BOOL bInheritHandles = TRUE;
|
||||
|
||||
PROCESS_INFORMATION info = {};
|
||||
if (!CreateProcessW((wchar_t *)exe.data, (wchar_t *)cmd.data, 0, 0, bInheritHandles, dwCreationFlags, env, (wchar_t *)cwd.data, &startup, &info)) {
|
||||
Win32ProcessError(&process, "failed to create process", args.cmd);
|
||||
return process;
|
||||
}
|
||||
|
||||
// Close handles to the stdin and stdout pipes no longer needed by the child process.
|
||||
// If they are not explicitly closed, there is no way to recognize that the child process has ended.
|
||||
CloseHandle(info.hThread);
|
||||
CloseHandle(p->child_stdout_write);
|
||||
p->child_stdout_write = INVALID_HANDLE_VALUE;
|
||||
|
||||
p->handle = info.hProcess;
|
||||
process.is_valid = true;
|
||||
process.id = (int64_t)p->handle;
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
bool IsValid(Process *process) {
|
||||
if (process->is_valid == false) return false;
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
bool get_exit_code_failed = GetExitCodeProcess(p->handle, &exit_code) == 0;
|
||||
if (get_exit_code_failed) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
process->exit_code = (int)exit_code;
|
||||
Win32CloseProcess(process);
|
||||
return false;
|
||||
}
|
||||
|
||||
void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
bool terminate_process_error = TerminateProcess(p->handle, -1) == 0;
|
||||
if (terminate_process_error) {
|
||||
Assert(0);
|
||||
}
|
||||
Win32CloseProcess(process);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
DWORD bytes_avail = 0;
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
if (peek_error) {
|
||||
return {};
|
||||
} else if (bytes_avail == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t buffer_size = ClampTop(bytes_avail, (DWORD)(4096 * 16));
|
||||
char *buffer = AllocArray(allocator, char, buffer_size);
|
||||
|
||||
DWORD bytes_read = 0;
|
||||
bool read_error = ReadFile(p->child_stdout_read, buffer, (DWORD)buffer_size, &bytes_read, 0) == 0;
|
||||
if (read_error) {
|
||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||
Dealloc(allocator, buffer);
|
||||
Win32CloseProcess(process);
|
||||
return {};
|
||||
}
|
||||
|
||||
String result = {buffer, bytes_read};
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif OS_POSIX
|
||||
|
||||
struct UnixProcess {
|
||||
pid_t pid;
|
||||
int child_stdout_read;
|
||||
int stdin_write;
|
||||
};
|
||||
|
||||
Array<char *> SplitCommand(Allocator allocator, String command_line) {
|
||||
Array<char *> cmd = {allocator};
|
||||
|
||||
String curr = {};
|
||||
for (int i = 0; i < command_line.len; i += 1) {
|
||||
if (command_line.data[i] == ' ') {
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
curr = {};
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (curr.len == 0) {
|
||||
curr.data = command_line.data + i;
|
||||
}
|
||||
curr.len += 1;
|
||||
}
|
||||
|
||||
if (curr.len > 0) {
|
||||
Add(&cmd, Copy(allocator, curr).data);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) {
|
||||
Scratch scratch;
|
||||
const int PIPE_READ = 0;
|
||||
const int PIPE_WRITE = 1;
|
||||
bool error = false;
|
||||
|
||||
working_dir = Copy(scratch, working_dir);
|
||||
chdir(working_dir.data);
|
||||
|
||||
Process process = {};
|
||||
process.args = args;
|
||||
UnixProcess *plat = (UnixProcess *)&process.platform;
|
||||
Array<char *> args = SplitCommand(scratch, command_line);
|
||||
Array<char *> env = {scratch};
|
||||
|
||||
For (enviroment) {
|
||||
Add(&env, Copy(scratch, it).data);
|
||||
}
|
||||
|
||||
int stdout_desc[2] = {};
|
||||
int stdin_desc[2] = {};
|
||||
posix_spawn_file_actions_t actions = {};
|
||||
|
||||
if (posix_spawn_file_actions_init(&actions) != 0) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_init, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
};
|
||||
|
||||
if (pipe(stdout_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdout_desc[PIPE_READ]);
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdout_desc[PIPE_WRITE]);
|
||||
}
|
||||
};
|
||||
|
||||
if (pipe(stdin_desc) == -1) {
|
||||
Error("Libc function failed: pipe, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
defer {
|
||||
if (error) {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
close(stdin_desc[PIPE_WRITE]);
|
||||
} else {
|
||||
close(stdin_desc[PIPE_READ]);
|
||||
}
|
||||
};
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdout_desc[PIPE_READ]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDOUT_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDERR_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_addclose, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: posix_spawn_file_actions_adddup2 STDIN_FILENO, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
pid_t process_pid = 0;
|
||||
error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0;
|
||||
if (error) {
|
||||
Error("Libc function failed: failed to create process\n, with error: %s", strerror(errno));
|
||||
return process;
|
||||
}
|
||||
|
||||
|
||||
plat->child_stdout_read = stdout_desc[PIPE_READ];
|
||||
plat->stdin_write = stdin_desc[PIPE_WRITE];
|
||||
plat->pid = process_pid;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
process.id = process_pid;
|
||||
process.is_valid = true;
|
||||
return process;
|
||||
}
|
||||
|
||||
bool IsValid(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)&process->platform;
|
||||
if (process->is_valid == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLRDHUP | POLLERR | POLLHUP | POLLNVAL;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0) {
|
||||
pid_t result = waitpid(plat->pid, &status, 0);
|
||||
Assert(result != -1);
|
||||
process->exit_code = WEXITSTATUS(status);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void KillProcess(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
kill(plat->pid, SIGKILL);
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read) {
|
||||
Assert(process->is_valid);
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
|
||||
String result = {};
|
||||
result.data = AllocArray(allocator, char, 16 * 4096);
|
||||
|
||||
pollfd p = {};
|
||||
p.fd = plat->child_stdout_read;
|
||||
p.events = POLLIN;
|
||||
int res = poll(&p, 1, 0);
|
||||
if (res > 0 || force_read) {
|
||||
result.len = read(plat->child_stdout_read, result.data, 4 * 4096);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
ssize_t size = write(plat->stdin_write, string.data, string.len);
|
||||
|
||||
Assert(size == string.len);
|
||||
}
|
||||
|
||||
void CloseStdin(Process *process) {
|
||||
UnixProcess *plat = (UnixProcess *)process->platform;
|
||||
close(plat->stdin_write);
|
||||
}
|
||||
|
||||
#else
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin, Array<String> enviroment) { return {}; }
|
||||
bool IsValid(Process *process) { return false; }
|
||||
void KillProcess(Process *process) { }
|
||||
String PollStdout(Allocator allocator, Process *process, bool force_read) { return {}; }
|
||||
void WriteStdin(Process *process, String string) { return; }
|
||||
void CloseStdin(Process *process) { return; }
|
||||
#endif
|
||||
|
||||
// @todo: perhaps rework the PATH ProcessEnviroment into an object from data_desc
|
||||
String GetEnv(String name) {
|
||||
For (ProcessEnviroment) {
|
||||
if (StartsWith(it, name, true)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
String FindPython(Allocator allocator) {
|
||||
Scratch scratch(allocator);
|
||||
String path = GetEnv("PATH="); // ignore case on windows so it's fine. 'Path=' not needed
|
||||
Assert(path.len);
|
||||
String delim = IF_OS_WINDOWS_ELSE(";", ":");
|
||||
Array<String> paths = Split(scratch, path, delim);
|
||||
String tries[] = {"python3", "python", "py"};
|
||||
for (int i = 0; i < Lengthof(tries); i += 1) {
|
||||
For (paths) {
|
||||
String path_it = Format(scratch, "%S/%S" IF_OS_WINDOWS_ELSE(".exe", ""), it, tries[i]);
|
||||
bool is_bad_bad_ms = EndsWith(it, "AppData\\Local\\Microsoft\\WindowsApps") || EndsWith(it, "AppData/Local/Microsoft/WindowsApps");
|
||||
if (FileExists(path_it) IF_OS_WINDOWS(&& !is_bad_bad_ms)) {
|
||||
return Copy(allocator, path_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void SetShell(Allocator allocator, String *exe, String *cmd, String in_cmd) {
|
||||
#if OS_WINDOWS
|
||||
*exe = "c:\\windows\\system32\\cmd.exe";
|
||||
*cmd = Format(allocator, "%S /C %S", *exe, in_cmd);
|
||||
#else
|
||||
*exe = "/usr/bin/bash";
|
||||
String temp_file = WriteTempFile(in_cmd); // @todo: maybe try to pass by stdio here
|
||||
*cmd = Format(allocator, "%S %S", *exe, temp_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
// WARNING: seems that this maybe can't work reliably?
|
||||
// in case of 'git grep a' it's possible that child process spawns it's own
|
||||
// child process and then it won't print anything because it won't have
|
||||
@@ -8,11 +461,11 @@ void UpdateProcesses() {
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
Scratch scratch;
|
||||
View *view = GetView(ViewID{it.view_id});
|
||||
View *view = GetView(it.args.output_view);
|
||||
|
||||
String poll = PollStdout(scratch, &it, false);
|
||||
if (poll.len) {
|
||||
Append(view, poll, it.scroll_to_end);
|
||||
Append(view, poll, it.args.scroll_to_end);
|
||||
}
|
||||
if (!IsValid(&it)) {
|
||||
ReportConsolef("process %lld exit code = %d", it.id, it.exit_code);
|
||||
@@ -21,20 +474,23 @@ void UpdateProcesses() {
|
||||
}
|
||||
}
|
||||
|
||||
void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) {
|
||||
Process process = SpawnProcess(cmd, working_dir, {}, ProcessEnviroment);
|
||||
ReportConsolef("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, cmd, working_dir);
|
||||
process.view_id = view.id;
|
||||
process.scroll_to_end = scroll_to_end;
|
||||
if (process.is_valid) {
|
||||
Add(&ActiveProcesses, process);
|
||||
}
|
||||
ExecArgs ShellArgs(Allocator allocator, ViewID output_view, String cmd, String working_directory) {
|
||||
ExecArgs args = {};
|
||||
SetShell(allocator, &args.exe, &args.cmd, cmd);
|
||||
args.env = ProcessEnviroment;
|
||||
args.cwd = working_directory;
|
||||
args.output_view = output_view;
|
||||
args.poll_process = 1;
|
||||
return args;
|
||||
}
|
||||
|
||||
void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
|
||||
Scratch scratch;
|
||||
String cmd = ToString(scratch, cmd16);
|
||||
Exec(view, scroll_to_end, cmd, working_dir);
|
||||
Process Exec(ExecArgs args) {
|
||||
Process process = SpawnProcess(args);
|
||||
ReportConsolef("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, args.cmd, args.cwd);
|
||||
if (process.is_valid && args.poll_process) {
|
||||
Add(&ActiveProcesses, process);
|
||||
}
|
||||
return process;
|
||||
}
|
||||
|
||||
struct ExecResult {
|
||||
@@ -44,9 +500,14 @@ struct ExecResult {
|
||||
|
||||
ExecResult ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) {
|
||||
ReportConsolef("ExecAndWait cmd = %S working_dir = %S stdin_string = %S", cmd, working_dir, stdin_string);
|
||||
|
||||
Buffer *scratch_buff = CreateScratchBuffer(allocator, 4096 * 4);
|
||||
Process process = SpawnProcess(cmd, working_dir, stdin_string, ProcessEnviroment);
|
||||
|
||||
ExecArgs args = ShellArgs(allocator, {}, cmd, working_dir);
|
||||
args.poll_process = 0;
|
||||
args.open_stdin = 1;
|
||||
Process process = Exec(args);
|
||||
WriteStdin(&process, stdin_string);
|
||||
CloseStdin(&process);
|
||||
for (;IsValid(&process);) {
|
||||
Scratch scratch(allocator);
|
||||
String poll = PollStdout(scratch, &process, true);
|
||||
@@ -60,12 +521,12 @@ void KillProcess(View *view) {
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
|
||||
ViewID view_id = {it.view_id};
|
||||
ViewID view_id = {it.args.output_view};
|
||||
if (view_id == view->id) {
|
||||
KillProcess(&it);
|
||||
remove_item = true;
|
||||
String string = "process was killed by user\n";
|
||||
Append(view, string, it.scroll_to_end);
|
||||
Append(view, string, it.args.scroll_to_end);
|
||||
// dont break because that will fuck with removal ...
|
||||
}
|
||||
}
|
||||
@@ -73,27 +534,9 @@ void KillProcess(View *view) {
|
||||
|
||||
bool ProcessIsActive(ViewID view) {
|
||||
For (ActiveProcesses) {
|
||||
if (it.view_id == view.id) {
|
||||
if (it.args.output_view == view) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
View *ExecHidden(String buffer_name, String cmd, String working_dir) {
|
||||
View *view = OpenBufferView(buffer_name);
|
||||
Buffer *buffer = GetBuffer(view->active_buffer);
|
||||
buffer->dont_try_to_save_in_bulk_ops = true;
|
||||
Exec(view->id, true, cmd, working_dir);
|
||||
return view;
|
||||
}
|
||||
|
||||
BSet Exec(String cmd, String working_dir, bool set_active = true) {
|
||||
BSet main = GetBSet(PrimaryWindowID);
|
||||
if (set_active) {
|
||||
NextActiveWindowID = main.window->id;
|
||||
}
|
||||
JumpTempBuffer(&main);
|
||||
Exec(main.view->id, true, cmd, working_dir);
|
||||
return main;
|
||||
}
|
||||
|
||||
@@ -872,7 +872,18 @@ int main(int argc, char **argv)
|
||||
// return 0;
|
||||
}
|
||||
|
||||
#if !OS_WINDOWS
|
||||
#if OS_WINDOWS
|
||||
{
|
||||
wchar_t *p = GetEnvironmentStringsW();
|
||||
for (;p && p[0];) {
|
||||
String16 string16((char16_t *)p);
|
||||
String string = ToString(Perm, string16);
|
||||
Add(&ProcessEnviroment, string);
|
||||
p += string16.len + 1;
|
||||
}
|
||||
// FreeEnvironmentStringsW(p); // I get a trap here? why?
|
||||
}
|
||||
#else
|
||||
for (int i = 0; environ[i]; i += 1) {
|
||||
Add(&ProcessEnviroment, Copy(Perm, environ[i]));
|
||||
}
|
||||
|
||||
@@ -45,11 +45,33 @@ enum UIAction {
|
||||
UIAction_No,
|
||||
};
|
||||
|
||||
struct ExecArgs {
|
||||
String exe;
|
||||
String cmd;
|
||||
String cwd;
|
||||
Array<String> env;
|
||||
|
||||
ViewID output_view; // poll_process
|
||||
struct {
|
||||
U32 poll_process : 1;
|
||||
U32 open_stdin : 1;
|
||||
U32 scroll_to_end : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct Process {
|
||||
bool is_valid;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
Int id;
|
||||
|
||||
ExecArgs args; // memory for exe and all that is invalid
|
||||
};
|
||||
|
||||
enum OpenKind {
|
||||
OpenKind_Invalid,
|
||||
OpenKind_Skip,
|
||||
OpenKind_Exec,
|
||||
OpenKind_BackgroundExec,
|
||||
OpenKind_Goto,
|
||||
OpenKind_Command,
|
||||
#if PLUGIN_CONFIG
|
||||
@@ -68,7 +90,12 @@ struct ResolvedOpen {
|
||||
OpenKind kind;
|
||||
String path;
|
||||
Int line, col;
|
||||
bool existing_buffer;
|
||||
|
||||
struct {
|
||||
U32 existing_buffer : 1;
|
||||
U32 exec_in_background : 1;
|
||||
U32 use_python_shell : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct Buffer {
|
||||
|
||||
@@ -281,7 +281,8 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
|
||||
// !!exec_hidden
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "!!")) {
|
||||
result.kind = OpenKind_BackgroundExec;
|
||||
result.kind = OpenKind_Exec;
|
||||
result.exec_in_background = 1;
|
||||
result.path = Skip(path, 2);
|
||||
}
|
||||
|
||||
@@ -289,22 +290,20 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "!")) {
|
||||
result.kind = OpenKind_Exec;
|
||||
result.path = Skip(path, 1);
|
||||
|
||||
Int idx = 0;
|
||||
String needle = "{{TEMP}}";
|
||||
if (Seek(result.path, needle, &idx, SeekFlag_None)) {
|
||||
String rest = Skip(result.path, idx + needle.len);
|
||||
String begin = GetPrefix(result.path, idx);
|
||||
String temp_filename = WriteTempFile(rest);
|
||||
result.path = Format(alo, "%S%S", begin, temp_filename);
|
||||
}
|
||||
|
||||
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "py:")) {
|
||||
result.kind = OpenKind_Exec;
|
||||
result.path = Skip(path, 3);
|
||||
result.use_python_shell = 1;
|
||||
}
|
||||
|
||||
// https://web
|
||||
bool web = StartsWith(path, "https://") || StartsWith(path, "http://");
|
||||
if (exec && result.kind == OpenKind_Invalid && web) {
|
||||
result.path = Format(alo, "%S %S", InternetBrowser, path);
|
||||
result.kind = OpenKind_BackgroundExec;
|
||||
result.kind = OpenKind_Exec;
|
||||
result.exec_in_background = 1;
|
||||
}
|
||||
|
||||
// commit 3kc09as92
|
||||
@@ -352,7 +351,7 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
Int start = i;
|
||||
String b = {path.data + 1 + start, (end - start)};
|
||||
|
||||
if (At(path, i) == ')') {
|
||||
if (At(path, i) == '(') {
|
||||
i -= 1;
|
||||
path.len = i + 1;
|
||||
result.line = strtoll(b.data, NULL, 10);
|
||||
@@ -402,7 +401,9 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
|
||||
String rel_path = Format(alo, "%S/%S", GetDirectory(window), path);
|
||||
existing_buffer = GetBuffer(rel_path, NULL);
|
||||
if (existing_buffer || FileExists(rel_path)) {
|
||||
result.existing_buffer = existing_buffer;
|
||||
if (existing_buffer) {
|
||||
result.existing_buffer = 1;
|
||||
}
|
||||
result.path = rel_path;
|
||||
result.kind = OpenKind_Goto;
|
||||
}
|
||||
@@ -444,15 +445,28 @@ BSet Open(Window *window, String path, ResolveOpenMeta meta, bool set_active = t
|
||||
}
|
||||
CenterView(window->id);
|
||||
} else if (o.kind == OpenKind_Exec) {
|
||||
ExecArgs args = {};// ShellArgs(scratch, LogView->id, o.path, GetPrimaryDirectory());
|
||||
args.poll_process = 1;
|
||||
args.output_view = LogView->id;
|
||||
args.cwd = GetPrimaryDirectory();
|
||||
if (o.exec_in_background == 0) {
|
||||
if (set_active) {
|
||||
NextActiveWindowID = set.window->id;
|
||||
}
|
||||
JumpTempBuffer(&set);
|
||||
RawAppend(set.buffer, u"\n");
|
||||
Exec(set.view->id, false, o.path, GetPrimaryDirectory());
|
||||
} else if (o.kind == OpenKind_BackgroundExec) {
|
||||
// this shouldn't change the focus/window/view
|
||||
Exec(LogView->id, false, o.path, GetPrimaryDirectory());
|
||||
args.output_view = set.view->id;
|
||||
}
|
||||
|
||||
if (o.use_python_shell == 1) {
|
||||
args.exe = FindPython(scratch);
|
||||
String temp_file = WriteTempFile(o.path);
|
||||
args.cmd = Format(scratch, "%S %S", args.exe, temp_file);
|
||||
} else {
|
||||
SetShell(scratch, &args.exe, &args.cmd, o.path);
|
||||
}
|
||||
|
||||
Exec(args);
|
||||
} else if (o.kind == OpenKind_Command) {
|
||||
EvalCommand(o.path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user