Improve process logic
This commit is contained in:
@@ -24,7 +24,7 @@ String GetAbsolutePath(Allocator arena, String relative);
|
||||
bool IsValid(const FileIter &it);
|
||||
void Advance(FileIter *it);
|
||||
FileIter IterateFiles(Allocator allocator, String path);
|
||||
bool InitOS();
|
||||
void InitOS(void (*error_proc)(const char *, ...));
|
||||
|
||||
String GetExePath(Allocator allocator);
|
||||
String GetExeDir(Allocator allocator);
|
||||
@@ -34,26 +34,18 @@ bool IsFile(String path);
|
||||
String GetWorkingDir(Allocator arena);
|
||||
bool IsAbsolute(String path);
|
||||
|
||||
struct StdoutPollInfo {
|
||||
int64_t size_read;
|
||||
int64_t size_available;
|
||||
};
|
||||
|
||||
struct Process {
|
||||
bool is_valid;
|
||||
String error_message;
|
||||
bool exited;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
bool is_valid;
|
||||
int exit_code;
|
||||
char platform[6 * 8];
|
||||
|
||||
int64_t view_id; // text editor view
|
||||
bool scroll_to_end;
|
||||
};
|
||||
|
||||
Process CreateCommandLineProcess(String command_line, String working_dir);
|
||||
bool PollExitCode(Process *process);
|
||||
void CloseProcess(Process *process);
|
||||
void KillProcess(Process *process);
|
||||
StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size);
|
||||
void WriteStdin(Process *process, String string);
|
||||
void CloseStdin(Process *process);
|
||||
Process SpawnProcess(String command_line, String working_dir, String write_stdin = {});
|
||||
bool IsValid(Process *process);
|
||||
void KillProcess(Process *process);
|
||||
String PollStdout(Allocator allocator, Process *process);
|
||||
void WriteStdin(Process *process, String string);
|
||||
void CloseStdin(Process *process);
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include "win32_thread.cpp"
|
||||
|
||||
void (*Error)(const char *, ...);
|
||||
|
||||
// Basic begin
|
||||
void *VReserve(size_t size) {
|
||||
void *result = (uint8_t *)VirtualAlloc(0, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
@@ -35,26 +37,8 @@ bool VDecommit(void *p, size_t size) {
|
||||
}
|
||||
// Basic end
|
||||
|
||||
bool InitOS() {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut != INVALID_HANDLE_VALUE) {
|
||||
DWORD dwMode = 0;
|
||||
if (GetConsoleMode(hOut, &dwMode)) {
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (SetConsoleMode(hOut, dwMode)) {
|
||||
return true;
|
||||
} else {
|
||||
printf("Failed to enable colored terminal output C\n");
|
||||
}
|
||||
} else {
|
||||
printf("Failed to enable colored terminal output B\n");
|
||||
}
|
||||
} else {
|
||||
printf("Failed to enable colored terminal output A\n");
|
||||
}
|
||||
return false;
|
||||
void InitOS(void (*error_proc)(const char *, ...)) {
|
||||
Error = error_proc;
|
||||
}
|
||||
|
||||
String ReadFile(Allocator arena, String path) {
|
||||
@@ -287,25 +271,17 @@ struct Win32Process {
|
||||
};
|
||||
static_assert(sizeof(Win32Process) < sizeof(Process::platform));
|
||||
|
||||
static String Win32GetPlatformError(String msg, String cmd) {
|
||||
void Win32ReportError(String msg, String cmd) {
|
||||
LPVOID lpMsgBuf;
|
||||
DWORD dw = GetLastError();
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// @warning: leak! but we don't care
|
||||
String r = Format(GetSystemAllocator(), "%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
return r;
|
||||
if (buffLen > 0 && buff[buffLen - 1] == '\n') buff[buffLen - 1] = 0;
|
||||
Error("%.*s: %.*s! %s", FmtString(msg), FmtString(cmd), (char *)lpMsgBuf);
|
||||
}
|
||||
|
||||
static void Win32CloseProcess(Process *process) {
|
||||
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);
|
||||
@@ -316,12 +292,32 @@ static void Win32CloseProcess(Process *process) {
|
||||
}
|
||||
|
||||
static void Win32ProcessError(Process *process, String msg, String cmd) {
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
process->error_message = Win32GetPlatformError(msg, cmd);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
Win32ReportError(msg, cmd);
|
||||
Win32CloseProcess(process);
|
||||
}
|
||||
|
||||
Process CreateCommandLineProcess(String command_line, String working_dir) {
|
||||
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(String command_line, String working_dir, String write_stdin) {
|
||||
Process process = {};
|
||||
Win32Process *p = (Win32Process *)process.platform;
|
||||
|
||||
@@ -389,16 +385,21 @@ Process CreateCommandLineProcess(String command_line, String working_dir) {
|
||||
|
||||
p->handle = info.hProcess;
|
||||
process.is_valid = true;
|
||||
|
||||
if (write_stdin.len) {
|
||||
WriteStdin(&process, write_stdin);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
bool PollExitCode(Process *process) {
|
||||
Assert(process->is_valid);
|
||||
bool IsValid(Process *process) {
|
||||
if (process->is_valid == false) return false;
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
if (process->exited) return process->exited;
|
||||
|
||||
if (WaitForSingleObject(p->handle, 0) != WAIT_OBJECT_0) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD exit_code;
|
||||
@@ -407,13 +408,9 @@ bool PollExitCode(Process *process) {
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
process->exited = true;
|
||||
process->exit_code = (int)exit_code;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloseProcess(Process *process) {
|
||||
Win32CloseProcess(process);
|
||||
return false;
|
||||
}
|
||||
|
||||
void KillProcess(Process *process) {
|
||||
@@ -428,53 +425,30 @@ void KillProcess(Process *process) {
|
||||
process->exit_code = -1;
|
||||
}
|
||||
|
||||
StdoutPollInfo PollStdout(Process *process, char *buffer, int64_t buffer_size) {
|
||||
String PollStdout(Allocator allocator, Process *process) {
|
||||
Assert(process->is_valid);
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
DWORD bytes_avail = 0;
|
||||
|
||||
StdoutPollInfo result = {};
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
bool peek_error = PeekNamedPipe(p->child_stdout_read, NULL, 0, NULL, &bytes_avail, NULL) == 0;
|
||||
if (peek_error) {
|
||||
process->error_message = Win32GetPlatformError("Failed to peek stdout of child process", "PeekNamedPipe");
|
||||
return result;
|
||||
return {};
|
||||
} else if (bytes_avail == 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
return {0, bytes_avail};
|
||||
}
|
||||
|
||||
if (bytes_avail == 0) {
|
||||
return result;
|
||||
}
|
||||
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) {
|
||||
process->error_message = Win32GetPlatformError("Failed to read the stdout of child process", "ReadFile");
|
||||
return result;
|
||||
Win32ReportError("Failed to read the stdout of child process", "ReadFile");
|
||||
Dealloc(allocator, &buffer);
|
||||
Win32CloseProcess(process);
|
||||
return {};
|
||||
}
|
||||
|
||||
result = {bytes_read, bytes_avail - bytes_read};
|
||||
String result = {buffer, bytes_read};
|
||||
return result;
|
||||
}
|
||||
|
||||
void WriteStdin(Process *process, String string) {
|
||||
if (string.len == 0) return;
|
||||
Assert(process->is_valid);
|
||||
|
||||
Win32Process *p = (Win32Process *)process->platform;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -383,19 +383,24 @@ void SaveBuffer(View *view) {
|
||||
Assert(view->active_buffer == buffer->id);
|
||||
}
|
||||
|
||||
{
|
||||
Scratch scratch;
|
||||
String16 string16 = GetString(*buffer);
|
||||
String string = ToString(scratch, string16);
|
||||
Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string);
|
||||
Command_ReplaceWithoutMovingCarets(view, GetRange(*buffer), {temp_buffer->str, temp_buffer->len});
|
||||
}
|
||||
|
||||
Scratch scratch;
|
||||
String16 string16 = GetString(*buffer);
|
||||
String string = ToString(scratch, string16);
|
||||
Buffer *temp_buffer = ExecAndWait(scratch, "clang-format", GetDir(buffer), string);
|
||||
Command_ReplaceWithoutMovingCarets(view, GetRange(*buffer), {temp_buffer->str, temp_buffer->len});
|
||||
String16 string16 = GetString(*buffer);
|
||||
String string = ToString(scratch, string16);
|
||||
bool success = WriteFile(buffer->name, string);
|
||||
|
||||
// bool success = WriteFile(buffer->name, string);
|
||||
|
||||
// if (success) {
|
||||
// buffer->dirty = false;
|
||||
// } else {
|
||||
// ReportWarningf("Failed to save file with name: %.*s", FmtString(buffer->name));
|
||||
// }
|
||||
if (success) {
|
||||
buffer->dirty = false;
|
||||
} else {
|
||||
ReportWarningf("Failed to save file with name: %.*s", FmtString(buffer->name));
|
||||
}
|
||||
}
|
||||
|
||||
void Command_KillSelectedLines(View *view) {
|
||||
|
||||
@@ -7,35 +7,20 @@ Array<Process> ActiveProcesses = {};
|
||||
// 'less' program which errors out and doesn't print anything
|
||||
// @todo: maybe I should ask someone smarter about this!
|
||||
void UpdateProcesses() {
|
||||
Scratch scratch;
|
||||
int64_t buffer_size = 4096;
|
||||
char *buffer = AllocArray(scratch, char, buffer_size);
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
StdoutPollInfo info = PollStdout(&it, buffer, buffer_size);
|
||||
String string = {buffer, info.size_read};
|
||||
ViewID view_id = {it.view_id};
|
||||
if (string.len) Command_Append(view_id, string, it.scroll_to_end);
|
||||
if (it.error_message.len) Command_Append(view_id, it.error_message, it.scroll_to_end);
|
||||
|
||||
bool exited = PollExitCode(&it);
|
||||
if (exited) {
|
||||
CloseProcess(&it);
|
||||
remove_item = true;
|
||||
// String s = Format(scratch, "exited with code: %d", it.exit_code);
|
||||
// Command_Append(view_id, s, it.scroll_to_end);
|
||||
}
|
||||
Scratch scratch;
|
||||
String poll = PollStdout(scratch, &it);
|
||||
if (poll.len) Command_Append({it.view_id}, poll, it.scroll_to_end);
|
||||
if (!IsValid(&it)) remove_item = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Exec(ViewID view, bool scroll_to_end, String cmd, String working_dir) {
|
||||
Process process = CreateCommandLineProcess(cmd, working_dir);
|
||||
if (process.error_message.len) Command_Append(view, process.error_message, process.scroll_to_end);
|
||||
Process process = SpawnProcess(cmd, working_dir);
|
||||
process.view_id = view.id;
|
||||
process.scroll_to_end = scroll_to_end;
|
||||
if (!process.is_valid) return;
|
||||
Assert(process.error_message.len == 0);
|
||||
Add(&ActiveProcesses, process);
|
||||
if (process.is_valid) Add(&ActiveProcesses, process);
|
||||
}
|
||||
|
||||
void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
|
||||
@@ -45,78 +30,16 @@ void Exec(ViewID view, bool scroll_to_end, String16 cmd16, String working_dir) {
|
||||
}
|
||||
|
||||
Buffer *ExecAndWait(Allocator allocator, String cmd, String working_dir, String stdin_string = {}) {
|
||||
Process process = CreateCommandLineProcess(cmd, working_dir);
|
||||
if (process.error_message.len) ReportWarningf("%.*s", FmtString(process.error_message));
|
||||
|
||||
Buffer *temp_buffer = CreateTempBuffer(allocator, 4096 * 4);
|
||||
if (!process.is_valid) return temp_buffer;
|
||||
|
||||
if (stdin_string.len) {
|
||||
WriteStdin(&process, stdin_string);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
Scratch scratch(allocator);
|
||||
char *buffer = AllocArray(scratch, char, 4096);
|
||||
for (;;) {
|
||||
StdoutPollInfo info = PollStdout(&process, buffer, 4096);
|
||||
String string = {buffer, info.size_read};
|
||||
String16 string16 = ToString16(scratch, string);
|
||||
|
||||
if (string.len) {
|
||||
IKnowWhatImDoing_Append(temp_buffer, string16);
|
||||
}
|
||||
|
||||
bool exited = PollExitCode(&process);
|
||||
if (exited) {
|
||||
CloseProcess(&process);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (process.error_message.len) {
|
||||
ReportConsolef("%.*s", FmtString(process.error_message));
|
||||
for (Process process = SpawnProcess(cmd, working_dir, stdin_string); IsValid(&process);) {
|
||||
Scratch scratch(allocator);
|
||||
String poll = PollStdout(scratch, &process);
|
||||
if (poll.len) IKnowWhatImDoing_Append(temp_buffer, poll);
|
||||
}
|
||||
|
||||
return temp_buffer;
|
||||
}
|
||||
|
||||
void ExecAndWait(View *view, String cmd, String working_dir, String stdin_string = {}) {
|
||||
Process process = CreateCommandLineProcess(cmd, working_dir);
|
||||
if (process.error_message.len) ReportWarningf("%.*s", FmtString(process.error_message));
|
||||
if (!process.is_valid) return;
|
||||
|
||||
if (stdin_string.len) {
|
||||
WriteStdin(&process, stdin_string);
|
||||
CloseStdin(&process);
|
||||
}
|
||||
|
||||
Scratch scratch;
|
||||
char *buffer = AllocArray(scratch, char, 4096);
|
||||
Buffer *the_buffer = GetBuffer(view->active_buffer);
|
||||
Range range = GetRange(*the_buffer);
|
||||
for (;;) {
|
||||
StdoutPollInfo info = PollStdout(&process, buffer, 4096);
|
||||
String string = {buffer, info.size_read};
|
||||
String16 string16 = ToString16(scratch, string);
|
||||
|
||||
if (string.len) {
|
||||
Command_ReplaceWithoutMovingCarets(view, range, string16);
|
||||
range = GetEndAsRange(*the_buffer);
|
||||
}
|
||||
|
||||
bool exited = PollExitCode(&process);
|
||||
if (exited) {
|
||||
CloseProcess(&process);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (process.error_message.len) {
|
||||
ReportConsolef("%.*s", FmtString(process.error_message));
|
||||
}
|
||||
}
|
||||
|
||||
void KillProcess(ViewID view) {
|
||||
IterRemove(ActiveProcesses) {
|
||||
IterRemovePrepare(ActiveProcesses);
|
||||
|
||||
@@ -250,15 +250,6 @@ int main(int argc, char **argv)
|
||||
SDL_free(sdl_config_path.data);
|
||||
}
|
||||
|
||||
// Process process = CreateCommandLineProcess("git log", WorkingDir);
|
||||
// Scratch scratch;
|
||||
// char *buffer = AllocArray(scratch, char, 4096);
|
||||
// for (int i = 0; i < 4; i += 1) {
|
||||
// StdoutPollInfo info = PollStdout(&process, buffer, 4096);
|
||||
// bool exited = PollExitCode(&process);
|
||||
// if (exited) break;
|
||||
// }
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) == -1) {
|
||||
ReportErrorf("Couldn't initialize SDL! %s", SDL_GetError());
|
||||
return 1;
|
||||
@@ -315,6 +306,7 @@ int main(int argc, char **argv)
|
||||
ReloadFont();
|
||||
InitLuaConfig();
|
||||
InitWindows();
|
||||
InitOS(ReportWarningf);
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
String it = argv[i];
|
||||
|
||||
Reference in New Issue
Block a user