struct TH_Process { bool is_valid; char platform[32]; }; #if OS_WINDOWS TH_Process TH_CreateProcess(S8_String in_cmd, S8_String in_working_dir = "") { MA_Scratch scratch; if (in_working_dir != "" && !OS_IsAbsolute(in_working_dir)) { in_working_dir = OS_GetAbsolutePath(scratch, in_working_dir); } wchar_t *application_name = NULL; wchar_t *cmd = S8_ToWidechar(scratch, in_cmd); BOOL inherit_handles = FALSE; DWORD creation_flags = 0; void *enviroment = NULL; wchar_t *working_dir = in_working_dir == "" ? NULL : S8_ToWidechar(scratch, in_working_dir); STARTUPINFOW startup_info = {}; startup_info.cb = sizeof(STARTUPINFOW); TH_Process result = {}; IO_Assert(sizeof(result.platform) >= sizeof(PROCESS_INFORMATION)); PROCESS_INFORMATION *process_info = (PROCESS_INFORMATION *)result.platform; BOOL success = CreateProcessW(application_name, cmd, NULL, NULL, inherit_handles, creation_flags, enviroment, working_dir, &startup_info, process_info); result.is_valid = true; if (!success) { result.is_valid = false; 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); LocalFree(lpMsgBuf); IO_FatalErrorf("Failed to create process \nworking_dir: %.*s\ncmd: %.*s\nwindows_message: %s", S8_Expand(in_working_dir), S8_Expand(in_cmd), lpMsgBuf); } return result; } int TH_WaitForProcessExit(TH_Process *process) { IO_Assert(process->is_valid); PROCESS_INFORMATION *pi = (PROCESS_INFORMATION *)process->platform; WaitForSingleObject(pi->hProcess, INFINITE); DWORD exit_code; BOOL err = GetExitCodeProcess(pi->hProcess, &exit_code); IO_Assert(err != 0); CloseHandle(pi->hProcess); CloseHandle(pi->hThread); process[0] = {}; return (int)exit_code; } #else #include #include struct TH_UnixProcess { pid_t pid; }; extern char **environ; TH_Process TH_CreateProcess(S8_String cmd, S8_String working_dir = "") { MA_Scratch scratch; if (working_dir != "" && !OS_IsAbsolute(working_dir)) { working_dir = OS_GetAbsolutePath(scratch, working_dir); } TH_Process result = {}; IO_Assert(sizeof(result.platform) >= sizeof(TH_UnixProcess)); TH_UnixProcess *u = (TH_UnixProcess *)result.platform; S8_String exec_file = cmd; S8_String argv = ""; int64_t pos; if (S8_Seek(cmd, S8_Lit(" "), 0, &pos)) { exec_file = S8_GetPrefix(cmd, pos); argv = S8_Skip(cmd, pos + 1); } exec_file = S8_Copy(scratch, exec_file); // Split string on whitespace and conform with argv format Array args = {scratch}; { args.add(exec_file.str); for (int64_t i = 0; i < argv.len;) { while (i < argv.len && CHAR_IsWhitespace(argv.str[i])) { i += 1; } S8_String word = {argv.str + i, 0}; while (i < argv.len && !CHAR_IsWhitespace(argv.str[i])) { word.len += 1; i += 1; } word = S8_Copy(scratch, word); args.add(word.str); } args.add(NULL); } S8_String prev_dir = {}; if (working_dir != "") prev_dir = OS_GetWorkingDir(scratch); if (working_dir != "") OS_SetWorkingDir(working_dir); int err = posix_spawnp(&u->pid, exec_file.str, NULL, NULL, args.data, environ); if (err == 0) { result.is_valid = true; } else { perror("Failed to create process"); IO_FatalErrorf("Failed to create process \nworking_dir: %.*s\ncmd: %.*s", S8_Expand(working_dir), S8_Expand(cmd)); } if (working_dir != "") OS_SetWorkingDir(prev_dir); return result; } int TH_WaitForProcessExit(TH_Process *process) { if (!process->is_valid) return 1; TH_UnixProcess *u = (TH_UnixProcess *)process->platform; int status = 0; int pid = waitpid(u->pid, &status, 0); IO_Assert(pid != -1); int result = 0; if (WIFEXITED(status)) { result = WEXITSTATUS(status); } else { result = 1; } process[0] = {}; return result; } #endif