#include "filesystem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void (*Error)(const char *, ...); void *VReserve(size_t size) { void *result = mmap(0, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, (off_t)0); return result == (void *)-1 ? 0 : result; } bool VCommit(void *p, size_t size) { int result = mprotect(p, size, PROT_READ | PROT_WRITE); return result == 0; } bool VRelease(void *p, size_t size) { int result = munmap(p, size); return result == 0; } bool VDecommit(void *p, size_t size) { mprotect(p, size, PROT_NONE); madvise(p, size, MADV_DONTNEED); return true; } void InitOS(void (*error_proc)(const char *, ...)) { Error = error_proc; } String ReadFile(Allocator al, String path) { Scratch scratch(al); String null_term = Copy(scratch, path); String result = {}; FILE *f = fopen(null_term.data, "rb"); if (f) { fseek(f, 0, SEEK_END); result.len = ftell(f); fseek(f, 0, SEEK_SET); result.data = (char *)AllocSize(al, result.len + 1); fread(result.data, result.len, 1, f); result.data[result.len] = 0; fclose(f); } return result; } bool WriteFile(String path, String data) { Scratch scratch; String null_term = Copy(scratch, path); bool result = false; FILE *f = fopen((const char *)null_term.data, "w"); if (f) { size_t written = fwrite(data.data, 1, data.len, f); if (written == data.len) { result = true; } fclose(f); } return result; } bool DeleteFile(String path) { Scratch scratch; String null_term = Copy(scratch, path); int result = unlink(null_term.data); return result == 0; } MakeDirResult MakeDir(String path) { Scratch scratch; String null_term = Copy(scratch, path); int error = mkdir(null_term.data, 0755); MakeDirResult result = MakeDirResult_Success; if (error != 0) { result = MakeDirResult_ErrorOther; if (errno == EEXIST) result = MakeDirResult_Exists; } return result; } int64_t GetFileModTime(String path) { Scratch scratch; String null_term = Copy(scratch, path); struct stat attrib = {}; stat(null_term.data, &attrib); struct timespec ts = attrib.st_mtim; int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll; return result; } String GetAbsolutePath(Allocator al, String path) { Scratch scratch(al); String null_term = Copy(scratch, path); char *buffer = AllocArray(al, char, PATH_MAX); realpath(null_term.data, buffer); String result = buffer; return buffer; } bool FileExists(String path) { Scratch scratch; String null_term = Copy(scratch, path); bool result = false; if (access((char *)null_term.data, F_OK) == 0) { result = true; } return result; } bool IsDir(String path) { Scratch scratch; String null_term = Copy(scratch, path); struct stat s; if (stat(null_term.data, &s) != 0) return false; return S_ISDIR(s.st_mode); } bool IsFile(String path) { Scratch scratch; String null_term = Copy(scratch, path); struct stat s; if (stat(null_term.data, &s) != 0) return false; return S_ISREG(s.st_mode); } bool IsAbsolute(String path) { bool result = path.len && path.data[0] == '/'; return result; } String GetWorkingDir(Allocator al) { char *buffer = AllocArray(al, char, PATH_MAX); char *cwd = getcwd(buffer, PATH_MAX); return cwd; } String GetExePath(Allocator al) { char *buffer = AllocArray(al, char, PATH_MAX); readlink("/proc/self/exe", buffer, PATH_MAX); return buffer; } String GetExeDir(Allocator al) { Scratch scratch(al); String exe_path = GetExePath(scratch); String dir = ChopLastSlash(exe_path); String result = Copy(al, dir); return result; } double get_time_in_micros(void) { struct timespec spec; clock_gettime(CLOCK_MONOTONIC, &spec); return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000); } bool IsValid(const FileIter &it) { return it.is_valid; } void Advance(FileIter *it) { struct dirent *file = NULL; while ((file = readdir((DIR *)it->dir)) != NULL) { if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) { continue; } if (file->d_name[0] == '.' && file->d_name[1] == 0) { continue; } it->is_directory = file->d_type == DT_DIR; it->filename = Copy(it->allocator, file->d_name); const char *dir_char_ending = it->is_directory ? "/" : ""; const char *separator = it->path.data[it->path.len - 1] == '/' ? "" : "/"; it->relative_path = Format(it->allocator, "%.*s%s%s%s", FmtString(it->path), separator, file->d_name, dir_char_ending); it->absolute_path = GetAbsolutePath(it->allocator, it->relative_path); if (it->is_directory) it->absolute_path = Format(it->allocator, "%.*s/", FmtString(it->absolute_path)); it->is_valid = true; return; } it->is_valid = false; closedir((DIR *)it->dir); } FileIter IterateFiles(Allocator alo, String path) { FileIter it = {}; it.allocator = alo; it.path = path; Scratch scratch(alo); String null_term = Copy(scratch, path); it.dir = (void *)opendir((char *)null_term.data); if (it.dir) { Advance(&it); } return it; } // struct Process { // bool is_valid; // int exit_code; // char platform[6 * 8]; // // int64_t view_id; // text editor view // bool scroll_to_end; // }; struct UnixProcess { pid_t pid; int child_stdout_read; int stdin_write; }; Array SplitCommand(Allocator allocator, String command_line) { Array 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 enviroment) { Scratch scratch; const int PIPE_READ = 0; const int PIPE_WRITE = 1; bool error = false; printf("cmd = %.*s\n", FmtString(command_line)); printf("cwd = %.*s\n", FmtString(working_dir)); char *buffer = AllocArray(scratch, char, 4096); chdir(working_dir.data); getcwd(buffer, 4096); defer { chdir(buffer); }; Process process = {}; UnixProcess *plat = (UnixProcess *)&process.platform; Array args = SplitCommand(scratch, command_line); Array 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) { perror("posix_spawn_file_actions_init"); return process; } defer { posix_spawn_file_actions_destroy(&actions); }; if (pipe(stdout_desc) == -1) { perror("pipe"); 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) { perror("pipe"); 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) { perror("posix_spawn_file_actions_addclose"); return process; } error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDOUT_FILENO) != 0; if (error) { perror("posix_spawn_file_actions_adddup2 STDOUT_FILENO"); return process; } error = posix_spawn_file_actions_adddup2(&actions, stdout_desc[PIPE_WRITE], STDERR_FILENO) != 0; if (error) { perror("posix_spawn_file_actions_adddup2 STDERR_FILENO"); return process; } error = posix_spawn_file_actions_addclose(&actions, stdin_desc[PIPE_WRITE]) != 0; if (error) { perror("posix_spawn_file_actions_addclose"); return process; } error = posix_spawn_file_actions_adddup2(&actions, stdin_desc[PIPE_READ], STDIN_FILENO) != 0; if (error) { perror("posix_spawn_file_actions_adddup2 STDIN_FILENO"); return process; } pid_t process_pid = 0; error = posix_spawnp(&process_pid, args[0], &actions, NULL, args.data, env.data) != 0; if (error) { perror("failed to create process\n"); return process; } process.is_valid = true; plat->child_stdout_read = stdout_desc[PIPE_READ]; plat->stdin_write = stdin_desc[PIPE_WRITE]; if (write_stdin.len) { WriteStdin(&process, write_stdin); CloseStdin(&process); } return process; } bool IsValid(Process *process) { UnixProcess *plat = (UnixProcess *)&process->platform; if (process->is_valid == false) { return false; } int status = 0; pid_t result = waitpid(plat->pid, &status, WNOHANG); if (result >= 0) { if (WIFSIGNALED(status) || WIFEXITED(status)) { 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 == 1 || 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); }