Major redesign of the Exec API, in order to make linux more uniform with windows and introduce python shell

This commit is contained in:
Krzosa Karol
2026-02-02 22:21:19 +01:00
parent ed3be39037
commit 830be12b24
12 changed files with 596 additions and 534 deletions

View File

@@ -85,6 +85,14 @@
#define IF_DEBUG(x) #define IF_DEBUG(x)
#endif #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 #if OS_WINDOWS
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX #define NOMINMAX

View File

@@ -257,204 +257,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
return it; 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 #elif OS_WINDOWS
@@ -725,210 +527,6 @@ API MakeDirResult MakeDir(String path) {
return result; 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 #elif OS_WASM
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -1122,54 +720,6 @@ API FileIter IterateFiles(Allocator alo, String path) {
return it; 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 #endif
API double GetTimeSeconds() { API double GetTimeSeconds() {

View File

@@ -33,22 +33,9 @@ String GetWorkingDir(Allocator arena);
bool IsAbsolute(String path); bool IsAbsolute(String path);
int64_t GetFileModTime(String file); 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); double GetTimeMicros(void);
enum MakeDirResult { enum MakeDirResult {

View File

@@ -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) { BSet ExecBuild(String windows_cmd, String unix_cmd, String working_dir = ProjectDirectory) {
SaveAll(); SaveAll();
Scratch scratch;
BSet build = GetBSet(BuildWindowID); BSet build = GetBSet(BuildWindowID);
BSet main = GetBSet(PrimaryWindowID); BSet main = GetBSet(PrimaryWindowID);
SelectRange(build.view, Range{}); SelectRange(build.view, Range{});
ResetBuffer(build.buffer); ResetBuffer(build.buffer);
if (OS_WINDOWS) { {
Exec(build.view->id, true, windows_cmd, working_dir); ExecArgs args = ShellArgs(scratch, build.view->id, windows_cmd, working_dir);
} else { args.scroll_to_end = true;
Exec(build.view->id, true, unix_cmd, working_dir); if (!OS_WINDOWS) {
args.cmd = unix_cmd;
}
Exec(args);
} }
main.window->active_goto_list = build.view->id; main.window->active_goto_list = build.view->id;
main.window->goto_list_pos = 0; main.window->goto_list_pos = 0;

View File

@@ -5,7 +5,10 @@ void Windows_SetupVCVarsall(mco_coro *co) {
String working_dir = ProjectDirectory; String working_dir = ProjectDirectory;
String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-"); String buffer_name = GetUniqueBufferName(working_dir, "vcvarsall-");
String cmd = Format(scratch, "\"%S\" && set", VCVarsPath); 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 (;;) { for (;;) {
if (!ProcessIsActive(view->id)) { if (!ProcessIsActive(view->id)) {
@@ -19,6 +22,7 @@ void Windows_SetupVCVarsall(mco_coro *co) {
String16 string16 = GetString(buffer); String16 string16 = GetString(buffer);
String string = ToString(scratch, string16); String string = ToString(scratch, string16);
Array<String> lines = Split(scratch, string, "\n"); Array<String> lines = Split(scratch, string, "\n");
ProcessEnviroment.len = 0;
For (lines) { For (lines) {
String s = Trim(it); String s = Trim(it);
Array<String> values = Split(scratch, s, "="); Array<String> values = Split(scratch, s, "=");
@@ -27,8 +31,6 @@ void Windows_SetupVCVarsall(mco_coro *co) {
} }
} }
void LoadVCVars() { void LoadVCVars() {
RemoveCoroutine("Windows_SetupVCVarsall"); RemoveCoroutine("Windows_SetupVCVarsall");
CCtx *co_data = AddCoroutine(Windows_SetupVCVarsall); CCtx *co_data = AddCoroutine(Windows_SetupVCVarsall);

View File

@@ -2141,7 +2141,9 @@ bool RDBG_InitConnection(mco_coro *co, bool create_session = true) {
String session_name = Format(ctx->arena, "te%llu", GetTimeNanos()); String session_name = Format(ctx->arena, "te%llu", GetTimeNanos());
String remedy_string = Format(ctx->arena, "%S --servername %S", RemedyBGPath, session_name); String remedy_string = Format(ctx->arena, "%S --servername %S", RemedyBGPath, session_name);
ReportConsolef("Remedybg: %S", remedy_string); 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)); MemoryZero(&RDBG_Ctx, sizeof(RDBG_Ctx));
RDBG_Ctx.cmd.data = command_buf; RDBG_Ctx.cmd.data = command_buf;
RDBG_Ctx.cmd.capacity = sizeof(command_buf); RDBG_Ctx.cmd.capacity = sizeof(command_buf);

View File

@@ -74,7 +74,7 @@ void UpdateStatusWindow() {
commands = Format(scratch, "%S :Reopen", commands); commands = Format(scratch, "%S :Reopen", commands);
} }
For (ActiveProcesses) { For (ActiveProcesses) {
if (it.view_id == main.view->id.id) { if (it.args.output_view == main.view->id) {
commands = Format(scratch, "%S :KillProcess", commands); commands = Format(scratch, "%S :KillProcess", commands);
break; 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); 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) { 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); s = Format(scratch, "%S %lld :KillProcess", s, (long long)it.id);
} }
} }

View File

@@ -49,6 +49,20 @@ void CMD_FocusWindow3() {
} }
} RegisterCommand(CMD_FocusWindow3, "ctrl-3", "Select the 3rd window, counting from left"); } 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() { void CMD_NewWindow() {
CreateWind(); CreateWind();
} RegisterCommand(CMD_NewWindow, "ctrl-backslash", "Creates a new window"); } RegisterCommand(CMD_NewWindow, "ctrl-backslash", "Creates a new window");

View File

@@ -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? // 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 // 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 // child process and then it won't print anything because it won't have
@@ -8,11 +461,11 @@ void UpdateProcesses() {
IterRemove(ActiveProcesses) { IterRemove(ActiveProcesses) {
IterRemovePrepare(ActiveProcesses); IterRemovePrepare(ActiveProcesses);
Scratch scratch; Scratch scratch;
View *view = GetView(ViewID{it.view_id}); View *view = GetView(it.args.output_view);
String poll = PollStdout(scratch, &it, false); String poll = PollStdout(scratch, &it, false);
if (poll.len) { if (poll.len) {
Append(view, poll, it.scroll_to_end); Append(view, poll, it.args.scroll_to_end);
} }
if (!IsValid(&it)) { if (!IsValid(&it)) {
ReportConsolef("process %lld exit code = %d", it.id, it.exit_code); 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) { ExecArgs ShellArgs(Allocator allocator, ViewID output_view, String cmd, String working_directory) {
Process process = SpawnProcess(cmd, working_dir, {}, ProcessEnviroment); ExecArgs args = {};
ReportConsolef("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, cmd, working_dir); SetShell(allocator, &args.exe, &args.cmd, cmd);
process.view_id = view.id; args.env = ProcessEnviroment;
process.scroll_to_end = scroll_to_end; args.cwd = working_directory;
if (process.is_valid) { args.output_view = output_view;
Add(&ActiveProcesses, process); args.poll_process = 1;
} return args;
} }
void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) { Process Exec(ExecArgs args) {
Scratch scratch; Process process = SpawnProcess(args);
String cmd = ToString(scratch, cmd16); ReportConsolef("process %lld start. is_valid = %d cmd = %S working_dir = %S", process.id, process.is_valid, args.cmd, args.cwd);
Exec(view, scroll_to_end, cmd, working_dir); if (process.is_valid && args.poll_process) {
Add(&ActiveProcesses, process);
}
return process;
} }
struct ExecResult { struct ExecResult {
@@ -44,9 +500,14 @@ struct ExecResult {
ExecResult ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) { 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); ReportConsolef("ExecAndWait cmd = %S working_dir = %S stdin_string = %S", cmd, working_dir, stdin_string);
Buffer *scratch_buff = CreateScratchBuffer(allocator, 4096 * 4); 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);) { for (;IsValid(&process);) {
Scratch scratch(allocator); Scratch scratch(allocator);
String poll = PollStdout(scratch, &process, true); String poll = PollStdout(scratch, &process, true);
@@ -60,12 +521,12 @@ void KillProcess(View *view) {
IterRemove(ActiveProcesses) { IterRemove(ActiveProcesses) {
IterRemovePrepare(ActiveProcesses); IterRemovePrepare(ActiveProcesses);
ViewID view_id = {it.view_id}; ViewID view_id = {it.args.output_view};
if (view_id == view->id) { if (view_id == view->id) {
KillProcess(&it); KillProcess(&it);
remove_item = true; remove_item = true;
String string = "process was killed by user\n"; 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 ... // dont break because that will fuck with removal ...
} }
} }
@@ -73,27 +534,9 @@ void KillProcess(View *view) {
bool ProcessIsActive(ViewID view) { bool ProcessIsActive(ViewID view) {
For (ActiveProcesses) { For (ActiveProcesses) {
if (it.view_id == view.id) { if (it.args.output_view == view) {
return true; return true;
} }
} }
return false; 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;
}

View File

@@ -872,7 +872,18 @@ int main(int argc, char **argv)
// return 0; // 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) { for (int i = 0; environ[i]; i += 1) {
Add(&ProcessEnviroment, Copy(Perm, environ[i])); Add(&ProcessEnviroment, Copy(Perm, environ[i]));
} }

View File

@@ -45,11 +45,33 @@ enum UIAction {
UIAction_No, 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 { enum OpenKind {
OpenKind_Invalid, OpenKind_Invalid,
OpenKind_Skip, OpenKind_Skip,
OpenKind_Exec, OpenKind_Exec,
OpenKind_BackgroundExec,
OpenKind_Goto, OpenKind_Goto,
OpenKind_Command, OpenKind_Command,
#if PLUGIN_CONFIG #if PLUGIN_CONFIG
@@ -68,7 +90,12 @@ struct ResolvedOpen {
OpenKind kind; OpenKind kind;
String path; String path;
Int line, col; Int line, col;
bool existing_buffer;
struct {
U32 existing_buffer : 1;
U32 exec_in_background : 1;
U32 use_python_shell : 1;
};
}; };
struct Buffer { struct Buffer {

View File

@@ -281,7 +281,8 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
// !!exec_hidden // !!exec_hidden
if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "!!")) { 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); 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, "!")) { if (exec && result.kind == OpenKind_Invalid && StartsWith(path, "!")) {
result.kind = OpenKind_Exec; result.kind = OpenKind_Exec;
result.path = Skip(path, 1); 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 // https://web
bool web = StartsWith(path, "https://") || StartsWith(path, "http://"); bool web = StartsWith(path, "https://") || StartsWith(path, "http://");
if (exec && result.kind == OpenKind_Invalid && web) { if (exec && result.kind == OpenKind_Invalid && web) {
result.path = Format(alo, "%S %S", InternetBrowser, path); result.path = Format(alo, "%S %S", InternetBrowser, path);
result.kind = OpenKind_BackgroundExec; result.kind = OpenKind_Exec;
result.exec_in_background = 1;
} }
// commit 3kc09as92 // commit 3kc09as92
@@ -352,7 +351,7 @@ ResolvedOpen ResolveOpen(Allocator alo, Window *window, String path, ResolveOpen
Int start = i; Int start = i;
String b = {path.data + 1 + start, (end - start)}; String b = {path.data + 1 + start, (end - start)};
if (At(path, i) == ')') { if (At(path, i) == '(') {
i -= 1; i -= 1;
path.len = i + 1; path.len = i + 1;
result.line = strtoll(b.data, NULL, 10); 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); String rel_path = Format(alo, "%S/%S", GetDirectory(window), path);
existing_buffer = GetBuffer(rel_path, NULL); existing_buffer = GetBuffer(rel_path, NULL);
if (existing_buffer || FileExists(rel_path)) { if (existing_buffer || FileExists(rel_path)) {
result.existing_buffer = existing_buffer; if (existing_buffer) {
result.existing_buffer = 1;
}
result.path = rel_path; result.path = rel_path;
result.kind = OpenKind_Goto; result.kind = OpenKind_Goto;
} }
@@ -444,15 +445,28 @@ BSet Open(Window *window, String path, ResolveOpenMeta meta, bool set_active = t
} }
CenterView(window->id); CenterView(window->id);
} else if (o.kind == OpenKind_Exec) { } 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) { if (set_active) {
NextActiveWindowID = set.window->id; NextActiveWindowID = set.window->id;
} }
JumpTempBuffer(&set); JumpTempBuffer(&set);
RawAppend(set.buffer, u"\n"); RawAppend(set.buffer, u"\n");
Exec(set.view->id, false, o.path, GetPrimaryDirectory()); args.output_view = set.view->id;
} else if (o.kind == OpenKind_BackgroundExec) { }
// this shouldn't change the focus/window/view
Exec(LogView->id, false, o.path, GetPrimaryDirectory()); 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) { } else if (o.kind == OpenKind_Command) {
EvalCommand(o.path); EvalCommand(o.path);
} }