Luna/libos/src/File.cpp

331 lines
7.2 KiB
C++

/**
* @file File.cpp
* @author apio (cloudapio.eu)
* @brief A C++-friendly API for file access.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <errno.h>
#include <luna/Format.h>
#include <luna/StringBuilder.h>
#include <os/File.h>
#include <unistd.h>
static SharedPtr<os::File> g_stdin = {};
static SharedPtr<os::File> g_stdout = {};
static SharedPtr<os::File> g_stderr = {};
static const char* stdio_mode_from_openmode(int mode)
{
mode &= ~(O_CREAT | O_EXCL);
switch (mode)
{
case os::File::ReadOnly: return "r";
case os::File::ReadWrite: return "w+";
case os::File::WriteOnly: return "w";
case os::File::ReadAppend: return "a+";
case os::File::Append: return "a";
default: fail("Mode incompatible with fdopen()");
}
}
namespace os
{
File::File(Badge<File>)
{
}
File::~File()
{
if (m_file) fclose(m_file);
}
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_file = stdin;
g_stdout = adopt_shared_if_nonnull(new (std::nothrow) File({}))
.expect_release_value("Cannot open standard output stream");
g_stdout->m_file = stdout;
g_stderr = adopt_shared_if_nonnull(new (std::nothrow) File({}))
.expect_release_value("Cannot open standard error stream");
g_stderr->m_file = stderr;
}
SharedPtr<File> File::standard_input()
{
if (!g_stdin) initialize_standard_streams();
return g_stdin;
}
SharedPtr<File> File::standard_output()
{
if (!g_stdout) initialize_standard_streams();
return g_stdout;
}
SharedPtr<File> File::standard_error()
{
if (!g_stderr) initialize_standard_streams();
return g_stderr;
}
Result<SharedPtr<File>> File::construct(const Path& path, int flags, mode_t mode)
{
auto file = TRY(adopt_shared_if_nonnull(new (std::nothrow) File({})));
int fd = openat(path.dirfd(), path.name().chars(), flags, mode);
if (fd < 0) return err(errno);
file->m_file = fdopen(fd, stdio_mode_from_openmode(flags));
if (!file->m_file) return err(errno);
return file;
}
Result<SharedPtr<File>> File::open(const Path& path, OpenMode flags)
{
return construct(path, (int)flags, 0);
}
Result<SharedPtr<File>> File::open_or_create(const Path& path, OpenMode flags, mode_t mode)
{
return construct(path, (int)flags | O_CREAT, mode);
}
Result<SharedPtr<File>> File::create(const Path& path, OpenMode flags, mode_t mode)
{
return construct(path, (int)flags | (O_CREAT | O_EXCL), mode);
}
Result<SharedPtr<File>> File::open_input_file(StringView path)
{
if (path == "-"_sv) return standard_input();
return construct(path, O_RDONLY, 0);
}
Result<usize> File::raw_read(u8* buf, usize length)
{
size_t nread = fread(buf, 1, length, m_file);
if (nread == 0 && ferror(m_file)) return err(errno);
return nread;
}
Result<usize> File::raw_write(const u8* buf, usize length)
{
size_t nwrite = fwrite(buf, 1, length, m_file);
if (nwrite == 0 && ferror(m_file)) return err(errno);
return nwrite;
}
Result<void> File::write(StringView str)
{
TRY(raw_write((const u8*)str.chars(), str.length()));
return {};
}
Result<void> File::write(const Buffer& buf)
{
TRY(raw_write(buf.data(), buf.size()));
return {};
}
Result<usize> File::write_formatted(const char* format, ...)
{
va_list ap;
va_start(ap, format);
auto result = write_vformatted(format, ap);
va_end(ap);
return result;
}
Result<usize> File::write_vformatted(const char* format, va_list args)
{
auto rc = TRY(cstyle_format(
format,
[](char c, void* f) -> Result<void> {
TRY(((File*)f)->raw_write((const u8*)&c, 1));
return {};
},
this, args));
flush();
return rc;
}
Result<String> File::read_line()
{
Vector<char> data;
int current;
while (true)
{
current = TRY(getchar());
if (current == EOF) break;
TRY(data.try_append((char)current));
if (current == '\n') break;
}
if (!data.size()) return String {};
TRY(data.try_append('\0'));
return String { data.release_data() };
}
Result<String> 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<Buffer> File::read_all()
{
Buffer data;
u8 buf[2048];
while (true)
{
usize nread = TRY(raw_read(buf, sizeof(buf)));
TRY(data.append_data(buf, nread));
if (nread < sizeof(buf)) break;
}
return data;
}
Result<void> 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<int> File::getchar()
{
int rc = fgetc(m_file);
if (rc == EOF && ferror(m_file)) return err(errno);
return rc;
}
void File::set_close_on_exec()
{
fcntl(fd(), F_SETFD, FD_CLOEXEC);
}
void File::rewind()
{
::rewind(m_file);
}
void File::flush()
{
fflush(m_file);
}
Result<void> File::seek(off_t offset, SeekMode mode)
{
int rc = fseek(m_file, offset, (int)mode);
if (rc < 0) return err(errno);
return {};
}
Result<off_t> File::tell()
{
off_t off = ftell(m_file);
if (off < 0) return err(errno);
return off;
}
void File::set_buffer(BufferingMode mode)
{
setvbuf(m_file, NULL, mode, 0);
}
Result<void> print(const char* format, ...)
{
va_list ap;
va_start(ap, format);
TRY(File::standard_output()->write_vformatted(format, ap));
va_end(ap);
return {};
}
Result<void> println(const char* format, ...)
{
va_list ap;
va_start(ap, format);
auto file = File::standard_output();
TRY(file->write_vformatted(format, ap));
TRY(file->write("\n"_sv));
va_end(ap);
return {};
}
Result<void> eprint(const char* format, ...)
{
va_list ap;
va_start(ap, format);
TRY(File::standard_error()->write_vformatted(format, ap));
va_end(ap);
return {};
}
Result<void> eprintln(const char* format, ...)
{
va_list ap;
va_start(ap, format);
auto file = File::standard_error();
TRY(file->write_vformatted(format, ap));
TRY(file->write("\n"_sv));
va_end(ap);
return {};
}
}