#include #include #include #include #include static SharedPtr g_stdin = {}; static SharedPtr g_stdout = {}; static SharedPtr g_stderr = {}; namespace os { File::File(Badge) { } File::~File() { if (m_fd >= 0) close(m_fd); } void File::initialize_standard_streams() { g_stdin = adopt_shared_if_nonnull(new (std::nothrow) File({})) .expect_release_value("Cannot open standard input stream"); g_stdin->m_fd = STDIN_FILENO; g_stdout = adopt_shared_if_nonnull(new (std::nothrow) File({})) .expect_release_value("Cannot open standard output stream"); g_stdout->m_fd = STDOUT_FILENO; g_stderr = adopt_shared_if_nonnull(new (std::nothrow) File({})) .expect_release_value("Cannot open standard error stream"); g_stderr->m_fd = STDERR_FILENO; } SharedPtr File::standard_input() { if (!g_stdin) initialize_standard_streams(); return g_stdin; } SharedPtr File::standard_output() { if (!g_stdout) initialize_standard_streams(); return g_stdout; } SharedPtr File::standard_error() { if (!g_stderr) initialize_standard_streams(); return g_stderr; } Result> File::construct(StringView path, int flags, mode_t mode) { auto file = TRY(adopt_shared_if_nonnull(new (std::nothrow) File({}))); long rc = syscall(SYS_openat, AT_FDCWD, path.chars(), flags, mode); int fd = TRY(Result::from_syscall(rc)); file->m_fd = fd; return file; } Result> File::open(StringView path, OpenMode flags) { return construct(path, (int)flags, 0); } Result> File::open_or_create(StringView path, OpenMode flags, mode_t mode) { return construct(path, (int)flags | O_CREAT, mode); } Result> File::create(StringView path, OpenMode flags, mode_t mode) { return construct(path, (int)flags | (O_CREAT | O_EXCL), mode); } Result> File::open_input_file(StringView path) { if (path == "-"_sv) return standard_input(); return construct(path, O_RDONLY, 0); } Result File::raw_read(u8* buf, usize length) { long rc = syscall(SYS_read, m_fd, buf, length); return Result::from_syscall(rc); } Result File::raw_write(const u8* buf, usize length) { long rc = syscall(SYS_write, m_fd, buf, length); return Result::from_syscall(rc); } Result File::write(StringView str) { TRY(raw_write((const u8*)str.chars(), str.length())); return {}; } Result File::write(const Buffer& buf) { TRY(raw_write(buf.data(), buf.size())); return {}; } Result File::read_line() { Vector data; int current; while (true) { current = TRY(getchar()); if (current == -1) break; TRY(data.try_append((char)current)); if (current == '\n') break; } if (!data.size()) return String {}; TRY(data.try_append('\0')); return String::from_cstring(data.data()); } Result File::read_all_as_string() { StringBuilder sb; while (true) { auto line = TRY(read_line()); if (line.is_empty()) break; TRY(sb.add(line)); } return sb.string(); } Result File::read_all() { Vector data; while (true) { int c = TRY(getchar()); if (c == -1) break; TRY(data.try_append((u8)c)); } Buffer buf; TRY(buf.append_data(data.data(), data.size())); return buf; } Result File::read(Buffer& buf, usize size) { u8* slice = TRY(buf.slice(0, size)); usize nread = TRY(raw_read(slice, size)); TRY(buf.try_resize(nread)); return {}; } Result File::getchar() { u8 value; usize nread = TRY(raw_read(&value, 1)); if (!nread) return -1; return value; } void File::set_close_on_exec() { fcntl(m_fd, F_SETFD, FD_CLOEXEC); } // FIXME: Do not allocate memory for printing. Result print_impl(SharedPtr f, StringView fmt, va_list ap) { auto str = TRY(String::vformat(fmt, ap)); return f->write(str.view()); } Result print(StringView fmt, ...) { va_list ap; va_start(ap, fmt); auto rc = print_impl(File::standard_output(), fmt, ap); va_end(ap); return rc; } Result println(StringView fmt, ...) { va_list ap; va_start(ap, fmt); auto rc = print_impl(File::standard_output(), fmt, ap); va_end(ap); TRY(rc); return File::standard_output()->write("\n"_sv); } Result eprint(StringView fmt, ...) { va_list ap; va_start(ap, fmt); auto rc = print_impl(File::standard_error(), fmt, ap); va_end(ap); return rc; } Result eprintln(StringView fmt, ...) { va_list ap; va_start(ap, fmt); auto rc = print_impl(File::standard_error(), fmt, ap); va_end(ap); TRY(rc); return File::standard_error()->write("\n"_sv); } }