diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-12-25 18:54:07 +0330 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-12-30 13:31:55 +0100 |
commit | 4e203f7e2d2c71af5ba8b833c98dcb67dd8dd68c (patch) | |
tree | 9e383031d5558cad0d876a340de81a6cb6f38173 /AK | |
parent | bf7cda414fa5e98f6b3c2b901dc7bdf6435c1b9b (diff) | |
download | serenity-4e203f7e2d2c71af5ba8b833c98dcb67dd8dd68c.zip |
AK: Add {Input,Output}FileStream
Unlike the ones in LibCore, these only wrap an stdio FILE* (or an fd,
which they close when destroyed).
Diffstat (limited to 'AK')
-rw-r--r-- | AK/FileStream.h | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/AK/FileStream.h b/AK/FileStream.h new file mode 100644 index 0000000000..0355ec16f9 --- /dev/null +++ b/AK/FileStream.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Stream.h> +#ifndef KERNEL +# include <AK/LogStream.h> +# include <errno.h> +# include <stdio.h> + +namespace AK { + +class InputFileStream : public InputStream { +public: + explicit InputFileStream(int fd) + : m_file(fdopen(fd, "r")) + , m_owned(true) + { + if (!m_file) + set_fatal_error(); + } + + explicit InputFileStream(FILE* fp) + : m_file(fp) + { + if (!m_file) + set_fatal_error(); + } + + ~InputFileStream() + { + if (m_file) { + if (m_owned) + fflush(m_file); + } + } + + bool unreliable_eof() const override { return eof(); } + bool eof() const { return feof(m_file); } + + size_t read(Bytes bytes) override + { + if (has_any_error()) + return 0; + return fread(bytes.data(), sizeof(u8), bytes.size(), m_file); + } + + bool read_or_error(Bytes bytes) override + { + if (has_any_error()) + return false; + auto size = read(bytes); + if (size < bytes.size()) { + set_recoverable_error(); + return false; + } + return true; + } + bool discard_or_error(size_t count) override + { + if (fseek(m_file, count, SEEK_CUR) == 0) + return true; + + // TODO: Why not ferror(m_file)? + if (errno != ESPIPE) + return false; + + char buf[4]; + size_t i = 0; + while (i < count) { + auto size = min(count - i, 4ul); + if (read({ buf, size }) < size) { + // Can't reset here. + return false; + } + i += size; + } + + return true; + } + + bool seek(size_t offset, int whence = SEEK_SET) + { + return fseek(m_file, offset, whence) == 0; + } + + virtual bool handle_any_error() override + { + clearerr(m_file); + return Stream::handle_any_error(); + } + + void make_unbuffered() + { + setvbuf(m_file, nullptr, _IONBF, 0); + } + +private: + FILE* m_file { nullptr }; + bool m_owned { false }; +}; + +class OutputFileStream : public OutputStream { +public: + explicit OutputFileStream(int fd) + : m_file(fdopen(fd, "w")) + , m_owned(true) + { + if (!m_file) + set_fatal_error(); + } + + explicit OutputFileStream(FILE* fp) + : m_file(fp) + { + if (!m_file) + set_fatal_error(); + } + + ~OutputFileStream() + { + if (m_file) { + fflush(m_file); + if (m_owned) + fclose(m_file); + } + } + + size_t write(ReadonlyBytes bytes) override + { + auto nwritten = fwrite(bytes.data(), sizeof(u8), bytes.size(), m_file); + m_bytes_written += nwritten; + return nwritten; + } + + bool write_or_error(ReadonlyBytes bytes) override + { + auto nwritten = write(bytes); + if (nwritten < bytes.size()) { + set_recoverable_error(); + return false; + } + return true; + } + + size_t size() const { return m_bytes_written; } + + virtual bool handle_any_error() override + { + clearerr(m_file); + return Stream::handle_any_error(); + } + + void make_unbuffered() + { + setvbuf(m_file, nullptr, _IONBF, 0); + } + +private: + FILE* m_file { nullptr }; + size_t m_bytes_written { 0 }; + bool m_owned { false }; +}; + +} + +using AK::InputFileStream; +using AK::OutputFileStream; + +#endif |