blob: f77faaefe58b2bfd08bc3361a1476a92d6c33779 [file] [log] [blame]
/** @file
Minimalist version of std::filesystem.
@section license License
Licensed to the Apache Software Foundation (ASF) under one or more contributor license
agreements. See the NOTICE file distributed with this work for additional information regarding
copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with the License. You may
obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the specific language governing permissions and
limitations under the License.
*/
#include "tscore/ts_file.h"
#include <fcntl.h>
#include <sys/types.h>
#include <dirent.h>
namespace ts
{
namespace file
{
path &
path::operator/=(std::string_view that)
{
if (!that.empty()) { // don't waste time appending nothing.
if (that.front() == preferred_separator || _path.empty()) {
_path.assign(that);
} else {
if (_path.back() == preferred_separator) {
_path.reserve(_path.size() + that.size());
} else {
_path.reserve(_path.size() + that.size() + 1);
_path.push_back(preferred_separator);
}
_path.append(that);
}
}
return *this;
}
file_status
status(const path &p, std::error_code &ec) noexcept
{
file_status zret;
if (::stat(p.c_str(), &zret._stat) >= 0) {
ec.clear();
} else {
ec = std::error_code(errno, std::system_category());
}
return zret;
}
path
temp_directory_path()
{
/* ISO/IEC 9945 (POSIX): The path supplied by the first environment variable found in the list TMPDIR, TMP, TEMP, TEMPDIR.
* If none of these are found, "/tmp" */
char const *folder = nullptr;
if ((nullptr == (folder = getenv("TMPDIR"))) && (nullptr == (folder = getenv("TMP"))) &&
(nullptr == (folder = getenv("TEMPDIR")))) {
folder = "/tmp";
}
return path(folder);
}
path
current_path()
{
char cwd[PATH_MAX];
if (::getcwd(cwd, sizeof(cwd)) != nullptr) {
return path(cwd);
}
return path();
}
path
canonical(const path &p, std::error_code &ec)
{
if (p.empty()) {
ec = std::error_code(EINVAL, std::system_category());
return path();
}
char buf[PATH_MAX + 1];
char *res = ::realpath(p.c_str(), buf);
if (res) {
ec = std::error_code();
return path(res);
}
ec = std::error_code(errno, std::system_category());
return path();
}
path
filename(const path &p)
{
const size_t last_slash_idx = p.string().find_last_of(p.preferred_separator);
return p.string().substr(last_slash_idx + 1);
}
bool
exists(const path &p)
{
std::error_code ec;
status(p, ec);
return !(ec && ENOENT == ec.value());
}
static bool
do_mkdir(const path &p, std::error_code &ec, mode_t mode)
{
struct stat st;
if (stat(p.c_str(), &st) != 0) {
if (mkdir(p.c_str(), mode) != 0 && errno != EEXIST) {
ec = std::error_code(errno, std::system_category());
return false;
}
} else if (!S_ISDIR(st.st_mode)) {
ec = std::error_code(ENOTDIR, std::system_category());
return false;
}
return true;
}
bool
create_directories(const path &p, std::error_code &ec, mode_t mode) noexcept
{
if (p.empty()) {
ec = std::error_code(EINVAL, std::system_category());
return false;
}
bool result = false;
ec = std::error_code();
size_t pos = 0;
std::string token;
while ((pos = p.string().find_first_of(p.preferred_separator, pos)) != std::string::npos) {
token = p.string().substr(0, pos);
if (!token.empty()) {
result = do_mkdir(path(token), ec, mode);
}
pos = pos + sizeof(p.preferred_separator);
}
if (result) {
result = do_mkdir(p, ec, mode);
}
return result;
}
bool
copy(const path &from, const path &to, std::error_code &ec)
{
static int BUF_SIZE = 65536;
FILE *src, *dst;
char buf[BUF_SIZE];
int bufsize = BUF_SIZE;
if (from.empty() || to.empty()) {
ec = std::error_code(EINVAL, std::system_category());
return false;
}
ec = std::error_code();
std::error_code err;
path final_to;
file_status s = status(to, err);
if (!(err && ENOENT == err.value()) && is_dir(s)) {
const auto file = filename(from);
final_to = to / file;
} else {
final_to = to;
}
if (nullptr == (src = fopen(from.c_str(), "r"))) {
ec = std::error_code(errno, std::system_category());
return false;
}
if (nullptr == (dst = fopen(final_to.c_str(), "w"))) {
ec = std::error_code(errno, std::system_category());
fclose(src);
return false;
}
while (true) {
size_t in = fread(buf, 1, bufsize, src);
if (0 == in) {
break;
}
size_t out = fwrite(buf, 1, in, dst);
if (0 == out) {
break;
}
}
fclose(src);
fclose(dst);
return true;
}
static bool
remove_path(const path &p, std::error_code &ec)
{
DIR *dir;
struct dirent *entry;
bool res = true;
std::error_code err;
file_status s = status(p, err);
if (err && ENOENT == err.value()) {
// file/dir does not exist
return false;
} else if (is_regular_file(s)) {
// regular file, try to remove it!
if (unlink(p.c_str()) != 0) {
ec = std::error_code(errno, std::system_category());
res = false;
}
return res;
} else if (!is_dir(s)) {
// not a directory
ec = std::error_code(ENOTDIR, std::system_category());
return false;
}
// recursively remove nested files and directories
if (nullptr == (dir = opendir(p.c_str()))) {
ec = std::error_code(errno, std::system_category());
return false;
}
while (nullptr != (entry = readdir(dir))) {
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
continue;
}
remove_path(p / entry->d_name, ec);
}
if (0 != rmdir(p.c_str())) {
ec = std::error_code(errno, std::system_category());
}
closedir(dir);
return true;
}
bool
remove(const path &p, std::error_code &ec)
{
if (p.empty()) {
ec = std::error_code(EINVAL, std::system_category());
return false;
}
ec = std::error_code();
return remove_path(p, ec);
} // namespace file
int
file_type(const file_status &fs)
{
return fs._stat.st_mode & S_IFMT;
}
time_t
modification_time(const file_status &fs)
{
return fs._stat.st_mtime;
}
uintmax_t
file_size(const file_status &fs)
{
return fs._stat.st_size;
}
bool
is_char_device(const file_status &fs)
{
return file_type(fs) == S_IFCHR;
}
bool
is_block_device(const file_status &fs)
{
return file_type(fs) == S_IFBLK;
}
bool
is_regular_file(const file_status &fs)
{
return file_type(fs) == S_IFREG;
}
bool
is_dir(const file_status &fs)
{
return file_type(fs) == S_IFDIR;
}
bool
is_readable(const path &p)
{
return 0 == access(p.c_str(), R_OK);
}
std::string
load(const path &p, std::error_code &ec)
{
std::string zret;
ats_scoped_fd fd(::open(p.c_str(), O_RDONLY));
ec.clear();
if (fd < 0) {
ec = std::error_code(errno, std::system_category());
} else {
struct stat info;
if (0 != ::fstat(fd, &info)) {
ec = std::error_code(errno, std::system_category());
} else {
int n = info.st_size;
zret.resize(n);
auto read_len = ::read(fd, const_cast<char *>(zret.data()), n);
if (read_len < n) {
ec = std::error_code(errno, std::system_category());
}
}
}
return zret;
}
} // namespace file
} // namespace ts