summaryrefslogtreecommitdiffstats
path: root/src/base/fs_util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/fs_util.cc')
-rw-r--r--src/base/fs_util.cc183
1 files changed, 183 insertions, 0 deletions
diff --git a/src/base/fs_util.cc b/src/base/fs_util.cc
new file mode 100644
index 0000000..f72aaa6
--- /dev/null
+++ b/src/base/fs_util.cc
@@ -0,0 +1,183 @@
+/**
+ * Copyright (c) 2022, Timothy Stack
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Timothy Stack nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ */
+
+#include "fs_util.hh"
+
+#include "config.h"
+#include "fmt/format.h"
+#include "itertools.hh"
+#include "opt_util.hh"
+
+namespace lnav {
+namespace filesystem {
+
+Result<auto_fd, std::string>
+create_file(const ghc::filesystem::path& path, int flags, mode_t mode)
+{
+ auto fd = openp(path, flags | O_CREAT, mode);
+
+ if (fd == -1) {
+ return Err(fmt::format(FMT_STRING("Failed to open: {} -- {}"),
+ path.string(),
+ strerror(errno)));
+ }
+
+ return Ok(auto_fd(fd));
+}
+
+Result<auto_fd, std::string>
+open_file(const ghc::filesystem::path& path, int flags)
+{
+ auto fd = openp(path, flags);
+
+ if (fd == -1) {
+ return Err(fmt::format(FMT_STRING("Failed to open: {} -- {}"),
+ path.string(),
+ strerror(errno)));
+ }
+
+ return Ok(auto_fd(fd));
+}
+
+Result<std::pair<ghc::filesystem::path, auto_fd>, std::string>
+open_temp_file(const ghc::filesystem::path& pattern)
+{
+ auto pattern_str = pattern.string();
+ char pattern_copy[pattern_str.size() + 1];
+ int fd;
+
+ strcpy(pattern_copy, pattern_str.c_str());
+ if ((fd = mkstemp(pattern_copy)) == -1) {
+ return Err(
+ fmt::format(FMT_STRING("unable to create temporary file: {} -- {}"),
+ pattern.string(),
+ strerror(errno)));
+ }
+
+ return Ok(std::make_pair(ghc::filesystem::path(pattern_copy), auto_fd(fd)));
+}
+
+Result<std::string, std::string>
+read_file(const ghc::filesystem::path& path)
+{
+ try {
+ ghc::filesystem::ifstream file_stream(path);
+
+ if (!file_stream) {
+ return Err(std::string(strerror(errno)));
+ }
+
+ std::string retval;
+ retval.assign((std::istreambuf_iterator<char>(file_stream)),
+ std::istreambuf_iterator<char>());
+ return Ok(retval);
+ } catch (const std::exception& e) {
+ return Err(std::string(e.what()));
+ }
+}
+
+Result<void, std::string>
+write_file(const ghc::filesystem::path& path, const string_fragment& content)
+{
+ auto tmp_pattern = path;
+ tmp_pattern += ".XXXXXX";
+
+ auto tmp_pair = TRY(open_temp_file(tmp_pattern));
+ auto bytes_written
+ = write(tmp_pair.second.get(), content.data(), content.length());
+ if (bytes_written < 0) {
+ return Err(
+ fmt::format(FMT_STRING("unable to write to temporary file {}: {}"),
+ tmp_pair.first.string(),
+ strerror(errno)));
+ }
+ if (bytes_written != content.length()) {
+ return Err(fmt::format(FMT_STRING("short write to file {}: {} < {}"),
+ tmp_pair.first.string(),
+ bytes_written,
+ content.length()));
+ }
+ std::error_code ec;
+ ghc::filesystem::rename(tmp_pair.first, path, ec);
+ if (ec) {
+ return Err(
+ fmt::format(FMT_STRING("unable to move temporary file {}: {}"),
+ tmp_pair.first.string(),
+ ec.message()));
+ }
+
+ return Ok();
+}
+
+std::string
+build_path(const std::vector<ghc::filesystem::path>& paths)
+{
+ return paths
+ | lnav::itertools::map([](const auto& path) { return path.string(); })
+ | lnav::itertools::append(getenv_opt("PATH").value_or(""))
+ | lnav::itertools::filter_out(&std::string::empty)
+ | lnav::itertools::fold(
+ [](const auto& elem, auto& accum) {
+ if (!accum.empty()) {
+ accum.push_back(':');
+ }
+ return accum.append(elem);
+ },
+ std::string());
+}
+
+Result<struct stat, std::string>
+stat_file(const ghc::filesystem::path& path)
+{
+ struct stat retval;
+
+ if (statp(path, &retval) == 0) {
+ return Ok(retval);
+ }
+
+ return Err(fmt::format(FMT_STRING("failed to find file: {} -- {}"),
+ path.string(),
+ strerror(errno)));
+}
+
+file_lock::file_lock(const ghc::filesystem::path& archive_path)
+{
+ auto lock_path = archive_path;
+
+ lock_path += ".lck";
+ auto open_res
+ = lnav::filesystem::create_file(lock_path, O_RDWR | O_CLOEXEC, 0600);
+ if (open_res.isErr()) {
+ throw std::runtime_error(open_res.unwrapErr());
+ }
+ this->lh_fd = open_res.unwrap();
+}
+
+} // namespace filesystem
+} // namespace lnav