Skip to content

Commit

Permalink
Merge branch 'main' into beta
Browse files Browse the repository at this point in the history
  • Loading branch information
dacap committed May 28, 2024
2 parents d590eec + 695bb59 commit 0d8396e
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 36 deletions.
6 changes: 5 additions & 1 deletion base/fs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ std::string get_absolute_path(const std::string& filename)

bool is_path_separator(std::string::value_type chr)
{
return (chr == '\\' || chr == '/');
return (
#if LAF_WINDOWS
chr == '\\' ||
#endif
chr == '/');
}

std::string get_file_path(const std::string& filename)
Expand Down
113 changes: 87 additions & 26 deletions base/fs_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// LAF Base Library
// Copyright (c) 2024 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

#include <gtest/gtest.h>

#include "base/file_content.h"
#include "base/fs.h"

using namespace base;
Expand Down Expand Up @@ -45,96 +47,131 @@ TEST(FS, MakeAllDirectories)

TEST(FS, IsPathSeparator)
{
EXPECT_TRUE (is_path_separator('\\'));
EXPECT_TRUE (is_path_separator('/'));
EXPECT_FALSE(is_path_separator('a'));
EXPECT_FALSE(is_path_separator('+'));
EXPECT_FALSE(is_path_separator(':'));

#if LAF_WINDOWS
EXPECT_TRUE (is_path_separator('\\'));
#else
EXPECT_FALSE(is_path_separator('\\'));
#endif
}

TEST(FS, GetFilePath)
{
EXPECT_EQ("C:\\foo", get_file_path("C:\\foo\\main.cpp"));
EXPECT_EQ("C:/foo", get_file_path("C:/foo/pack.tar.gz"));
EXPECT_EQ(".", get_file_path("./main.cpp"));
EXPECT_EQ(".", get_file_path(".\\main.cpp"));
EXPECT_EQ("", get_file_path("\\main.cpp"));
EXPECT_EQ("", get_file_path("main.cpp"));
EXPECT_EQ("", get_file_path("main."));
EXPECT_EQ("", get_file_path("main"));
EXPECT_EQ("C:/foo", get_file_path("C:/foo/"));
EXPECT_EQ("C:", get_file_path("C:\\"));
EXPECT_EQ("C:", get_file_path("C:\\.cpp"));
EXPECT_EQ("", get_file_path(".cpp"));
EXPECT_EQ("", get_file_path(""));

#if LAF_WINDOWS
EXPECT_EQ("C:\\foo", get_file_path("C:\\foo\\main.cpp"));
EXPECT_EQ(".", get_file_path(".\\main.cpp"));
EXPECT_EQ("C:", get_file_path("C:\\"));
EXPECT_EQ("C:", get_file_path("C:\\.cpp"));
#else
EXPECT_EQ("", get_file_path("C:\\foo\\main.cpp"));
EXPECT_EQ("", get_file_path(".\\main.cpp"));
EXPECT_EQ("", get_file_path("C:\\"));
EXPECT_EQ("", get_file_path("C:\\.cpp"));
#endif
}

TEST(FS, GetFileName)
{
EXPECT_EQ("main.cpp", get_file_name("C:\\foo\\main.cpp"));
EXPECT_EQ("pack.tar.gz", get_file_name("C:/foo/pack.tar.gz"));
EXPECT_EQ("main.cpp", get_file_name("./main.cpp"));
EXPECT_EQ("main.cpp", get_file_name(".\\main.cpp"));
EXPECT_EQ("main.cpp", get_file_name("\\main.cpp"));
EXPECT_EQ("main.cpp", get_file_name("main.cpp"));
EXPECT_EQ("main.", get_file_name("main."));
EXPECT_EQ("main", get_file_name("main"));
EXPECT_EQ("", get_file_name("C:/foo/"));
EXPECT_EQ("", get_file_name("C:\\"));
EXPECT_EQ(".cpp", get_file_name("C:\\.cpp"));
EXPECT_EQ(".cpp", get_file_name(".cpp"));
EXPECT_EQ("", get_file_name(""));

#if LAF_WINDOWS
EXPECT_EQ("main.cpp", get_file_name("C:\\foo\\main.cpp"));
EXPECT_EQ("main.cpp", get_file_name(".\\main.cpp"));
EXPECT_EQ("main.cpp", get_file_name("\\main.cpp"));
EXPECT_EQ("", get_file_name("C:\\"));
EXPECT_EQ(".cpp", get_file_name("C:\\.cpp"));
#else
EXPECT_EQ("C:\\foo\\main.cpp", get_file_name("C:\\foo\\main.cpp"));
EXPECT_EQ(".\\main.cpp", get_file_name(".\\main.cpp"));
EXPECT_EQ("\\main.cpp", get_file_name("\\main.cpp"));
EXPECT_EQ("C:\\", get_file_name("C:\\"));
EXPECT_EQ("C:\\.cpp", get_file_name("C:\\.cpp"));
#endif
}

TEST(FS, GetFileExtension)
{
EXPECT_EQ("cpp", get_file_extension("C:\\foo\\main.cpp"));
EXPECT_EQ("gz", get_file_extension("C:/foo/pack.tar.gz"));
EXPECT_EQ("cpp", get_file_extension("./main.cpp"));
EXPECT_EQ("cpp", get_file_extension(".\\main.cpp"));
EXPECT_EQ("cpp", get_file_extension("\\main.cpp"));
EXPECT_EQ("cpp", get_file_extension("main.cpp"));
EXPECT_EQ("", get_file_extension("main."));
EXPECT_EQ("", get_file_extension("main"));
EXPECT_EQ("", get_file_extension("C:/foo/"));
EXPECT_EQ("", get_file_extension("C:\\"));
EXPECT_EQ("cpp", get_file_extension("C:\\.cpp"));
EXPECT_EQ("cpp", get_file_extension(".cpp"));
EXPECT_EQ("", get_file_extension(""));

// Same results on Windows/macOS/Linux
EXPECT_EQ("cpp", get_file_extension("C:\\foo\\main.cpp"));
EXPECT_EQ("cpp", get_file_extension(".\\main.cpp"));
EXPECT_EQ("cpp", get_file_extension("\\main.cpp"));
EXPECT_EQ("", get_file_extension("C:\\"));
EXPECT_EQ("cpp", get_file_extension("C:\\.cpp"));
}

TEST(FS, GetFileTitle)
{
EXPECT_EQ("main", get_file_title("C:\\foo\\main.cpp"));
EXPECT_EQ("pack.tar", get_file_title("C:/foo/pack.tar.gz"));
EXPECT_EQ("main", get_file_title("./main.cpp"));
EXPECT_EQ("main", get_file_title(".\\main.cpp"));
EXPECT_EQ("main", get_file_title("\\main.cpp"));
EXPECT_EQ("main", get_file_title("main.cpp"));
EXPECT_EQ("main", get_file_title("main."));
EXPECT_EQ("main", get_file_title("main"));
EXPECT_EQ("", get_file_title("C:/foo/"));
EXPECT_EQ("", get_file_title("C:\\"));
EXPECT_EQ("", get_file_title("C:\\.cpp"));
EXPECT_EQ("", get_file_title(".cpp"));
EXPECT_EQ("", get_file_title(""));

#if LAF_WINDOWS
EXPECT_EQ("main", get_file_title("C:\\foo\\main.cpp"));
EXPECT_EQ("main", get_file_title(".\\main.cpp"));
EXPECT_EQ("main", get_file_title("\\main.cpp"));
EXPECT_EQ("", get_file_title("C:\\"));
EXPECT_EQ("", get_file_title("C:\\.cpp"));
#else
EXPECT_EQ("C:\\foo\\main", get_file_title("C:\\foo\\main.cpp"));
EXPECT_EQ(".\\main", get_file_title(".\\main.cpp"));
EXPECT_EQ("\\main", get_file_title("\\main.cpp"));
EXPECT_EQ("C:\\", get_file_title("C:\\"));
EXPECT_EQ("C:\\", get_file_title("C:\\.cpp"));
#endif
}

TEST(FS, GetFileTitleWithPath)
{
EXPECT_EQ("C:\\foo\\main", get_file_title_with_path("C:\\foo\\main.cpp"));
EXPECT_EQ("C:/foo/pack.tar", get_file_title_with_path("C:/foo/pack.tar.gz"));
EXPECT_EQ("./main", get_file_title_with_path("./main.cpp"));
EXPECT_EQ(".\\main", get_file_title_with_path(".\\main.cpp"));
EXPECT_EQ("\\main", get_file_title_with_path("\\main.cpp"));
EXPECT_EQ("main", get_file_title_with_path("main.cpp"));
EXPECT_EQ("main", get_file_title_with_path("main."));
EXPECT_EQ("main", get_file_title_with_path("main"));
EXPECT_EQ("C:/foo/", get_file_title_with_path("C:/foo/"));
EXPECT_EQ("C:\\", get_file_title_with_path("C:\\"));
EXPECT_EQ("C:\\", get_file_title_with_path("C:\\.cpp"));
EXPECT_EQ("", get_file_title_with_path(".cpp"));
EXPECT_EQ("", get_file_title_with_path(""));

// Same results on Windows/macOS/Linux
EXPECT_EQ("C:\\foo\\main", get_file_title_with_path("C:\\foo\\main.cpp"));
EXPECT_EQ(".\\main", get_file_title_with_path(".\\main.cpp"));
EXPECT_EQ("\\main", get_file_title_with_path("\\main.cpp"));
EXPECT_EQ("C:\\", get_file_title_with_path("C:\\"));
EXPECT_EQ("C:\\", get_file_title_with_path("C:\\.cpp"));
}

TEST(FS, JoinPath)
Expand All @@ -146,16 +183,26 @@ TEST(FS, JoinPath)
EXPECT_EQ("fn", join_path("", "fn"));
EXPECT_EQ("/fn", join_path("/", "fn"));
EXPECT_EQ("/this"+sep+"fn", join_path("/this", "fn"));

EXPECT_EQ("C:\\path"+sep+"fn", join_path("C:\\path", "fn"));
#if LAF_WINDOWS
EXPECT_EQ("C:\\path\\fn", join_path("C:\\path\\", "fn"));
#else
EXPECT_EQ("C:\\path\\/fn", join_path("C:\\path\\", "fn"));
#endif
}

TEST(FS, RemovePathSeparator)
{
EXPECT_EQ("C:\\foo", remove_path_separator("C:\\foo\\"));
EXPECT_EQ("C:/foo", remove_path_separator("C:/foo/"));
EXPECT_EQ("C:\\foo\\main.cpp", remove_path_separator("C:\\foo\\main.cpp"));
EXPECT_EQ("C:\\foo\\main.cpp", remove_path_separator("C:\\foo\\main.cpp/"));

#if LAF_WINDOWS
EXPECT_EQ("C:\\foo", remove_path_separator("C:\\foo\\"));
#else
EXPECT_EQ("C:\\foo\\", remove_path_separator("C:\\foo\\"));
#endif
}

TEST(FS, HasFileExtension)
Expand Down Expand Up @@ -215,6 +262,20 @@ TEST(FS, CompareFilenames)
EXPECT_EQ(1, compare_filenames("a1-64-10.png", "a1-64-9.png"));
}

TEST(FS, CopyFiles)
{
std::vector<uint8_t> data = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
const std::string dst = "_test_copy_.tmp";

if (base::is_file(dst))
base::delete_file(dst);

base::write_file_content("_test_orig_.tmp", data.data(), data.size());
base::copy_file("_test_orig_.tmp", dst, true);

EXPECT_EQ(data, base::read_file_content(dst));
}

int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
Expand Down
52 changes: 44 additions & 8 deletions base/fs_unix.h
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
// LAF Base Library
// Copyright (c) 2021 Igara Studio S.A.
// Copyright (c) 2021-2024 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.

#include "base/file_handle.h"
#include "base/ints.h"
#include "base/paths.h"
#include "base/time.h"

#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <cerrno>
#include <climits> // Required for PATH_MAX
#include <cstdio> // Required for rename()
#include <climits>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
Expand All @@ -25,9 +30,6 @@
#include <sys/sysctl.h>
#endif

#include "base/paths.h"
#include "base/time.h"

#define MAXPATHLEN 1024

namespace base {
Expand Down Expand Up @@ -67,9 +69,43 @@ void move_file(const std::string& src, const std::string& dst)
std::string(std::strerror(errno)));
}

void copy_file(const std::string& src, const std::string& dst, bool overwrite)
void copy_file(const std::string& src_fn, const std::string& dst_fn,
const bool overwrite)
{
throw std::runtime_error("Error copying file: unimplemented");
// First copy the file content
FileHandle src = open_file(src_fn, "rb");
if (!src) {
throw std::runtime_error("Cannot open source file " +
std::string(std::strerror(errno)));
}

FileHandle dst = open_file(dst_fn, "wb");
if (!dst) {
throw std::runtime_error("Cannot open destination file " +
std::string(std::strerror(errno)));
}

// Copy data in 4KB chunks
constexpr size_t kChunkSize = 4096;
std::vector<uint8_t> buf(kChunkSize);
while (size_t bytes = std::fread(buf.data(), 1, buf.size(), src.get())) {
std::fwrite(buf.data(), 1, bytes, dst.get());
}

// Now copy file attributes (mode and owner)
struct stat sts;
stat(src_fn.c_str(), &sts);
fchmod(fileno(dst.get()), sts.st_mode);
fchown(fileno(dst.get()), sts.st_uid, sts.st_gid);

// Check that the output file has the same mode and owner
#if _DEBUG
struct stat sts2;
stat(dst_fn.c_str(), &sts2);
ASSERT(sts.st_mode == sts2.st_mode);
ASSERT(sts.st_uid == sts2.st_uid);
ASSERT(sts.st_gid == sts2.st_gid);
#endif
}

void delete_file(const std::string& path)
Expand Down
8 changes: 7 additions & 1 deletion base/memory.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// LAF Base Library
// Copyright (c) 2022 Igara Studio S.A.
// Copyright (c) 2022-2024 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
Expand All @@ -9,6 +9,8 @@
#include "config.h"
#endif

#include "base/debug.h"

#include <cassert>
#include <cstdio>
#include <cstdlib>
Expand Down Expand Up @@ -304,6 +306,10 @@ void* base_aligned_alloc(std::size_t bytes, std::size_t alignment)
#if LAF_WINDOWS
return _aligned_malloc(bytes, alignment);
#else
ASSERT(alignment > 0);
std::size_t misaligned = (bytes % alignment);
if (misaligned > 0)
bytes += alignment - misaligned;
return aligned_alloc(alignment, bytes);
#endif
}
Expand Down

0 comments on commit 0d8396e

Please sign in to comment.