Files
mob/src/op.cpp
T
2020-05-04 01:24:47 -04:00

396 lines
8.2 KiB
C++

#include "pch.h"
#include "op.h"
#include "utility.h"
#include "conf.h"
#include "context.h"
namespace mob::op
{
void do_touch(const context& cx, const fs::path& p);
void do_create_directories(const context& cx, const fs::path& p);
void do_delete_directory(const context& cx, const fs::path& p);
void do_delete_file(const context& cx, const fs::path& p);
void do_copy_file_to_dir(const context& cx, const fs::path& f, const fs::path& d);
void do_remove_readonly(const context& cx, const fs::path& p);
void do_rename(const context& cx, const fs::path& src, const fs::path& dest);
void check(const context& cx, const fs::path& p);
void touch(const context& cx, const fs::path& p)
{
cx.trace(context::fs, "touching " + p.string());
check(cx, p);
if (!conf::dry())
do_touch(cx ,p);
}
void create_directories(const context& cx, const fs::path& p)
{
cx.trace(context::fs, "creating dir " + p.string());
check(cx, p);
if (!conf::dry())
do_create_directories(cx, p);
}
void delete_directory(const context& cx, const fs::path& p, flags f)
{
cx.trace(context::fs, "deleting dir " + p.string());
check(cx, p);
if (!fs::exists(p))
{
if (f & optional)
{
cx.trace(context::fs,
"not deleting dir " + p.string() + ", doesn't exist");
return;
}
cx.bail_out(context::fs,
"can't delete dir " + p.string() + ", doesn't exist");
}
if (fs::exists(p) && !fs::is_directory(p))
cx.bail_out(context::fs, p.string() + " is not a dir");
if (!conf::dry())
do_delete_directory(cx, p);
}
void delete_file(const context& cx, const fs::path& p, flags f)
{
cx.trace(context::fs, "deleting file " + p.string());
check(cx, p);
if (!fs::exists(p))
{
if (f & optional)
{
cx.trace(context::fs,
"not deleting file " + p.string() + ", doesn't exist");
return;
}
cx.bail_out(context::fs,
"can't delete file " + p.string() + ", doesn't exist");
}
if (fs::exists(p) && !fs::is_regular_file(p))
{
cx.bail_out(context::fs,
"can't delete " + p.string() + ", not a file");
}
if (!conf::dry())
do_delete_file(cx, p);
}
void remove_readonly(const context& cx, const fs::path& first)
{
cx.trace(context::fs, "removing read-only from " + first.string());
check(cx, first);
if (!conf::dry())
{
if (fs::is_regular_file(first))
do_remove_readonly(cx, first);
for (auto&& p : fs::recursive_directory_iterator(first))
{
if (fs::is_regular_file(p))
do_remove_readonly(cx, p);
}
}
}
bool is_source_better(
const context& cx, const fs::path& src, const fs::path& dest)
{
if (!fs::exists(dest))
{
cx.trace(context::fs,
"target " + dest.string() + " doesn't exist; copying");
return true;
}
std::error_code ec;
const auto src_size = fs::file_size(src, ec);
if (ec)
{
cx.warning(context::fs,
"failed to get size of " + src.string() + "; forcing copy");
return true;
}
const auto dest_size = fs::file_size(dest, ec);
if (ec)
{
cx.warning(context::fs,
"failed to get size of " + dest.string() + "; forcing copy");
return true;
}
if (src_size != dest_size)
{
cx.trace(
context::fs,
"src " + src.string() + " is " + std::to_string(src_size) + "), "
"dest " + dest.string() + " is " + std::to_string(dest_size) + "); "
"sizes different, copying");
return true;
}
const auto src_time = fs::last_write_time(src, ec);
if (ec)
{
cx.warning(context::fs,
"failed to get time of " + src.string() + "; forcing copy");
return true;
}
const auto dest_time = fs::last_write_time(dest, ec);
if (ec)
{
cx.warning(context::fs,
"failed to get time of " + dest.string() + "; forcing copy");
return true;
}
if (src_time > dest_time)
{
cx.trace(context::fs,
"src " + src.string() + " is newer than " + dest.string() + "; "
"copying");
return true;
}
// same size, same date
return false;
}
void rename(const context& cx, const fs::path& src, const fs::path& dest)
{
check(cx, src);
check(cx, dest);
if (fs::exists(dest))
{
cx.bail_out(context::fs,
"can't rename " + src.string() + " to " + dest.string() + ", "
"already exists");
}
cx.trace(context::fs, "renaming " + src.string() + " to " + dest.string());
do_rename(cx, src, dest);
}
void move_to_directory(
const context& cx, const fs::path& src, const fs::path& dest_dir)
{
check(cx, src);
check(cx, dest_dir);
const auto target = dest_dir / src.filename();
if (fs::exists(target))
{
cx.bail_out(context::fs,
"can't move " + src.string() + " to " + dest_dir.string() + ", " +
src.filename().string() + " already exists");
}
cx.trace(context::fs, "moving " + src.string() + " to " + target.string());
do_rename(cx, src, target);
}
void copy_file_to_dir_if_better(
const context& cx, const fs::path& file, const fs::path& dir, flags f)
{
check(cx, file);
check(cx, dir);
if (file.filename().string().find("*") == std::string::npos)
{
if (!conf::dry())
{
if (!fs::exists(file) || !fs::is_regular_file(file))
{
if (f & optional)
{
cx.trace(context::fs,
"not copying " + file.string() + ", not found but "
"optional");
return;
}
cx.bail_out(context::fs,
"can't copy " + file.string() + ", not a file");
}
if (fs::exists(dir) && !fs::is_directory(dir))
{
cx.bail_out(context::fs,
"can't copy to " + dir.string() + ", not a dir");
}
}
const auto target = dir / file.filename();
if (is_source_better(cx, file, target))
{
cx.trace(context::fs, file.string() + " -> " + dir.string());
if (!conf::dry())
do_copy_file_to_dir(cx, file, dir);
}
else
{
cx.trace(context::bypass,
"(skipped) " + file.string() + " -> " + dir.string());
}
}
else
{
cx.trace(context::fs, file.filename().string() + " is a glob");
// wildcard
const auto file_parent = file.parent_path();
const auto wildcard = file.filename().string();
for (auto&& e : fs::directory_iterator(file_parent))
{
const auto name = e.path().filename().string();
if (PathMatchSpecA(name.c_str(), wildcard.c_str()))
copy_file_to_dir_if_better(cx, e.path(), dir);
else
cx.trace(context::fs, name + " did not match " + wildcard);
}
}
}
void do_touch(const context& cx, const fs::path& p)
{
op::create_directories(cx, p.parent_path());
std::ofstream out(p);
if (!out)
cx.bail_out(context::fs, "failed to touch " + p.string());
}
void do_create_directories(const context& cx, const fs::path& p)
{
std::error_code ec;
fs::create_directories(p, ec);
if (ec)
cx.bail_out(context::fs, "can't create " + p.string(), ec);
}
void do_delete_directory(const context& cx, const fs::path& p)
{
std::error_code ec;
fs::remove_all(p, ec);
if (ec)
{
if (ec.value() == ERROR_ACCESS_DENIED)
{
cx.trace(
context::fs,
"got access denied trying to delete dir " + p.string() + ", "
"trying to remove read-only flag recursively");
remove_readonly(cx, p);
fs::remove_all(p, ec);
if (!ec)
return;
}
cx.bail_out(context::fs, "failed to delete " + p.string(), ec);
}
}
void do_delete_file(const context& cx, const fs::path& p)
{
std::error_code ec;
fs::remove(p, ec);
if (ec)
cx.bail_out(context::fs, "can't delete " + p.string(), ec);
}
void do_copy_file_to_dir(
const context& cx, const fs::path& f, const fs::path& d)
{
op::create_directories(cx, d);
std::error_code ec;
fs::copy_file(
f, d / f.filename(),
fs::copy_options::overwrite_existing, ec);
if (ec)
{
cx.bail_out(context::fs,
"can't copy " + f.string() + " to " + d.string(), ec);
}
}
void do_remove_readonly(const context& cx, const fs::path& p)
{
cx.trace(context::fs, "chmod +x " + p.string());
std::error_code ec;
fs::permissions(p, fs::perms::owner_write, fs::perm_options::add, ec);
if (ec)
{
cx.bail_out(context::fs,
"can't remove read-only flag on " + p.string(), ec);
}
}
void do_rename(const context& cx, const fs::path& src, const fs::path& dest)
{
std::error_code ec;
fs::rename(src, dest, ec);
if (ec)
{
cx.bail_out(context::fs,
"can't rename " + src.string() + " to " + dest.string(), ec);
}
}
void check(const context& cx, const fs::path& p)
{
if (p.empty())
cx.bail_out(context::fs, "path is empty");
if (p.native().starts_with(paths::prefix().native()))
return;
if (p.native().starts_with(paths::temp_dir().native()))
return;
cx.bail_out(context::fs, "path " + p.string() + " is outside prefix");
}
} // namespace