2023-06-09 20:45:06 +00:00
|
|
|
/**
|
|
|
|
* @file File.cpp
|
|
|
|
* @author apio (cloudapio.eu)
|
|
|
|
* @brief A C++-friendly API for file access.
|
|
|
|
*
|
|
|
|
* @copyright Copyright (c) 2023, the Luna authors.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2023-07-22 10:40:02 +00:00
|
|
|
#include <errno.h>
|
2023-04-13 15:31:21 +00:00
|
|
|
#include <luna/StringBuilder.h>
|
2023-04-12 19:46:10 +00:00
|
|
|
#include <os/File.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2023-04-13 15:31:21 +00:00
|
|
|
static SharedPtr<os::File> g_stdin = {};
|
|
|
|
static SharedPtr<os::File> g_stdout = {};
|
|
|
|
static SharedPtr<os::File> g_stderr = {};
|
|
|
|
|
2023-07-22 10:40:02 +00:00
|
|
|
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()");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-12 19:46:10 +00:00
|
|
|
namespace os
|
|
|
|
{
|
|
|
|
File::File(Badge<File>)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
File::~File()
|
|
|
|
{
|
2023-07-22 10:40:02 +00:00
|
|
|
if (m_file) fclose(m_file);
|
2023-04-12 19:46:10 +00:00
|
|
|
}
|
|
|
|
|
2023-04-13 15:31:21 +00:00
|
|
|
void File::initialize_standard_streams()
|
|
|
|
{
|
|
|
|
g_stdin = adopt_shared_if_nonnull(new (std::nothrow) File({}))
|
|
|
|
.expect_release_value("Cannot open standard input stream");
|
2023-07-22 10:40:02 +00:00
|
|
|
g_stdin->m_file = stdin;
|
2023-04-13 15:31:21 +00:00
|
|
|
|
|
|
|
g_stdout = adopt_shared_if_nonnull(new (std::nothrow) File({}))
|
|
|
|
.expect_release_value("Cannot open standard output stream");
|
2023-07-22 10:40:02 +00:00
|
|
|
g_stdout->m_file = stdout;
|
2023-04-13 15:31:21 +00:00
|
|
|
|
|
|
|
g_stderr = adopt_shared_if_nonnull(new (std::nothrow) File({}))
|
|
|
|
.expect_release_value("Cannot open standard error stream");
|
2023-07-22 10:40:02 +00:00
|
|
|
g_stderr->m_file = stderr;
|
2023-04-13 15:31:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:37:26 +00:00
|
|
|
Result<SharedPtr<File>> File::construct(const Path& path, int flags, mode_t mode)
|
2023-04-12 19:46:10 +00:00
|
|
|
{
|
2023-04-13 15:31:21 +00:00
|
|
|
auto file = TRY(adopt_shared_if_nonnull(new (std::nothrow) File({})));
|
2023-04-12 19:46:10 +00:00
|
|
|
|
2023-05-03 15:37:26 +00:00
|
|
|
long rc = syscall(SYS_openat, path.dirfd(), path.name().chars(), flags, mode);
|
2023-04-12 19:46:10 +00:00
|
|
|
int fd = TRY(Result<int>::from_syscall(rc));
|
|
|
|
|
2023-07-22 10:40:02 +00:00
|
|
|
file->m_file = fdopen(fd, stdio_mode_from_openmode(flags));
|
|
|
|
if (!file->m_file) return err(errno);
|
2023-04-12 19:46:10 +00:00
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:37:26 +00:00
|
|
|
Result<SharedPtr<File>> File::open(const Path& path, OpenMode flags)
|
2023-04-12 19:46:10 +00:00
|
|
|
{
|
|
|
|
return construct(path, (int)flags, 0);
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:37:26 +00:00
|
|
|
Result<SharedPtr<File>> File::open_or_create(const Path& path, OpenMode flags, mode_t mode)
|
2023-04-12 19:46:10 +00:00
|
|
|
{
|
|
|
|
return construct(path, (int)flags | O_CREAT, mode);
|
|
|
|
}
|
|
|
|
|
2023-05-03 15:37:26 +00:00
|
|
|
Result<SharedPtr<File>> File::create(const Path& path, OpenMode flags, mode_t mode)
|
2023-04-12 19:46:10 +00:00
|
|
|
{
|
|
|
|
return construct(path, (int)flags | (O_CREAT | O_EXCL), mode);
|
|
|
|
}
|
|
|
|
|
2023-04-26 18:41:03 +00:00
|
|
|
Result<SharedPtr<File>> File::open_input_file(StringView path)
|
|
|
|
{
|
|
|
|
if (path == "-"_sv) return standard_input();
|
|
|
|
|
|
|
|
return construct(path, O_RDONLY, 0);
|
|
|
|
}
|
|
|
|
|
2023-04-12 19:46:10 +00:00
|
|
|
Result<usize> File::raw_read(u8* buf, usize length)
|
|
|
|
{
|
2023-07-22 10:40:02 +00:00
|
|
|
size_t nread = fread(buf, 1, length, m_file);
|
|
|
|
if (nread == 0 && ferror(m_file)) return err(errno);
|
|
|
|
return nread;
|
2023-04-12 19:46:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Result<usize> File::raw_write(const u8* buf, usize length)
|
|
|
|
{
|
2023-07-22 10:40:02 +00:00
|
|
|
size_t nwrite = fwrite(buf, 1, length, m_file);
|
|
|
|
if (nwrite == 0 && ferror(m_file)) return err(errno);
|
|
|
|
return nwrite;
|
2023-04-12 19:46:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Result<void> File::write(StringView str)
|
|
|
|
{
|
|
|
|
TRY(raw_write((const u8*)str.chars(), str.length()));
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-04-18 14:41:17 +00:00
|
|
|
Result<void> File::write(const Buffer& buf)
|
|
|
|
{
|
|
|
|
TRY(raw_write(buf.data(), buf.size()));
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-04-22 13:21:04 +00:00
|
|
|
Result<String> File::read_line()
|
2023-04-13 15:31:21 +00:00
|
|
|
{
|
|
|
|
Vector<char> data;
|
|
|
|
|
|
|
|
int current;
|
|
|
|
while (true)
|
|
|
|
{
|
2023-08-02 12:46:47 +00:00
|
|
|
current = TRY(getchar());
|
2023-04-13 15:31:21 +00:00
|
|
|
|
2023-08-02 12:46:47 +00:00
|
|
|
if (current == EOF) break;
|
2023-04-13 15:31:21 +00:00
|
|
|
|
|
|
|
TRY(data.try_append((char)current));
|
|
|
|
|
2023-04-22 13:21:04 +00:00
|
|
|
if (current == '\n') break;
|
2023-04-13 15:31:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!data.size()) return String {};
|
|
|
|
|
|
|
|
TRY(data.try_append('\0'));
|
|
|
|
|
2023-07-02 17:30:25 +00:00
|
|
|
return String { data.release_data() };
|
2023-04-13 15:31:21 +00:00
|
|
|
}
|
|
|
|
|
2023-04-26 18:34:09 +00:00
|
|
|
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()
|
|
|
|
{
|
2023-06-03 22:23:06 +00:00
|
|
|
Buffer data;
|
|
|
|
|
|
|
|
u8 buf[2048];
|
2023-04-26 18:34:09 +00:00
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
2023-06-03 22:23:06 +00:00
|
|
|
usize nread = TRY(raw_read(buf, sizeof(buf)));
|
|
|
|
TRY(data.append_data(buf, nread));
|
|
|
|
if (nread < sizeof(buf)) break;
|
2023-04-26 18:34:09 +00:00
|
|
|
}
|
|
|
|
|
2023-06-03 22:23:06 +00:00
|
|
|
return data;
|
2023-04-26 18:34:09 +00:00
|
|
|
}
|
|
|
|
|
2023-04-18 14:41:17 +00:00
|
|
|
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 {};
|
|
|
|
}
|
|
|
|
|
2023-04-13 15:31:21 +00:00
|
|
|
Result<int> File::getchar()
|
|
|
|
{
|
2023-08-02 12:46:47 +00:00
|
|
|
int rc = fgetc(m_file);
|
|
|
|
if (rc == EOF && ferror(m_file)) return err(errno);
|
|
|
|
return rc;
|
2023-04-13 15:31:21 +00:00
|
|
|
}
|
|
|
|
|
2023-04-12 19:46:10 +00:00
|
|
|
void File::set_close_on_exec()
|
|
|
|
{
|
2023-07-22 10:40:02 +00:00
|
|
|
fcntl(fd(), F_SETFD, FD_CLOEXEC);
|
2023-04-12 19:46:10 +00:00
|
|
|
}
|
2023-05-01 17:31:15 +00:00
|
|
|
|
2023-06-09 20:45:06 +00:00
|
|
|
void File::rewind()
|
|
|
|
{
|
2023-07-22 10:40:02 +00:00
|
|
|
::rewind(m_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void File::flush()
|
|
|
|
{
|
|
|
|
fflush(m_file);
|
2023-06-09 20:45:06 +00:00
|
|
|
}
|
|
|
|
|
2023-08-02 09:54:47 +00:00
|
|
|
void File::set_buffer(BufferingMode mode)
|
|
|
|
{
|
|
|
|
setvbuf(m_file, NULL, mode, 0);
|
|
|
|
}
|
|
|
|
|
2023-05-01 17:31:15 +00:00
|
|
|
// FIXME: Do not allocate memory for printing.
|
|
|
|
Result<void> print_impl(SharedPtr<File> f, StringView fmt, va_list ap)
|
|
|
|
{
|
|
|
|
auto str = TRY(String::vformat(fmt, ap));
|
2023-07-22 10:40:02 +00:00
|
|
|
auto rc = f->write(str.view());
|
|
|
|
f->flush();
|
|
|
|
return rc;
|
2023-05-01 17:31:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Result<void> 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<void> 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<void> 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<void> 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);
|
|
|
|
}
|
2023-04-12 19:46:10 +00:00
|
|
|
}
|