OS_Handle FS_FileOpen(Str8 path, FS_AccessFlags access) { OS_Handle result = { 0 }; M_TempScope(0, 0) { Str8 zpath = Str8_Copy(temp.arena, path); int mode = 0644; int flags = 0; if (access & FS_ACCESS_READ) { if (access & FS_ACCESS_WRITE) { flags = O_RDWR | O_CREAT; } else { flags = O_RDONLY; } } else if (access & FS_ACCESS_WRITE) { flags = O_WRONLY | O_CREAT; } int fd = open((const char *) zpath.data, flags, mode); result.v[0] = (fd > 0) ? (U64) fd : 0; } return result; } void FS_FileClose(OS_Handle file) { int fd = cast(int) file.v[0]; if (fd > 0) { close(fd); } } void FS_FileRead(OS_Handle file, void *ptr, U64 size, U64 offset) { int fd = cast(int) file.v[0]; if (fd > 0) { U8 *buffer = ptr; U64 current = offset; U64 remainder = size; while (remainder != 0) { S64 nread = pread(fd, buffer, remainder, current); if (nread <= 0) { break; } buffer += nread; current += nread; remainder -= nread; } } } void FS_FileWrite(OS_Handle file, void *ptr, U64 size, U64 offset) { int fd = cast(int) file.v[0]; if (fd > 0) { U8 *buffer = ptr; U64 current = offset; U64 remainder = size; while (remainder != 0) { S64 nwritten = pwrite(fd, buffer, remainder, current); if (nwritten <= 0) { break; } buffer += nwritten; current += nwritten; remainder -= nwritten; } } } U64 FS_FileSize(OS_Handle file) { U64 result = 0; int fd = cast(int) file.v[0]; if (fd > 0) { struct stat sb; fstat(fd, &sb); result = sb.st_size; } return result; } FS_List FS_PathList(M_Arena *arena, Str8 path) { FS_List result = { 0 }; M_TempScope(1, &arena) { Str8 zpath = Str8_Copy(temp.arena, path); DIR *dir = opendir((const char *) zpath.data); struct dirent *ent = readdir(dir); while (ent != 0) { if (ent->d_name[0] != '.') { FS_Entry *entry = M_ArenaPush(arena, FS_Entry); entry->basename = Str8_Copy(arena, Sz(ent->d_name)); entry->path = Sf(arena, "%.*s/%.*s", Sv(path), Sv(entry->basename)); struct stat sb; stat((const char *) entry->path.data, &sb); entry->type = ((sb.st_mode & S_IFMT) == S_IFDIR) ? FS_ENTRY_TYPE_DIR : FS_ENTRY_TYPE_FILE; entry->time = (sb.st_mtim.tv_sec * 1e9) + sb.st_mtim.tv_nsec; entry->size = sb.st_size; SLL_Enqueue(result.first, result.last, entry); result.count += 1; } ent = readdir(dir); } } return result; } Str8 FS_SystemPath(M_Arena *arena, FS_SystemPathType path) { Str8 result = { 0 }; switch (path) { case FS_SYSTEM_PATH_EXE: { U64 offset = M_ArenaOffset(arena); Str8 buffer; buffer.count = KB(1); buffer.data = M_ArenaPush(arena, U8, .count = buffer.count); ssize_t nwritten = 0; for (;;) { nwritten = readlink("/proc/self/exe", cast(char *) buffer.data, buffer.count); if (nwritten < buffer.count) { break; } M_ArenaPop(arena, offset); buffer.count *= 2; buffer.data = M_ArenaPush(arena, U8, .count = buffer.count); } if (nwritten > 0) { // Walk back until the prevous slash, then null-terminate there while (buffer.data[nwritten] != '/') { nwritten -= 1; } buffer.data[nwritten] = 0; M_ArenaPopSize(arena, buffer.count - nwritten - 1); result.count = nwritten; result.data = buffer.data; } else { M_ArenaPop(arena, offset); } } break; case FS_SYSTEM_PATH_WORKING: { U64 offset = M_ArenaOffset(arena); Str8 buffer; buffer.count = KB(1); buffer.data = M_ArenaPush(arena, U8, .count = buffer.count); char *wd; for (;;) { wd = getcwd((char *) buffer.data, buffer.count); if (wd != 0) { break; } else if (errno != ERANGE) { break; } M_ArenaPop(arena, offset); buffer.count *= 2; buffer.data = M_ArenaPush(arena, U8, .count = buffer.count); } if (wd) { result = Sz(wd); M_ArenaPopSize(arena, buffer.count - result.count - 1); } } break; case FS_SYSTEM_PATH_USER: { char *home = getenv("HOME"); if (home) { result = Sf(arena, "%s/.local/share", home); } } break; case FS_SYSTEM_PATH_TEMP: { char *temp = getenv("TEMP"); if (temp) { result = Str8_Copy(arena, Sz(temp)); } else { result = Str8_Copy(arena, S("/tmp")); } } break; } return result; }