/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2010-2018 Danny Robson */ #include "io.hpp" #include "debug.hpp" #include "cast.hpp" #include "format.hpp" #include "posix/except.hpp" #include #include #include #include #include #include using namespace util; ////////////////////////////////////////////////////////////////////////////// template std::vector util::slurp (const std::experimental::filesystem::path &path) { posix::fd out (path, O_RDONLY | O_BINARY); // Calculate the total file size off_t size = posix::error::try_value (lseek (out, 0, SEEK_END)); if (size % sizeof (T)) throw std::runtime_error ("filesize not correctly rounded"); posix::error::try_value (lseek (out, 0, SEEK_SET)); // Allocate a buffer, and keep reading until it's full. std::vector buffer (size); CHECK_GE (size, 0); size_t remaining = util::cast::sign (size); T *cursor = buffer.data (); while (remaining) { ssize_t consumed = posix::error::try_value( ::read (out, cursor, remaining) ); CHECK_GT (consumed, 0); CHECK_LE (util::cast::sign (consumed), remaining); remaining -= util::cast::sign (consumed); cursor += consumed; } return buffer; } //----------------------------------------------------------------------------- template std::vector util::slurp (const std::experimental::filesystem::path&); template std::vector util::slurp (const std::experimental::filesystem::path&); template std::vector util::slurp (const std::experimental::filesystem::path&); template std::vector util::slurp (const std::experimental::filesystem::path&); /////////////////////////////////////////////////////////////////////////////// template std::vector util::slurp (FILE *stream) { static_assert ( sizeof (T) == 1, "slurp is designed for grabbing bytes, not complex structures" ); // find how much data is in this file const int desc = util::posix::error::try_value (fileno (stream)); struct stat meta; posix::error::try_value (fstat (desc, &meta)); std::vector buf; // we think we know the size, so try to do a simple read if (meta.st_size) { buf.resize (meta.st_size); // read as much as possible, then resize to the actual length auto res = fread (buf.data (), 1, meta.st_size, stream); buf.resize (res); } // try reading small chunks until we've hit the end. important for // handling pipe streams (like from popen) which report a zero size. constexpr size_t CHUNK_SIZE = 128; size_t cursor = buf.size (); while (!feof (stream) && !ferror (stream)) { auto oldsize = buf.size (); buf.resize (oldsize + CHUNK_SIZE); auto res = fread (buf.data () + cursor, 1, CHUNK_SIZE, stream); if (res != CHUNK_SIZE) buf.resize (oldsize + res); } if (ferror (stream)) throw stream_error (); return buf; } //----------------------------------------------------------------------------- template std::vector util::slurp (FILE*); template std::vector util::slurp (FILE*); ////////////////////////////////////////////////////////////////////////////// int indenter::overflow (int ch) { if (m_line_start && ch != '\n') m_dest->sputn (m_indent.data (), util::cast::sign (m_indent.size ())); m_line_start = ch == '\n'; return m_dest->sputc (ch); } //----------------------------------------------------------------------------- indenter::indenter (std::streambuf* _dest, size_t _indent) : m_dest (_dest) , m_line_start (true) , m_indent (_indent, ' ') , m_owner (NULL) { ; } //----------------------------------------------------------------------------- indenter::indenter (std::ostream& _dest, size_t _indent) : m_dest (_dest.rdbuf()) , m_line_start (true) , m_indent (_indent, ' ') , m_owner (&_dest) { m_owner->rdbuf (this); } //----------------------------------------------------------------------------- indenter::~indenter () { if (m_owner != NULL) m_owner->rdbuf (m_dest); } ////////////////////////////////////////////////////////////////////////////// scoped_cwd::scoped_cwd () { m_original.resize (16); while (getcwd (&m_original[0], m_original.size ()) == nullptr && errno == ERANGE) m_original.resize (m_original.size () * 2); posix::error::try_code (); } //----------------------------------------------------------------------------- scoped_cwd::~scoped_cwd () { if (!chdir (m_original.c_str ())) posix::error::throw_code (); } /////////////////////////////////////////////////////////////////////////////// path_error::path_error (const std::experimental::filesystem::path &_path): runtime_error (to_string (format::printf ("Unknown path: %!", m_path))), m_path (_path) { ; } //----------------------------------------------------------------------------- const std::experimental::filesystem::path& path_error::path (void) const noexcept { return m_path; }