Skip to content

Commit

Permalink
refactor fs, add fs::mv()
Browse files Browse the repository at this point in the history
  • Loading branch information
idealvin committed Nov 6, 2023
1 parent b5e3ac0 commit 12efa6a
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 5 deletions.
20 changes: 16 additions & 4 deletions include/co/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ __coapi bool mkdir(char* path, bool p);
// r = true -> rm -r
__coapi bool remove(const char* path, bool r = false);

// rename or move a file or directory
__coapi bool mv(const char* from, const char* to);

// deprecated, use mv instead
__coapi bool rename(const char* from, const char* to);

// administrator privileges required on windows
Expand Down Expand Up @@ -73,12 +77,20 @@ inline bool mkdir(const std::string& path, bool p=false) {
return fs::mkdir(path.c_str(), p);
}

inline bool remove(const fastring& path, bool rf=false) {
return fs::remove(path.c_str(), rf);
inline bool remove(const fastring& path, bool r=false) {
return fs::remove(path.c_str(), r);
}

inline bool remove(const std::string& path, bool r=false) {
return fs::remove(path.c_str(), r);
}

inline bool mv(const fastring& from, const fastring& to) {
return fs::mv(from.c_str(), to.c_str());
}

inline bool remove(const std::string& path, bool rf=false) {
return fs::remove(path.c_str(), rf);
inline bool mv(const std::string& from, const std::string& to) {
return fs::mv(from.c_str(), to.c_str());
}

inline bool rename(const fastring& from, const fastring& to) {
Expand Down
17 changes: 17 additions & 0 deletions src/fs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,28 @@ bool remove(const char* path, bool r) {
return _rmdir(s);
}

bool mv(const char* from, const char* to) {
struct stat attr;
if (::lstat(to, &attr) != 0 || !S_ISDIR(attr.st_mode)) {
return ::rename(from, to) == 0;
}

const char* p = strrchr(from, '/');
fastring s(to);
if (!s.ends_with('/')) s.append('/');
s.append(p ? p + 1 : from);
return ::rename(from, s.c_str()) == 0;
}

bool rename(const char* from, const char* to) {
return ::rename(from, to) == 0;
}

bool symlink(const char* dst, const char* lnk) {
struct stat attr;
if (::lstat(lnk, &attr) == 0 && S_ISLNK(attr.st_mode)) {
::unlink(lnk);
}
return ::symlink(dst, lnk) == 0;
}

Expand Down
38 changes: 38 additions & 0 deletions src/fs_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static fastring narrow(const wchar_t* p) {

const DWORD g_bad_attr = INVALID_FILE_ATTRIBUTES;
const DWORD g_attr_dir = FILE_ATTRIBUTE_DIRECTORY;
const DWORD g_attr_lnk = FILE_ATTRIBUTE_REPARSE_POINT;

inline DWORD _getattr(const wchar_t* path) {
return GetFileAttributesW(path);
Expand Down Expand Up @@ -197,6 +198,39 @@ bool remove(const char* path, bool r) {
return _rmdir(cache(), c);
}

bool mv(const char* from, const char* to) {
PWC x, y;
widen(from, to, &x, &y);

const DWORD a = _getattr(x);
const DWORD b = _getattr(y);
if (a == g_bad_attr || b == g_bad_attr) {
return MoveFileExW(x, y, MOVEFILE_COPY_ALLOWED);
}
if (!(b & g_attr_dir)) {
return MoveFileExW(x, y, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
}

const char* p = strrchr(from, '/');
if (!p) p = strrchr(from, '\\');
const char c = strrchr(to, '/') ? '/' : '\\';
fastring s(to);
if (!s.ends_with(c)) s.append(c);
s.append(p ? p + 1 : from);

widen(from, s.c_str(), &x, &y);
const DWORD w = _getattr(y);
if (w == g_bad_attr) {
return MoveFileExW(x, y, MOVEFILE_COPY_ALLOWED);
}
if (!(w & g_attr_dir)) {
return MoveFileExW(x, y, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
}

if (a & g_attr_dir) RemoveDirectoryW(y); // remove dir y if it is empty
return MoveFileExW(x, y, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED);
}

bool rename(const char* from, const char* to) {
PWC x, y;
widen(from, to, &x, &y);
Expand All @@ -206,6 +240,10 @@ bool rename(const char* from, const char* to) {
bool symlink(const char* dst, const char* lnk) {
PWC x, y;
widen(dst, lnk, &x, &y);
const DWORD a = _getattr(y);
if (a != g_bad_attr && (a & g_attr_lnk)) {
(a & g_attr_dir) ? RemoveDirectoryW(y) : DeleteFileW(y);
}
const DWORD d = _isdir(x) ? 1 : 0;
return CreateSymbolicLinkW(y, x, d);
}
Expand Down
51 changes: 50 additions & 1 deletion unitest/fs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,69 @@ DEF_test(fs) {
fs::rename("yyy", "xxx");
}

DEF_case(mv) {
fs::mv("xxx", "yyy");
EXPECT(!fs::exists("xxx"));
EXPECT(fs::exists("yyy"));
fs::mv("yyy", "xxx");

fs::mv("xxx", "xxd");
EXPECT(!fs::exists("xxx"));
EXPECT(fs::exists("xxd/xxx"));
fs::mv("xxd/xxx", "xxx");
EXPECT(!fs::exists("xxd/xxx"));

fs::file o("xxd/xxx", 'w');
o.close();
EXPECT_EQ(fs::mv("xxx", "xxd"), true);
EXPECT_NE(fs::fsize("xxd/xxx"), 0);
fs::mv("xxd/xxx", "xxx");

fs::mkdir("xxd/xxx");
EXPECT_EQ(fs::mv("xxx", "xxd"), false);
fs::remove("xxd/xxx");

EXPECT_EQ(fs::mkdir("xxs"), true);
EXPECT_EQ(fs::mkdir("xxd/xxs"), true);
EXPECT_EQ(fs::mv("xxs", "xxd"), true);
EXPECT(!fs::exists("xxs"));

EXPECT_EQ(fs::mkdir("xxd/xxs/xx"), true);
EXPECT_EQ(fs::mkdir("xxs"), true);
EXPECT_EQ(fs::mv("xxs", "xxd"), false);

fs::remove("xxd/xxs", true);
EXPECT(!fs::exists("xxd/xxs"));

o.open("xxd/xxs", 'w');
o.close();
EXPECT_EQ(fs::mv("xxs", "xxd"), false);
}

#ifndef _WIN32
DEF_case(symlink) {
fs::symlink("xxx", "xxx.lnk");
//EXPECT(fs::exists("xxx.lnk"));
fs::symlink("xxd", "xxd.lnk");
EXPECT(fs::exists("xxx.lnk"));
EXPECT(fs::exists("xxd.lnk"));
EXPECT_EQ(fs::symlink("xxx", "xxx.lnk"), true);
EXPECT_EQ(fs::symlink("xxd", "xxd.lnk"), true);
}
#endif

DEF_case(remove) {
EXPECT(fs::remove("xxx"));
EXPECT(fs::remove("xxx.lnk"));
EXPECT(fs::remove("xxd.lnk"));
EXPECT(fs::remove("xxplus"));
EXPECT(!fs::remove("xxd"));
EXPECT(fs::remove("xxd", true));
EXPECT(fs::remove("xxs"));
EXPECT(!fs::exists("xxx"));
EXPECT(!fs::exists("xxx.lnk"));
EXPECT(!fs::exists("xxd.lnk"));
EXPECT(!fs::exists("xxplus"));
EXPECT(!fs::exists("xxs"));
EXPECT(!fs::exists("xxd"));
}
}
Expand Down

0 comments on commit 12efa6a

Please sign in to comment.