blob: c6a0fc975be0c08ab6ad4d5c5da31dd37d828243 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/util/file_util.h"
#include <fstream>
#include <streambuf>
#include <string>
#include "src/lib/util/status_builder.h"
#include "src/logging.h"
#include "src/public/lib/status.h"
#include "src/public/lib/status_codes.h"
#include "third_party/abseil-cpp/absl/strings/ascii.h"
#include "third_party/abseil-cpp/absl/strings/escaping.h"
namespace cobalt::util {
namespace {
constexpr int kMaxFileSize = 100000;
}
lib::statusor::StatusOr<std::string> ReadTextFile(const Path& file_path) {
std::string file_contents;
if (file_path.get().empty()) {
return util::StatusBuilder(StatusCode::INVALID_ARGUMENT, "ReadTextFile: file_path is empty.")
.Build();
}
std::ifstream stream(file_path.get(), std::ifstream::in);
if (!stream.good()) {
return util::StatusBuilder(StatusCode::NOT_FOUND, "Unable to open file")
.WithContexts(file_path)
.Build();
}
stream.seekg(0, std::ios::end);
if (!stream.good()) {
return util::StatusBuilder(StatusCode::INTERNAL, "Error reading file")
.WithContexts(file_path)
.Build();
}
int64_t file_size = stream.tellg();
if (file_size == 0) {
return std::string("");
}
// Don't try to read a file that's too big.
if (!stream.good() || file_size < 0 || file_size > kMaxFileSize) {
return util::StatusBuilder(StatusCode::FAILED_PRECONDITION, "Invalid file length")
.WithContexts(file_path)
.Build();
}
file_contents.reserve(static_cast<uint64_t>(file_size));
stream.seekg(0, std::ios::beg);
if (!stream.good()) {
return util::StatusBuilder(StatusCode::NOT_FOUND, "Error reading file")
.WithContexts(file_path)
.Build();
}
file_contents.assign((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
if (!stream.good()) {
return util::StatusBuilder(StatusCode::NOT_FOUND, "Error reading file")
.WithContexts(file_path)
.Build();
}
VLOG(3) << "Successfully read file at " << file_path;
return file_contents;
}
lib::statusor::StatusOr<std::string> ReadNonEmptyTextFile(const Path& file_path) {
auto read_file_result = ReadTextFile(file_path);
if (!read_file_result.ok()) {
return read_file_result;
}
if (read_file_result.ValueOrDie().empty()) {
return util::StatusBuilder(StatusCode::FAILED_PRECONDITION, "File was empty")
.WithContexts(file_path)
.Build();
}
return read_file_result;
}
lib::statusor::StatusOr<std::string> ReadNonEmptyTextFile(const std::string& file_path) {
return ReadNonEmptyTextFile(Path(file_path));
}
lib::statusor::StatusOr<std::string> ReadHexFile(const Path& file_path) {
auto read_file_result = ReadNonEmptyTextFile(file_path);
if (!read_file_result.ok()) {
return read_file_result;
}
auto hex = absl::StripAsciiWhitespace(read_file_result.ValueOrDie());
if (hex.size() % 2 == 1) {
return util::StatusBuilder(StatusCode::FAILED_PRECONDITION, "Hex file has invalid size")
.WithContexts(file_path)
.Build();
}
if (std::find_if_not(hex.begin(), hex.end(), absl::ascii_isxdigit) != hex.end()) {
return util::StatusBuilder(StatusCode::FAILED_PRECONDITION, "Hex file has non-hex characters")
.WithContexts(file_path)
.Build();
}
return absl::HexStringToBytes(hex);
}
std::string ReadHexFileOrDefault(const Path& file_path, const std::string& default_string) {
auto read_hex_file_result = ReadHexFile(file_path);
if (!read_hex_file_result.ok()) {
VLOG(1) << "Failed to read hex file: " << read_hex_file_result.status();
return default_string;
}
return read_hex_file_result.ValueOrDie();
}
std::string ReadHexFileOrDefault(const std::string& file_path, const std::string& default_string) {
return ReadHexFileOrDefault(Path(file_path), default_string);
}
} // namespace cobalt::util